javascriptangularjsangularjs-httpangularjs-components

AngularJS 1.5 Components printed before gets the controller values


I've a parent view with a parent controller, with 2 children component, each one with his own controller.

Something like this:

<section>     // Main Controller
  <table-users listusers="users"></table-users>     // Component Table User Controller
  <table-news listusers="users"></table-news>       // Component Table News Controller
</section>

The Main Controller executes a GET XHR request to an API (which takes about 1 or 2 seconds), and the response of that request is passed to the components.

(function() {
  'use strict';

  angular
    .module('app.view')
    .component('tableUsers', {
      bindings: {
        listusers: '='
      },
      controller   : 'UsersTableController',
      controllerAs : 'vm',
      templateUrl  : 'whatever.html'

    });

})();

My problem is that the response is returned after the components is already printed.

So I got vm.listusers = undefined and I have a vm.listusers = [{},{}...] object...

If I create a fake vm.users in the MainController everything works, so the problem is that GET returns the response too late.

I just created this sample case using GitHub API: Plunker

Is there a way to tell the component to wait until this value is returned before printed in the DOM or something like that?


Solution

  • Since Angular 1.5.3 we have access to some sweet methods one of these is called $postLink its the way we can basically mimic the linker function in a component and a way to essentially only write components.

        vm.$postLink = function() {
          $timeout(function() {
            vm.users = vm.listusers;
          })
        }
    

    There is the one catch of the $timeout that I havent been able to get to work correctly without it. Anyways it says fire a digest cycle.

    Some places to read about it here, here and I also wrote about it here

    Now having said all that why are u binding a value that is already "bound to the controller" just use the binding that already exists...

     angular
    .module('app')
    .component('tableUsers', {
      bindings: {
        listusers: '='
      },
      controller   : 'ComponentController',
      controllerAs : 'vm',
      template  : '<h1>{{vm.title}}</h1>'+
                  '<p>{{vm.listusers}}</p>' +
                  '<ul>' +
                    '<li ng-repeat="user in vm.listusers">' +
                      '<strong>{{user.login}}</strong><span> - {{user.id}} </span>' +
                    '</li>' +
                  '</ul>'
    });
    

    No need to make vm.users = vm.listusers... its already bound to the controller, just use it...

    Inside the source code for the component you will see that the bindToController line that we are familar with from directives defaults to the bindings array we pass it.

    bindToController: options.bindings || {},
    

    Components are all about removing that crappy boilerplate that directives forced upon us.

    Also you can then take advantage as well of controllerAs naming that comes out of the box. No need to give it the meaningless name of vm in the template $ctrl is the "free" binding we are also given out of the box - I strongly advocate for its use.

     angular
    .module('app')
    .component('tableUsers', {
      bindings: {
        listusers: '<' // Use 1 way bindings to enforce uni-directional data flow
      },
      controller   : 'ComponentController',
      template  : '<h1>{{$ctrl.title}}</h1>'+
                  '<p>{{$ctrl.listusers}}</p>' +
                  '<ul>' +
                    '<li ng-repeat="user in $ctrl.listusers">' +
                      '<strong>{{user.login}}</strong><span> - {{user.id}} </span>' +
                    '</li>' +
                  '</ul>'
    });
    

    Finally your PLUNKR