Brian Ogden
Brian Ogden

Reputation: 19232

Attribute directive scope.apply not working when used within ng-repeat

I am using an attribute directive to update the DOM, a variable called profileImage. The attribute directive is called ppt-profile-icon and works fine as long as it is not inside ng-repeat. I have looked through a myriad of questions on Stackoverflow and have not found a solution.

Here is my HTML:

<div class="img-preview" ng-style="{'background-image': 'url(' + profileImage + ')'}"></div>


<ul class="dropdown-menu pre-defined-icons" uib-dropdown-menu role="menu" aria-labelledby="single-button">
    <li ng-repeat="predefinedImage in vm.predefinedImages">
        <a>
            <img ppt-profile-icon src="{{predefinedImage}}" width="100%" />
        </a>
    </li>

    <!--This, outside of the ng-repeat will work-->
    <li>
        <a>
            <img ppt-profile-icon src="content/img/people/noProfileIcon.svg" width="100%" />
        </a>
    </li>
</ul>

And here is my directive:

angular
    .module('app.core')
    .directive('pptProfileIcon', function ($timeout) {
        //link function for DOM Minipulation
        function linkFunction(scope, elem, attrs) {
            elem.bind('click', function () {
                scope.profileImage = attrs.src;
                scope.$apply();
            });

        }//end

        //Directive Declaration
        return {
            restrict: "A",
            controller: ['$scope', function ($scope) {
                //sets enviornment
            }],
            link: linkFunction
        }//end

    });//end

In my link function I have tried:

attrs.$observe('src', function (val) {
    $timeout(function () {
        scope.profileImage = val;
        scope.$apply();
    });
});

$timeout(function () {
    scope.profileImage = attrs.src;
    scope.$apply();
});

Upvotes: 1

Views: 1081

Answers (2)

Andriy
Andriy

Reputation: 15472

you should always use object in your ngModels when interacting between parend - child scopes (see https://github.com/angular/angular.js/wiki/Understanding-Scopes for more details), so just adding vm.model before primitives would be sufficient:

<div class="img-preview" 
     ng-style="{'background-image': 'url(' + vm.model.profileImage + ')'}"></div>

and in the link function:

    //link function for DOM Minipulation
    function linkFunction(scope, elem, attrs) {

        elem.bind('click', function () {
          scope.vm.model.profileImage = attrs.src;
          scope.$apply();
        });

    }//end

plunker: http://plnkr.co/edit/jddW7zURZoyLA8MKkm6t?p=preview

Upvotes: 1

Phil
Phil

Reputation: 165058

Problem is, scope in your directive is the ngRepeat directive's scope, not the parent so you're only setting profileImage within the repeater.

I'd go with an executable binding

.directive('pptProfileIcon', function() {
    return {
        restrict: 'A',
        scope: {
            pptProfileIcon: '&'
        },
        link: function(scope, elem) {
            elem.on('click', function() {
                scope.pptProfileIcon({
                    profileImage: elem.prop('src')
                });
                scope.$apply();
            });
        }
    }
})

Then, create a function in your controller scope to set the image

$scope.setProfileImage = function(image) {
    $scope.profileImage = image;
};

and set your template like this

<img ppt-profile-icon="setProfileImage(profileImage)" ng-src="{{predefinedImage}}" />

Plunker demo ~ http://plnkr.co/edit/GqFXqZW5AmLCyWHblBDN?p=preview

Upvotes: 1

Related Questions