Darthg8r
Darthg8r

Reputation: 12675

Undo in Angular Directive

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>

Upvotes: 2

Views: 635

Answers (2)

Pankaj Parkar
Pankaj Parkar

Reputation: 136154

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

Upvotes: 1

Bryan K
Bryan K

Reputation: 477

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.

Upvotes: 0

Related Questions