5

I wish to alias a model used in a template such that the same template can be reused for different models. For instance in the following model:

member = {

    name: "Member1",
    children:[
    {
        name:"Child1"
    },
    {
        name:"Child2"
    }
    ]
}

both "member" and "children" have "name" property. So I wish to create a single template manipulating this "name" property. I was able to achieve this with the help of this question:

Bind ngInclude to different models

As suggested, I created a directive such as:

app.directive('member', function(){
return {
    restrict: 'A',
    template: "{{prefix}}<input type='text' ng-model='member.name'>",
    scope: {
        member: "="
    }
};
});

Following is the usage of this directive:

<div ng-controller="MemberCtrl">
    {{member | json}}
    <div member="member"></div>
    <div member="member.children[0]"></div>
</div>

I was able to achieve the template reuse, however since I'm using "scope" in my directive, this has created an isolated scope which is not able to access any property of the controller scope. So for the controller:

app.controller('MemberCtrl', function($scope){
$scope.member = {

    name: "Member1",
    children:[
    {
        name:"Child1"
    },
    {
        name:"Child2"
    }
    ]
};

    $scope.prefix = "Mr.";
});

the template inside the directive is not able to access the "prefix" property. Following is the jsfiddle:

http://jsfiddle.net/vaibhavgupta007/mVBaC/1/

What could be the issue in this?

Edit

I can also use $parent to access the prefix. But is this clean approach.

Community
  • 1
  • 1
Vaibhav
  • 569
  • 6
  • 31

2 Answers2

1

Just define 'prefix' as isolated scope variable in your directive definition object, and reference it in element attributes.

app.directive('member', function(){
return {
    restrict: 'A',
    template: "{{prefix}}<input type='text' ng-model='member.name'>",
    scope: {
        member: "=",
        prefix: "="
    }
};
});

 

<div ng-controller="MemberCtrl">
    {{member | json}}
    <div member="member" prefix="prefix"></div>
    <div member="member.children[0]" prefix="prefix"></div>
</div>

Fiddle

Stewie
  • 60,366
  • 20
  • 146
  • 113
  • Yes this is one approach, but how do I expose the controller scope to the directive so that it can access some helper functions as well. For instance if the controller had a function called "joinNameWithPrefix" in the scope, the directive will not be able to access it. – Vaibhav Apr 01 '13 at 06:48
  • In that case you can leave out the 'scope' altogether from the directive definition object, and the directive will not create isolated scope, but will instead, use the parent scope by default. – Stewie Apr 01 '13 at 07:10
  • But then I will not be able to achieve aliasing. Looks like a chicken and egg problem to me...:) I think aliasing is not supported in angularjs using a straight forward way. I will have to use $parent in the template. – Vaibhav Apr 01 '13 at 08:58
  • 1
    I'm still not sure what you mean by 'aliasing'. In directives, you may alias parent scope variables by specifying the 'scope' property on directive definition object. That enables you to do something like `scope: {myName: '=member'}`, in which case you may, inside your directive, access the parent `member`property through `myName` property inside the linking function. Now, if you want to be able to access any property (be it variable or function) in parent scope, without using $parent, than you **have** to leave your directive without isolated scope (by not specifying the 'scope' property). – Stewie Apr 01 '13 at 11:15
0

To be able to bind a directive scope property to various controller properties (e.g., member and member.children[0]) for use in a generic template, I believe you will need to use the isolate scope syntax (as you already discovered).

Once you go down the "isolate scope road", you'll have to specify all controller properties and functions that your directive needs access to as additional attributes:

<div member="member" prefix="{{prefix}}" join-fn="joinNameWithPrefix(someName)"></div>

Then in your directive:

template: "{{prefix}}<input type='text' ng-model='member.name'>"
   + "<br>fn result={{ joinFn( {someName: member.name} ) }}",
scope: {
  member: "=",
  prefix: '@',
  joinFn: '&'
},

Fiddle

Use = for two-way databinding, @ for one-way strings, and & for one-way expressions.

Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492