26

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?

2 Answers 2

20

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/

Sign up to request clarification or add additional context in comments.

5 Comments

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?
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.
This does not work. setting scope:'=' in the directive makes scope undefined.
@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.
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.
15

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().

1 Comment

BINGO! Good solution to assign parent scope. Ideal for something like modals

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.