The contents - i.e. the child elements - of the element hosting the directive have a different scope than the directive if the directive uses isolate scope (scope: {}), which I suspect is what you have.
In order to link the contents against the same scope, you'd need to manually transclude them:
.directive("expandable", function(){ return { transclude: true, scope: { // whatever you have now }, link: function(scope, element, attrs, ctrls, transcludeFn){ transcludeFn(scope, function(contentClone){ element.append(contentClone); }); // here you can expose whatever functions you want on the isolate scope: scope.someFuncInsideExpandableDirective = function(){ // do something } } }; });
The first parameter of the transclusion function transcludeFn is the scope against which the transcluded content is linked, which, in this case, is the directive's scope.
Keep in mind, though, that it is a bit awkward from the point of view of the user of your directive, since now they have HTML that refers to some "magic" variables/function (which is of-course defined by your directive) that is not apparent to someone observing the HTML. Imagine you'd encounter some directive foo which did something similar:
<div foo="abc"> {{foobar}} <button ng-click="doSomethingFoo()">do foo</button> </div>
and foobar and doSomethingFoo were defined inside the scope of the foo directive - it would be more difficult to decipher without knowing the specifics of foo what was going on.