0

New to creating custom directives. It renders fine on the initial render. However, I am trying to $watch for changes to the original data, and then, trigger an update.

As a quick test, I created a button and used jQuery to update the costPerDay.costs array (by hand)...but the $watch still doesn't fire & my breakpoint wasn't reached.

Thanks for the help...

MY CONTROLLER LOOKS LIKE:
The GET is mocked to return an object, not a promise, so ignore that particular line. Once I get the $watch working, I will update this part of the code accordingly.

// CONTROLLER
application.controller('HomeIndexController', function ($scope, costPerDayDataService) {
    var vm = this;

    // Internal
    vm.on = {
        databind: {
            costPerDay: function () {
                // The GET is mocked to return an object, not a promise, so ignore this line
                var costPerDay = costPerDayDataService.get();
                $scope.data.costPerDay = costPerDay;
            }
        }
    };
    vm.databind = function () {
        vm.on.databind.costPerDay();
    };

    // Scope
    $scope.data = {
        costPerDay: {}
    };

    $scope.on = {
        alterCosts: function (e) {
            var costs = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300];
            $scope.data.costPerDay.costs = costs;
        }
    }

    // Databind
    vm.databind();
});

MY ELEMENT LOOKS LIKE:
This renders fine initially. I need to automate updates.

<ul id="sparks" class="pull-down pull-left">
    <li cost-per-day-sparkline costperday="data.costPerDay">
    </li>
</ul>

MY DIRECTIVE LOOKS LIKE:
I am just trying to get ONE of them to work...I will obviously remove the others when I get a working example. And yes, I am aware you should NOT update the $parent directly. I'm just trying to find a combination that works before I get fancy.

define([], function () {
    'use strict';

    function CostPerDaySparklineDirective() {
        return {
            replace: true,
            restrict: "AE",
            scope: {
                costperday: "=costperday"
            },
            templateUrl: '/modules/templates/sparklines/costperdaysparklinetemplate.html',
            link: function (scope, elem, attrs) {


                // This fails       
                scope.$watch('costperday', function (newval) {
                    // ... code to update will go here
                }, true);

                // This fails           
                scope.$watch('costperday', function (newval) {
                    // ... code to update will go here
                });

                // This fails
                scope.$parent.$watch('data.costPerDay.costs', function (newval) {
                    // ... code to update will go here
                });

                // This renders initially, but fails to fire again
                scope.$watch('scope.$parent.data.costPerDay.costs', function (newval) {
                    var eleSparkline = $('.sparkline', elem);
                    eleSparkline.sparkline(scope.costperday.costs, { type: "bar" });
                });
            }
        };
    }

    return CostPerDaySparklineDirective;
});

UPDATE:
Even using ng-click to test the $watch fails to hit the breakpoint...

<a ng-click="on.alterCosts()">Change Costs</a>
Prisoner ZERO
  • 13,848
  • 21
  • 92
  • 137
  • 1
    Try updating costPerDay.costs array from angular function instead of jQuery to check for scope digest problem or not. It might be problem that costPerDay.costs variable will be out of scope digest cyle. – krish Nov 30 '15 at 03:22
  • @krish I thought $watch "listened" for scope changes (much like an observable). Am I wrong? I would "think" that if it were observable, then HOW you change the watch-target shouldn't matter...but that may be incorrect. – Prisoner ZERO Nov 30 '15 at 04:26
  • Hi @Prisoner There is a slight diff between observable and $watch. observer checks the change in DOM element where as $watch checks expression also. See this http://stackoverflow.com/questions/14876112/angularjs-difference-between-the-observe-and-watch-methods if i am wrong please correct me so it will help me – krish Nov 30 '15 at 04:58
  • @PrisonerZERO Try running a `$scope.$apply();` in alterCosts(). I think you need to trigger a template digest – kyler Nov 30 '15 at 15:33
  • @flybear THAT WAS IT!!!! Thank you! I'm still a noob...make sure to write an answer so I can credit you. – Prisoner ZERO Nov 30 '15 at 16:00

2 Answers2

1

in this situation I would watch the actual get of the costPerDayDataService vs listening to the controllers scope variable. so in your controller you would 'set' the variable in costPerDayDataService and in your directive you would just inject your service and watch the get function. OR if you are using 1.3.x > you can use bindToController which I believe eliminates the whole need for watches.

bindToController: {
  costperday: '='
}
atsituab
  • 637
  • 2
  • 7
  • 20
  • +1 ...I will definitely try this. I am just doing some experimentation on a test application at the moment (trying to learn). – Prisoner ZERO Nov 30 '15 at 16:06
1

In this case I'd run $scope.$apply(); in your alterCosts method to trigger a template digest. This will update the value in the DOM, which your directive catches, and subsequently triggers the $watch.

For more information on $apply(), https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply

"$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events.."

In this particular scenario you're changing the value from a DOM event.

kyler
  • 633
  • 5
  • 10
  • If I am using ng-click...how is this considered "outside of the angular framework"? – Prisoner ZERO Nov 30 '15 at 16:09
  • You don't always need to. I just default to that solution when I have a scenario where a value is changed via controller or service but is not being updated in the DOM – kyler Nov 30 '15 at 16:10