0

Summary:

I'm attempting to take an id from the $scope of a view's controller and pass it to my directive which then makes some more http calls using that id.

Here's my controller and directive js:

app.controller('MainCtrl', function(postsFactory) {
  _this = this;

  postsFactory.get({ id: $state.params.id}).$promise.then(function(res){

      //assign the response to this controller as a post and
      //output the controller on the view as editPostCtrl.
      _this.post = res;
      _this.idOfPost = _this.post.id;

      // the post.id does exist              
  });
});

app.directive('foo', function() {
  return {
    restrict: 'E',
    scope: {
      postId: '=postId',
    },
    controller: function($scope) {
      //make some more http calls with the postId
    },
    link: function(scope, element, attr) {
      scope.$watch('postId', function(newVal) {
        if(newVal) { postId = newVal;}
      });
    }
  };
});

The view:

  <body ng-controller="MainCtrl as mainCtrl">
    <foo post-id="mainCtrl.idOfPost"></foo>
  </body>

Issue:

The id never makes it to the view. When I inspect element and look at the directive's attribute, I see

post-id="mainCtrl.idOfPost"

Since I make a call to get the id in the main view's controller and bind it to the controller using the 'ctrl as' syntax, I saw I need to watch for it in the directive because the the main view's controller will return a promise: see this for ref: Directive is being rendered before promise is resolved

Well, this didn't work for me. However, I did find that using the watch method and also wrapping the directive in an ng-if would work:

<div ng-if="main.idOfPost">
  <foo  post-id="mainCtrl.idOfPost" ></foo>
</div>

However, this seems unnecessary to have logic in the view like this and makes the directive harder to use. I'm just not sure how to resolve it in the js. Any suggestions is appreciated.

Community
  • 1
  • 1
friendlyfire
  • 87
  • 1
  • 12

4 Answers4

0

I've had issues similar to this. Try using your $watch with a function to return the scope value

scope.$watch(function(){
    return scope.postId;
}, function(){  //whatever });

It's possible that since the $promise is fulfilled after the bindings, it may cause issues with the directive. If this doesn't work upload an example to Plunker and I'll take a look.

Also, I think you should interpolate your directive values with double curlys as well

<foo post-id="{{mainCtrl.idOfPost}}"></foo>

as opposed to

<foo post-id="mainCtrl.idOfPost"></foo>

Hope this helps

Keith James
  • 167
  • 1
  • 12
  • Can you give me a more explicit example using that watch concept? Maybe use my code in your example. I played with yours and couldn't get it to work. With or without {{}} on the view, too. – friendlyfire Jan 29 '16 at 17:32
0

I would include an init value of your idOfPost in your controller

_this.idOfPost = '';

And the $watch function in your directive link should have as 1st parameter, a function returning the variable that you want to watch:

link: function(scope, element, attr) {
   scope.$watch(
       function() { return scope.postId;},
       function(newVal,oldValue) {
             if(newVal!==oldValue) {   postId = newVal; }
  });
brewsky
  • 607
  • 1
  • 9
  • 15
  • Tried this and it didn't work. I placed init _this.idofPost = ''; inside and outside of the postFactory.get function. Still notta. – friendlyfire Jan 29 '16 at 17:54
  • You need to change your $watch to return a function. The 'postId' parameter to the watch is being treated like a string literal. And a string literal will never change. **You need to change the $watch() function.** – brewsky Jan 29 '16 at 18:47
0

What you might have to do is implement the $scope.$watch in the controller of the directive and not the link, then fire off any additional http calls you'd need to do inside that watch callback.

Here's a plnkr with a basic setup: http://plnkr.co/edit/pUybxobHYI6vvjd0M5ON?p=preview

paul trone
  • 702
  • 5
  • 10
  • This worked. However, It broke a filter I had set up for the directive. Any way to avoid that? – friendlyfire Jan 29 '16 at 18:17
  • Ok, I tried injecting the filter inside the controller using this: http://stackoverflow.com/questions/14302267/how-to-use-a-filter-in-a-controller. and then put the call to the filter in the with the rest of the calls but still no love. – friendlyfire Jan 29 '16 at 18:44
  • it's hard to say what's wrong, perhaps if you're using the filter inside a callback it doesn't have access to all the variables it used to – paul trone Jan 29 '16 at 19:18
0

The only one that came close enough for me was @Paul147 's suggestion. However, I also had a filter the directive was using and it caused deeper issues that were really difficult to figure out. So I decided on an entirely new option by resolving the data before the views load via ui-router. Works like a charm now and no $watch!

Here's where I picked it up from. https://scotch.io/tutorials/making-skinny-angularjs-controllers

friendlyfire
  • 87
  • 1
  • 12