Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I inject a variable into a directive's scope?

Tags:

angularjs

Let's say I've got a directive that looks like this:

directive('attachment', function() {
    return {
        restrict: 'E',
        controller: 'AttachmentCtrl'
    }
})

That means I can write a list of 'attachment' elements like this:

<attachment ng-repeat="a in note.getAttachments()">
    <p>Attachment ID: {{a.id}}</p>
</attachment>

In the above snippet, let's assume that note.getAttachments() returns a set of simple javascript object hashes.

Since I set a controller for the directive, I can include calls to that controller's scope functions inline.

Here's the controller:

function AttachmentCtrl($scope) {
    $scope.getFilename = function() {
        return 'image.jpg';
    }
}

And here is the modified HTML for when we include a call to that $scope.getFilename function inline (the new 2nd paragraph):

<attachment ng-repeat="a in note.getAttachments()">
    <p>Attachment ID: {{a.id}}</p>
    <p>Attachment file name: {{getFilename()}}
</attachment>

However, this isn't useful. This will just print the same string, "image.jpg", as the file name for each attachment.

In actuality, the file name for the attachments is based on attachment ID. So an attachment with ID of "2" would have the file name of "image-2.jpg".

So our getFilename function needs to be modified. Let's fix it:

function AttachmentCtrl($scope) {
    $scope.getFilename = function() {
        return 'image-' + a.id + '.jpg';
    }
}

But wait — this won't work. There is no variable a in the scope. We can use the variable a inline thanks to the ng-repeat, but that a variable isn't available to the scope bound to the directive.

So the question is, how do I make that a available to the scope?

Please note: I realize that in this particular example, I could just print image-{{a.id}}.jpg inline. But that does not answer the question. This is just an extremely simplified example. In reality, the getFilename function would be something too complex to print inline.

Edit: Yes, getFilename can accept an argument, and that would work. However, that also does not answer the question. I still want to know, without workarounds, whether you can get a into the scope without using it inline.

For example, maybe there is a way to inject it directly into the controller so it would be written as:

function AttachmentCtrl($scope, a) { ... }

But where would I pass it in from? Is there something I can add to the directive declaration? Maybe an ng-* attribute I can add next to the ng-repeat? I just want to know if it's possible.

like image 628
cilphex Avatar asked Oct 06 '22 07:10

cilphex


2 Answers

But wait — this won't work. There is no variable "a" in the scope. We can use the variable a inline thanks to the ng-repeat, but that a variable isn't available to the scope bound to the directive.

Actually variable a is in the scope associated with the directive controller. Each controller created by the directive gets the child scope created by the ng-repeat iteration. So this works (note $scope.a.id):

function AttachmentCtrl($scope) {
    $scope.getFilename = function() {
        return 'image-' + $scope.a.id + '.jpg';
}

Here's a fiddle that shows the controller scope, directive scopes, and ngRepeat scopes.

"If multiple directives on the same element request new scope, only one new scope is created. " -- Directive docs, section "Directive Definition Object"

In your example, ng-repeat is creating a new scope, so all directives on that same element get that same new (child) scope.

Also, if you do ever come across a case where you need to get a variable into a controller, using attributes would be better than using ng-init.

like image 166
Mark Rajcok Avatar answered Oct 07 '22 21:10

Mark Rajcok


Another way would be to use ng-init and set a model property for child scope. See this fiddle

Relevant code would be

<div ng-app='myApp' ng-controller='MyCtrl'>
    <attachment ng-repeat="a in attachments" ng-init='model=a'>
        <p>Attachment ID: {{model.id}}</p>
        <p>Attachment file name: {{getFilename()}}</p>
    </attachment>
</div>

and

function AttachmentCtrl($scope) {
    $scope.getFilename = function () {
        return 'image-' + $scope.model.id + '.jpg';
    }
}
like image 45
Chandermani Avatar answered Oct 07 '22 23:10

Chandermani