Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pitfalls of the New AngularJS ng-ref Directive

The release of AngularJS V1.7.1* introduces the new ng-ref directive. While this new directive enables users to easily do certain things, I see great potential for abuse and problems.

The ng-ref attribute tells AngularJS to publish the controller of a component on the current scope. This is useful for having a component such as an audio player expose its API to sibling components. Its play and stop controls can be easily accessed.

The first problem is the player controls are undefined inside the $onInit function of the controller.

Initial vm.pl = undefined  <<<< UNDEFINED
Sample = [true,false]

For code that depends on data being available, how do we fix this?

The DEMO

angular.module("app",[])
.controller("ctrl", class ctrl {
  constructor() {
    console.log("construct")
  }
  $onInit() {
    console.log("onInit", this.pl);
    this.initPL = this.pl || 'undefined';
    this.sample = this.pl || 'undefined';
    this.getSample = () => {
      this.sample = `[${this.pl.box1},${this.pl.box2}]`;
    }
  }
})
.component("player", {
  template: `
    <fieldset>
      $ctrl.box1={{$ctrl.box1}}<br>
      $ctrl.box2={{$ctrl.box2}}<br>
      <h3>Player</h3>
    </fieldset>
  `,
  controller: class player {
    constructor() {
      console.log("player",this);
    }
    $onInit() {
      console.log("pl.init", this)
      this.box1 = true;
      this.box2 = false;
    }
  },
})
<script src="//unpkg.com/[email protected]/angular.js"></script>
<body ng-app="app" ng-controller="ctrl as vm">
    Initial vm.pl = {{vm.initPL}}<br>
    Sample = {{vm.sample}}<br>
    <button ng-click="vm.getSample()">Get Sample</button>
    <br>
    <input type="checkbox" ng-model="vm.pl.box1" />
      Box1 pl.box1={{vm.pl.box1}}<br>
    <input type="checkbox" ng-model="vm.pl.box2" />
      Box2 pl.box2={{vm.pl.box2}}<br>
    <br>
    <player ng-ref="vm.pl"></player>
</body>
like image 587
georgeawg Avatar asked Aug 06 '18 21:08

georgeawg


People also ask

What is wrong about AngularJS?

It's slow, and it is especially all turns bad on mobile platforms and when you write something something complex. And it is a fundamental part of the framework. Angular even imposes restrictions on how rich UI you can write.

What is Ng ref?

The ngRef attribute tells AngularJS to assign the controller of a component (or a directive) to the given property in the current scope. It is also possible to add the jqlite-wrapped DOM element to the scope. If the element with ngRef is destroyed null is assigned to the property.

What is the use of NG in AngularJS?

The ng-app directive defines the root element of an AngularJS application. The ng-app directive will auto-bootstrap (automatically initialize) the application when a web page is loaded.

Which of the following is not an AngularJS directive?

ng-state is not an AngularJS directive. Q 15 - Which of the following is true about ng-app directive? A - ng-app directive defines and links an AngularJS application to HTML. B - ng-app directive indicates the start of the application.


1 Answers

Getting ref to components controller isn't new, directives back in the day allowed it and that wasn't a problem at all, it's necessary to have such feature, ng-ref is just a helper for you to do this from the template side (the same way angular 2+ does).

Nevertheless, if you need the child components ready you should use $postLink() instead of $onInit. $postLink is called after the component is linked with his children, which means, the ng-ref will be ready when it gets called.

So all you have to do is change your onInit like so:

̶$̶o̶n̶I̶n̶i̶t̶(̶)̶ ̶{̶
$postLink() {
    console.log("onInit", this.pl);
    this.initPL = this.pl || 'undefined';
    this.sample = this.pl || 'undefined';
    this.getSample = () => {
      this.sample = `[${this.pl.box1},${this.pl.box2}]`;
    }
}

$postLink() - Called after this controller's element and its children have been linked. Similar to the post-link function this hook can be used to set up DOM event handlers and do direct DOM manipulation. Note that child elements that contain templateUrl directives will not have been compiled and linked since they are waiting for their template to load asynchronously and their own compilation and linking has been suspended until that occurs. This hook can be considered analogous to the ngAfterViewInit and ngAfterContentInit hooks in Angular. Since the compilation process is rather different in AngularJS there is no direct mapping and care should be taken when upgrading.

Ref.: Understanding Components

The full working snippet can be found bellow (I removed all console.log to make it clearer):

angular.module("app",[])
.controller("ctrl", class ctrl {
  constructor() {
    //console.log("construct")
  }
  $postLink() {
    //console.log("onInit", this.pl);
    this.initPL = this.pl || 'undefined';
    this.sample = this.pl || 'undefined';
    this.getSample = () => {
      this.sample = `[${this.pl.box1},${this.pl.box2}]`;
    }
  }
})
.component("player", {
  template: `
    <fieldset>
      $ctrl.box1={{$ctrl.box1}}<br>
      $ctrl.box2={{$ctrl.box2}}<br>
    </fieldset>
  `,
  controller: class player {
    constructor() {
      //console.log("player",this);
    }
    $onInit() {
      //console.log("pl.init", this)
      this.box1 = true;
      this.box2 = false;
    }
  },
})
<script src="//unpkg.com/[email protected]/angular.js"></script>
<body ng-app="app" ng-controller="ctrl as vm">
    Initial vm.pl = {{vm.initPL}}<br>
    Sample = {{vm.sample}}<br>
    <button ng-click="vm.getSample()">Get Sample</button>
    <br>
    <input type="checkbox" ng-model="vm.pl.box1" />
      Box1 pl.box1={{vm.pl.box1}}<br>
    <input type="checkbox" ng-model="vm.pl.box2" />
      Box2 pl.box2={{vm.pl.box2}}<br>
    <player ng-ref="vm.pl"></player>
  </body>
like image 141
lenilsondc Avatar answered Nov 15 '22 06:11

lenilsondc