2

I've built a directive has an object passed to it via bindToController, with the intent of editing it. Edit was working great until I needed to cancel an edit. To undo, I needed to create a shadow copy of the original object, edit it, then copy it back or discard based on save or cancel. I've tried to achieve this in the link property of the directive, using scope.watch on the the controller property. The watch fires once, when initialized, the property is undefined because nothing has used it yet, which is expected. It never fires again though, once a real object is put into the property.

Where have I gone wrong? Should I got back to using $scope because I'm having issues getting a reference to the controller? Why is that watch only firing once?

The directive:

angular.module("ISG").directive('isgEditingFundDirective', function () {
    var ctrl = null;
    var isgEditingFundDirectiveController = function () {
        ctrl = this; // Getting a reference to the controller so it can be used in the link function.  Is there a better way to do this?

        this.cancel = function () {
            // Undo the edit
            ctrl.fund = null;
        };

        this.save = function () {
            // Save back to the original model
            angular.copy(ctrl.shadow, ctrl.fund);

            // Set to null because we aren't editing anymore
            ctrl.fund = null;
        }
    }

    var isgEditingFundDirectiveLink = function (scope, element, attrs) {
        // need link so we can undo an edit




        scope.$watch(ctrl.fund, function (orig, updated) {
            // Trying to watch the fund property in the controller so I can create a copy for undo later.   
            // This never fires with a real value
            angular.copy(ctrl.fund, ctrl.shadow);
        });
    }

    return {
        restrict: "E",
        controllerAs: 'editFundCtrl',
        templateUrl: "/angular/editfund",
        bindToController: {
            fund: "=fund"
        },
        controller: isgEditingFundDirectiveController,
        link: isgEditingFundDirectiveLink

    };
});

The Template:

Editing fund

Name:
<input ng-model="editFundCtrl.shadow.FundName"/>
<button ng-click="editFundCtrl.cancel()">Cancel</button>
<button ng-click="editFundCtrl.save()">Save</button>

<pre>{{editFundCtrl.fund}}</pre>
Darthg8r
  • 12,377
  • 15
  • 63
  • 100
  • 1
    the problem is you are trying to put watcher on the variable which is in `this` context instead of scope, [this answer](http://stackoverflow.com/a/24078893/2435473) will help you in this case – Pankaj Parkar Jan 27 '16 at 20:30
  • I don't see why you need a watch for this in the first place – charlietfl Jan 27 '16 at 20:35
  • @charlietfl Consuming this directive is a view that lists funds. Clicking a fund has its controller set a property on itself that gets passed to the directive above. This is an excerpt from the parent view: I'm open to other suggestions.... – Darthg8r Jan 27 '16 at 20:44
  • @PankajParkar Submit that as an answer and I'll accept. I would like to hear charlietfl ideas on why $watch isn't required. – Darthg8r Jan 27 '16 at 20:45

2 Answers2

1

Basically you are trying to put watch on the variable which belongs to this context of controller. $watch function accepts string scope variable name OR function which will evaluate on each digest cycle.

You could solve this issue just by having putting function inside watcher.

scope.$watch(function(){
    return ctrl.fund;
}, function (orig, updated) {
   // Trying to watch the fund property in the controller so I can create a copy for undo later.   
   // This never fires with a real value
        angular.copy(ctrl.fund, ctrl.shadow);
});

Otherwise you could also solve this problem by having angular.bind on this, refer this answer

Community
  • 1
  • 1
Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
0

If I'm reading this right and ctrl.fund is an object, you want

scope.$watch(ctrl.fund, function (orig, updated) {
     // do watch stuff here
}, true);

The true as the second parameter forces a "deep watch", where Angular will do an angular.equals() against the object each time it might have changed, which checks each (non-prototypical) property on the object.

For primitive types like String and Number, you'll want to have ctrl.fund as a string, since references will be lost updates, e.g. scope.$watch('ctrl.fund', function (orig, updated). Angular will figure out how to parse it.

Bryan K
  • 437
  • 4
  • 15