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?
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>
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.
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.
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.
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.
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 thengAfterViewInit
andngAfterContentInit
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With