简体   繁体   中英

Angular value is parsed when printed, but not as a directive attribute

I have an array of addresses called addressesForUser . I also have an "address" directive with an isolate scope.

When I do this:

<div ng-repeat="add in addressesForUser" entryid="{{ add.id }}">{{ add.id }}</div>            

all is well, and for three addresses I get the following generated html:

<div entryid="1">1</div>
<div entryid="2">2</div>
<div entryid="3">3</div>

but when I do this:

<address ng-repeat="add in addressesForUser" entryid="{{ add.id }}"></address>

and then try to console.log(attrs.entryid) within the directive's linking function, I get "add.id" as a string in the output. Worse yet, the attribute in the generated html is undefined - only the attribute name is there - so it's neither a number from 1 to 3, nor the string "add.id".

What am I doing wrong? The end goal is having each address directive read in its entryid attribute, so it can auto-populate independently and generate an address card, or if the entryid is missing, it shows a form to enter a new address. The latter part works fine, but I can't understand why it's not reading in the entryid value.

Edit: When I don't use isolate scope (ie I remove "scope: {}" from the directive), it works. But I need isolate scope, so I suppose I just need a way to make the attribute get included in the scope of the individual directive. Here's a slightly stripped down address directive code:

myapp.directive('address', ['GeoService', '$http', function (gs, http) {
    return {
        restrict: 'E',
        templateUrl: 'address.html',
        scope: {
        },
        link: function (scope, element, attrs) {

            scope.emptyForm = {
                first_name: '',
                last_name: '',
                country: '',
                city: '',
                street: '',
                phone: '',
                is_def: '',
                zip: '',
                additional_info: '',
                residence_type: ''
            }

            console.log(attrs.entryid);

            scope.countries = gs.countries();
            scope.resetAddressForm = function(e) {
                scope.newAddressForm = scope.emptyForm;
            }
            scope.saveAddress = function() {
                http.post('/users/saveaddress', scope.newAddressForm).then(function (response) {

                   console.log(response.data); // Do something
                });
            }
            scope.$watch('newAddressForm.country', function(v) {
                if (v != undefined) {
                    scope.cities = gs.cities(v);
                }
            });
        }
    }
}]);

The problem here is actually that in directives you cannot assume that an attribute is ready when the link function runs. If the value is interpolated, it will often not be available to you. The canonical way of accessing an attribute is through $observe :

link: function ( scope, element, attrs ) {
  attrs.$observe( 'entryid', function ( val ) {
    // do something with the val
  });
}

If you are using an isolate scope, it is probably a good idea to add scope properties for common attributes but you still shouldn't assume the value to be available during the link . You should always use the $observe method if the value could possibly need to be interpolated.

Since your directive does not modify the value of add.id (hence you don't need two-way databinding), I suggest using @ rather than = . Once defined with @ , you can use attrs.$observe() or scope.$watch() to get the interpolated value:

scope: {
   entryid: '@',
},
link: function (scope, element, attrs) {
    attrs.$observe('entryid', function(value) {
        console.log('observed value=',value);
    });
    scope.$watch('entryid', function(value) {
       console.log('watched value=',value);
    });
    ...
}

fiddle

After some more tinkering, I got the solution by accident. The documentation could really use some more work in this area.

Essentially, the isolate scope needs to be defined as such:

scope: {entryid: "="}

and the value is then accessed via scope, not via attrs, as in

console.log(scope.entryid);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM