1
votes

This is probably a stupid question, but I have been stuck on it for days and neither of the googled solutions panned out for me.

I'm writing an angular 1.4 app following a directive driven approach, so for any entity I have one or more widgets:

  • isolated scope
  • controller as
  • bindToController true

The problem is of the "user has stuff" variety.

<user-widget>
   <stuff-widget userid="user._id"></stuff-widget>
</user-widget>

The userid passes nicely into the widget, but I would like to use it inside the stuff widget's link function, because stuff is really complicated and in the real world I need to grab various other parts as well.

I tried various methods to get to the userid value (as suggested in various other stackoverflow discussions and elsewhere on the web)

  • use bidrectional binding -> not effective
  • tried via scope and controller -> userid, uid not defined
  • require ^userWidget -> lost access to my controller
  • use attrs.$observe('userid', ....) -> js error: userid not defined
  • passed it via pre link of the parent -> did work, but not a good idea/limited merits

I have a plunker with the various things I tried: http://plnkr.co/edit/SqlhYSteCDxMaAVZWCsy?p=info

The widgets look like this (working pre link variant)

  function userWidget() {
    return {
      restrict: 'EA',
      scope:{},
      template: '<h2>User</h2> {{user.name}}<stuff-widget userid="user._id"></stuff-widget>',
      replace: false,
      controllerAs: 'userCtrl',
      bindToController: true,
      controller: 'UserCtrl',
      link: { // this actually works, not sure wether this a good idea
        pre: function preLink(scope, element, attrs, ctrl) {
          var uid = 1;
          scope.user = ctrl.findById(uid);
          scope.userid = scope.user._id;
    },
    post: function postLink(scope, element, attrs, ctrl) {}
      }

    };
  }

  function stuffWidget() {
    return {
      restrict: 'EA',
      scope: {
        uid: '=userid'
      }, 
      template: '<h3>User stuff</h3>stuffCtrl.userid inside widget: {{stuffCtrl.userid}}<div ng-repeat="stuff in stuffCtrl.userStuff">\
User id: {{stuff.userid}}: {{stuff.stuff}}\
</div>',
      replace: false,
      controller: 'StuffCtrl',
      controllerAs: 'stuffCtrl',
      bindToController: {
        userid: '='
      },
      link: function (scope, element, attrs, ctrl) {
    console.log('stuff ctrl userid:', ctrl.userid); // not defined
    console.log('stuff scope uid:', scope.uid); // not defined
    console.log('Scope parent userid: ',scope.$parent.userid); // undefined

    // didn't work either - userid not defined
    /* attrs.$observe('userid', function(value) {
      if (value) {
        ctrl.userid = userid;
        console.log('stuff ctrl userid observed:', ctrl.userid);
      }
    }); */
    ctrl.userStuff = ctrl.findByUserId(ctrl.userid);
      }
    };
  } 

I'm an angular beginner (first serious project) and so far my experience has been: "if it's hard to figure out, you are probably doing it the wrong way".

So,

  • Did I miss an obvious way to access the param inside the link function?
  • Did I screw up the ways I tried (especially $observe) because I incorrectly transferred them to my setting?
  • Should I just stick the controller calls inside the template and be done with it?
  • Should I go about it in an entirely different way? compile function? controller function? whatever other angular depths I'm not yet familiar with?
2

2 Answers

0
votes

After the link function has finished its execution you will have to wait one digest cycle to update the values in the isolated scope from the scope where the directive is used.

You can try this snippet in stuffWidged:

link: function(scope, element, attrs, ctrl) {
  var uid, usrStuff;

  scope.uid; // it is normal to be undefined here

  scope.$watch('uid', function(newUid) {
    uid = newUid;

    scope.uid; // is ready and is same as newUid

    usrStuff = ctrl.usrStuff = scope.usrStuff =
      ctrl.findById(uid);
  });
}
0
votes
  1. Remove isolated scope from user-widget - do u really need it there? If you are not defining any additional variables in directive scope - you do not actually need isolated scope.
  2. Isolated scope. So user-widget should have some parameters? Like:
<user-widget widgetusers="model.users"></user-widget>

then user-widget template will be:

<stuff-widget ng-repeat="stuffuser in widgetusers" userid="stuffuser._id"></stuff-widget>
  1. You can pass userId first to userWidget, then to stuffWidget:
<user-widget userid="user._id">
   <stuff-widget userid="userid"></stuff-widget>
</user-widget>