How to modify scope from within a directive in AngularJs

58,857

Solution 1

I updated the fiddle, basically had to go to the parent to get the right "selected" variable, also used the isolate scope = to get two way binding between the value passed in and the internal model.

http://jsfiddle.net/nJ7FQ/2/

angular.module('app', [])

    .directive("customTag", [function () {
    return {
        restrict: "E",
        replace: true,
        template: "<input type='button' value='Click me' />",
        scope: {model:'='},

        link: function (scope, element, attrs) {
            element.bind('click', function () {
                scope.model[attrs.selectedItem] = "New value";
                scope.$apply();
            });
        }
    };
}]);

function AppController($scope) {
    $scope.selected = 'Old value';
}

and the HTML

<div ng-app="app" ng-controller='AppController'>
    <p>Selected: {{ selected }}</p>
    <div ng-switch on="selected">
        <div ng-switch-default>
            <p>Item: {{ selected }}</p>
            <custom-tag selected-item="selected" model="$parent" />
        </div>
        <div ng-switch-when="New value">
            <p>Worked</p>
        </div>
    </div>
</div>

Updated the fiddle to use your original reading of the property from the attribute: http://jsfiddle.net/nJ7FQ/4/

Solution 2

I improved the jsfiddle a bit:

angular.module('app', [])

    .directive("customTag", ['$parse', function ($parse) {
    return {
        restrict: "E",
        replace: true,
        template: "<input type='button' value='Click me' />",

        link: function (scope, element, attrs) {
            element.bind('click', function () {
                scope.$apply(function () {
                    $parse(attrs.selectedItem).assign(scope.$parent, "New value");
                });
            });
        }
    };
}]);

function AppController($scope) {
    $scope.selected = { 'foo': 'Old value' };
}

http://jsfiddle.net/nJ7FQ/15/

This way, the scope value, you want to change can also be an object property like selected.foo in the example. Also, I removed the scope parameter and told the directive to always use the parent scope. And finally I wrapped the click handler into the $apply callback (see here for example). Better would be, of course, to use ngClick instead of the element.bind().

Share:
58,857

Related videos on Youtube

Fernando
Author by

Fernando

Updated on July 09, 2022

Comments

  • Fernando
    Fernando almost 2 years

    I need to modify a root scope attribute from within a callback inside a directive. But the directive is in a inner scope created by a switch directive.

    HTML

    <div ng-app="app" ng-controller='AppController'>
        <p>Selected: {{ selected }}</p>
        <div ng-switch on="selected">
            <div ng-switch-default>
                <p>Item: {{ selected }}</p>
                <custom-tag selected-item="selected" />
            </div>
            <div ng-switch-when="New value">
                <p>Worked</p>
            </div>
        </div>
    </div>
    

    JavaScript

    angular.module('app', [])    
        .directive("customTag", [function () {
        return {
            restrict: "E",
            replace: true,
            template: "<input type='button' value='Click me' />",
    
            link: function (scope, element, attrs) {
                element.bind('click', function () {
                    scope[attrs.selectedItem] = "New value";
                    scope.$apply();
                });
            }
        };
    }]);
    
    function AppController($scope) {
        $scope.selected = 'Old value';
    }
    

    Fiddle: http://jsfiddle.net/nJ7FQ/

    My objective is to be able to display "New value" in the Selected area. How can I accomplish what I am trying to do? What am I doing wrong?

    Besides, as I am trying to make a component. Is there a way to do the same but with an isolated scope?

  • Fernando
    Fernando almost 11 years
    I liked better your first solution (jsfiddle.net/nJ7FQ/2) It seems cleaner somehow. But why should I use $parent.selected when, from what I understand, child scopes inherits all the properties of the parent?
  • shaunhusain
    shaunhusain almost 11 years
    Well if you change the selected for the one that was inherited it doesn't change the parent scopes selected variable, by going up to the parent and changing the variable on that scope then when the child selected value is read it goes up to the parent. If you change the child scope's selected property it would become a new property on the child scope and no longer inherit. (I know it's confusing) This is Javascript prototypical inheritence if you want to Google it.
  • dopatraman
    dopatraman over 9 years
    This does not work. setting scope:'=' in the directive makes scope undefined.
  • shaunhusain
    shaunhusain over 9 years
    @dopatraman, yeah having learned more about angular in the last year, I wouldn't agree with my own answer here, that said I'm not sure that the issue you're encountering is related to the problems I would have with this method. If you posted another SO question with your code and problem link it here and I'll take a look.
  • saike
    saike over 8 years
    BINGO! Good solution to assign parent scope. Ideal for something like modals
  • The Red Pea
    The Red Pea over 7 years
    Note that link must be used; this alternative, exactly the same as shaunhusain's, except for using controller, doesn't work. Presumably it's because isolate scope is only set up in time for the link function, not the controller function.