简体   繁体   中英

Directive isn't loading scope variable

So I have a very simple directive that's supposed to execute a jquery plugin function:

angular.module('myproject.directives').directive('starRating', function () {
    return {
        restrict: 'A',
        scope: {
            rating: '='
        },
        link: function (scope, elem, attr) {
            console.log('rating', scope.rating);

            elem.barrating({
                theme: 'css-stars',
                readonly: true
            });

            elem.barrating('set', scope.rating);
        }
    };
});

Here is the HTML:

<select class="service-rating" 
        ng-show="!!currentJob.Review.ServiceRating"
        star-rating rating="currentJob.Review.ServiceRating">
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
</select>

The 'currentJob' variable is only set after an $http call, however the div is only set to show once this is populated. The log is returning 'null' for scope.rating, however if I log 'scope' on it's own, it clearly shows a 'rating' property that's populated as expected.

Also if I just enter a hard-coded number for the 'rating' attribute the directive works as expected.

I'm not really sure where I'm going wrong here? Any ideas?

Essentially the problem is, as you're using ng-show , your directive gets loaded into DOM tree before your star value retrieved from the Ajax , it processed and barrating gets attached to DOM with star value 0 . Basically what ng-show does is, it just hide or show DOM on html, just by toggling display css property on DOM.

So You can have two options to make your star component working.

  1. Use ng-if instead of ng-show
  2. Use $watch inside a component to update star rating(this will be good solution to go).

     link: function (scope, elem, attr) { console.log('rating', scope.rating); elem.barrating({ theme: 'css-stars', readonly: true }); scope.$watch('star', function(newValue){ elem.barrating('set', newValue); }); } 
  3. You could have combination of both. Show ratings only when you have rating using ng-if & then any change in rating will be taken care by $watch to update on barrating element.

Change the directive to react to changes in the rating:

app.directive('starRating', function () {
    return {
        restrict: 'A',
        scope: false,
        link: function (scope, elem, attr) {

            elem.barrating({
                theme: 'css-stars',
                readonly: true
            });

            scope.$watch(attr.rating, function (newValue) {
                elem.barrating('set', newValue);
                console.log('rating', newValue);
            });

        }
    };
});

Coded this way, on every digest cycle the watcher checks for changes to the Angular Expression defined by the rating attribute, and updates the barrating plugin appropriately.


Update

I did think of using a watch but I was curious as to why my original setup didn't work.

The original setup didn't work because it sets the star rating, only once, when the directive initializes. Since the data arrives from the server after the directive initializes, the new data is not seen by the plugin. By using a watch, the setting updates every time the controller changes the variable including the time when the value arrives from the server.

Also notice that the other answer hardwires the watch to a specific scope variable (not wise). It is wiser to use an attribute to declare the specific scope variable. It makes for a more versatile directive.

When a directive lacks a template that uses AngularJS bindings, it is wiser to avoid isolate scope . Put watches directly on attributes as shown in this example.

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