0

I just started working on an Angular app that uses flot to plot a bunch of data. It worked fine for static data, but once we got the directive wired up to mongo, I had to follow the tutorial here to get it working for updating data. I had a hell of a time for one specific reason:

This is my directive HTML:

<div class="panel-body" data-ng-controller="flotChartCtrl">
  <div data-flot-line-chart data-data="revenueData.data" data-options="line1.options" style="width: 100%; height: 300px;"></div>
</div>

and javascript:

.directive("flotLineChart", [
    function () {
        return{
            restrict: 'A',
            scope: {
                data: "=",
                options: "="
            },
            link: function(scope, elem, attrs){
                var chart = null;
                // var options = { ... };

                scope.$watch('data', function(data, oldData) {
                    if(!chart) {
                        chart = $.plot(elem, data, options);
                        elem.show();
                    } else {
                        chart.setData(data);
                        chart.setupGrid();
                        chart.draw();
                    }
                });
            }
        };
    }
])

As you can see in the html, I'm using the data-options attribute to pass the line1.options object into the directive. When I was just using static data and not using ng-model or the $watch function, this worked and the scope: { options: "=" } assignments were correct. However it seems that whenever I set anything on the scope outside link, it breaks the $watch. $watch always receives a data of undefined... and my scope.options are also undefined. Outside of the $watch function scope.options is correct, but that doesn't help me much if I can't use them when the data is actually plotted.

I've had to resort to hard coding the options inside link: and commenting out the outer scope assignments. I have a bunch of different charts I need to create, all of which look differently. I'd hate to have to hard code different options for EVERY one, but at the moment I don't see any other way to make this work. Is there some way I can access my other data attributes from the HTML inside the $watch function without it breaking everything?

Note: I tried attrs.options, but that just gives me a "line1.options" string, and not the actual object.

Edit1:

Updated my code per ExpertSystem's suggestions. No longer using ng-model. scope is still not available inside $watch:

Proof that scope isn't defined inside $watch

Community
  • 1
  • 1
Mordred
  • 3,734
  • 3
  • 36
  • 55
  • Tip for avoiding hard times in the future: RTFM (and make sure you UTFM) :) – gkalpak Jun 09 '14 at 17:20
  • Believe me, I am reading the manual, but I'm not finding the answers I'm necessarily looking for (or if I am finding them, I'm apparently not understanding them). Unfortunately, I'm still having a hard time, so if you've got a solution instead of an obvious comment, I'd love to hear it. – Mordred Jun 09 '14 at 17:32
  • I posted the comment after I posted a solution :) – gkalpak Jun 09 '14 at 17:35
  • Sorry, wasn't paying attention to the name. Thought that was someone else :) – Mordred Jun 09 '14 at 17:36

1 Answers1

2

Your directive should look like this:

...
restrict: 'A',
scope: {
    data:    '=ngModel',
    options: '='
},
link: function(scope, elem, attrs){
    ...
    scope.$watch('data', function(newValue, oldValue) {
        ...

Althouth the use of ngModel seems redundant here.


This fiddle demonstrates that scope is indeed defined in the $watch callback.

gkalpak
  • 47,844
  • 8
  • 105
  • 118
  • That fixes the problem of using scope.data inside the $watch for me, but `scope` still doesn't exist inside $watch, which means I can't access `scope.options` inside the $watch... which is my major issue. – Mordred Jun 09 '14 at 17:30
  • `scope` is definitely available inside the `$watch` callback. Please, post you exact code (where `scope` is undefined). – gkalpak Jun 09 '14 at 17:33
  • That's my exact code. It's telling me scope is not defined. – Mordred Jun 09 '14 at 17:41
  • Stupidly realized that while `scope` may not be available inside `$watch`, variables declared inside the parent link function *are* available, and `scope` is there, so I'm just assigning `var options = scope.options`, and that works inside my `$watch`. Thanks for the help. – Mordred Jun 09 '14 at 18:06
  • In the posted code thare still is **not** any reference to `scope`. I am glad you found a workaround, but `scope` is available inside of the `$watch` callback (exaclty because it is defined inside the parent function). Maybe you are using `options`, instead of `scope.options`. – gkalpak Jun 09 '14 at 18:13
  • You're correct, there's not any reference to `scope` in that code, but there has been -- *multiple* times. It's not currently there, because `scope` *is undefined there* and so I had to take it out and use the `options` variable I'd declared inside `link`. The console screenshot that I posted shows that `scope` is undefined. I placed a breakpoint inside `$watch`, and when it was hit, I attempted to reference `scope` from the console, and as you can clearly see, chrome believes that scope is undefined. – Mordred Jun 09 '14 at 18:25
  • There may be several reasons for that. E.g. you are in the top-frame, but your code may be running in another frame etc. See at the end of my updated answer for a proof that `scope` **is defined inside the `$watch` callback** !!! You are one stubborn fella, aren't you :P – gkalpak Jun 09 '14 at 18:33
  • Sigh. As soon as I put back the reference to `scope.options` inside `$watch`, the debugger no longer thinks `scope` is undefined. Guess I can't trust the debugger anymore. This was driving me crazy because I couldn't see any reason you would be wrong, but then chrome was telling me differently. Thanks again for the help... and I'm obviously not the only stubborn one on this question. ;) – Mordred Jun 09 '14 at 18:41
  • Actually, it makes sense. It has to do with JS closures and scoping. – gkalpak Jun 09 '14 at 18:55