5

I have an issue with a data binding inside a directive, which call another directive.

Here is the main directive :

var app = angular.module('app');

app.directive("myMainDirective", function ($http) {
return {
    scope: {
        paramId: '='
    },
    link: function (scope) {
        $http.get('some/url/' + scope.paramId+ '.json'
        ).success(function (data) {
                scope.idFromServer = data;
            });
    },
    template: '<span other-directive id="idFromServer"></span>'
}
});

Here is the other directive :

var app = angular.module('app');

app.directive("otherDirective", ['$http', function(http) {
return {
    template: "<span>{{name}}</span>",
    scope: {
        id: "="
    },
    link: function(scope) {
        http.get('another/url/' + scope.id + '.json').then(function(result) {
            scope.name = result.data.name;
        }, function(err) {
            scope.name = "unknown";
        });
    }
}
}])

And the html code wich call the main directive :

<span my-main-directive param-id="myObject.id"></span>

When calling "other-directive", the "idFromServer" is not bind, and is "undefined", so it results to diplay "undefined".

I'm probably missing something stupid, but I don't see what ... (My piece of code is probabley not the best, I'm pretty new to angularjs, and specially the directives, and I tried a lot of ways to accomplish what I want.)

Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
  • When something is not working, it is helpful to specify any error messages, or the expected output and the actual output. – mattm Apr 17 '15 at 19:16
  • Matthieu - If you are using Chrome, you can right mouse click inside your page and choose inspect element, then click the console tab in the window that pops up... Do you see errors? What is it telling you? – Frank Tudor Apr 17 '15 at 19:21
  • Sorry, I forget to say what the problem is (I've edited my question). – Matthieu Lebigre Apr 17 '15 at 19:22
  • I just have a 404 error, because the "another-directive" fails to get the hhtp get, because id is "undefined" : GET http://localhost:63342/another/url/undefined.json 404 (Not Found) – Matthieu Lebigre Apr 17 '15 at 19:24
  • Make sure that you have IIS set up to be allowed to serve JSON files, you have to specify a mimeType in either IIS or on the web.config to be able to access them. See the answer on this question:http://stackoverflow.com/questions/17626776/why-is-my-json-file-not-found – Ben Black Apr 17 '15 at 19:27
  • I'm sure the issue is not in json file reachability, it works fine if "hardcode" the url. What's wrong is that in the url, "id" is resolved into "undefined" – Matthieu Lebigre Apr 17 '15 at 19:29
  • Ah, good point. Can you delay the second `$http` request until id is defined? You should be able to put a watch on that parameter and when it's not undefined make the call. – Ben Black Apr 17 '15 at 19:31
  • that's would indeed could do the trick. But how can I do that (not with a random delay value ...) ? – Matthieu Lebigre Apr 17 '15 at 19:33
  • Try putting a scope.$watch on that parameter, it gets fired off whenever the parameter value is updated. Theoretically, it should work. – Ben Black Apr 17 '15 at 19:36
  • Hmm, I'm not to know what is the correct way to use scope.$watch. What do I put as function ? – Matthieu Lebigre Apr 17 '15 at 19:39

3 Answers3

4

Per my comments, here's one way that might work, using a scope.$watch:

scope.$watch('id', function(id) {
    $http.get('some/url/' + id + '.json')
        .success(function (data) {
            scope.idFromServer = data;
        });
};

This would go inside the link function on the nested directive.

Ben Black
  • 3,751
  • 2
  • 25
  • 43
  • Yeah, adding this scope.$watch did the trick !! Is it a "good angular way" to do that ? – Matthieu Lebigre Apr 17 '15 at 19:58
  • I'd probably move it out of the scope of the second directive an into the scope of the main one, then put some way to observe changes in the second one. The answer below me provides a really good example of that. They're just different ways to skin the cat, both are "good anuglar" ways of doing it. – Ben Black Apr 17 '15 at 20:00
  • @BenBlack that is what I've done,,your is also too good answer. :) – Pankaj Parkar Apr 17 '15 at 20:30
2

One of the way I'd suggest is don't use two way(=) binding on idFromServer variable, use {{idFromServer}} Interpolation directive to assign value to attribute, & then use $attr.$observe it will call when interpolation is evaluated.

myMainDirective

app.directive("myMainDirective", function ($http) {
return {
    scope: {
        paramId: '='
    },
    link: function (scope) {
        $http.get('some/url/' + scope.paramId+ '.json'
        ).success(function (data) {
                scope.idFromServer = data;
            });
    },
    template: '<span other-directive id="{{idFromServer}}"></span>'
}
});

otherDirective

app.directive("otherDirective", ['$http', function(http) {
    return {
        template: "<span>{{name}}</span>",
        scope: true, //isolated scope
        link: function(scope, element, attr) {
            attr.$observe(attr.id, function(newVal) {
                http.get('another/url/' + newVal + '.json').then(function(result) {
                    scope.name = result.data.name;
                }, function(err) {
                    scope.name = "unknown";
                });
            });
        }
    }
}])
Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
1

Since javascript is asynchronous, your two ajax requests are running at basically the same time and id is undefined when the request in other-directive runs.

If you want to try testing this, just set a default value for idFromServer. The request in other-directive will run with the default value.

EDIT: in response to your comment, that is quite a broad question and there are many solutions. The best answer I can give you is simply, never ever run any logic in your link function, just define the directive's behavior and properties - that's what the link function is for. The template is used right away and you can't change that.

In this case, you could get the data prepared in a parent scope and pass the data in attributes.

Nick
  • 14,291
  • 3
  • 17
  • 19
  • That's what I was starting to think. With a default value, it works indeed well to get the correct json file for that default value, but how can I force "id" to be resolved before calling the template ? – Matthieu Lebigre Apr 17 '15 at 19:31