Why do the Angular docs recommend against ng-init in most cases? - javascript

From the ng-init documentation:
The only appropriate use of ngInit is for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
That doc goes on to give the following example:
<div ng-repeat="innerList in list" ng-init="outerIndex = $index">
// [snip]
</div>
What makes it appropriate to use ng-init with ng-repeat but not, for example, with ng-model like this:
<input ng-model="thing.prop"
ng-init="thing.prop = thing.prop || 'defaultValue'">
The doc says one should "use controllers rather than ngInit". What benefit does a controller offer in this case? Is this an Angular stylistic preference, or are there cases in which code like the above will not work?

That's because the documentation should be as concise as posible, and if in each example they create a Controller, it won't be so precise and clear

Related

How to use Angular structural directive with multiple inputs

I want to implement something similar with angular-permisssion. And with requirement to control the element's existance, I need to use angular structural directive.
At the beginning, i think such syntax would work:
<h2 *permissionIf [permissionIfExcept]="'Read'">Except</h2>
However, it doesn't work that way.
Moreover, the offical guide only teach you how to write custom structural directive with single input.
With multi-inputs, some third-party tutorials involve a bit. But that's using the angular template micro-syntax to achieve data binding. Then one problem occurs:
template syntax doesn't support pure key-value inputs:
<h2 *permissionIf="except: map.except;only: 'test'">Except</h2>
It expands into this(which is illegal):
<h2 template="permissionIf except: map.except;only: 'test'">Except</h2>
A stupid temporary solution is add a useless variable declaration.
<h2 *permissionIf="let i;except: map.except;only: 'test'">Except</h2>
Another inconvenient way is to use template element to wrap the code.
<template permissionIf [permissionIfExcept]="'Read'">
<h2>Except</h2>
</template>
The above all are not accepetable enough. But I can't find a bette way to resolve it.
Hope some guys can give some suggestion:).
The input names need all to be prefixed with the selector of the directive, followed by the input name capitalized (i.e. permissionIfExcept). Example:
#Directive({
selector: '[permissionIf]'
})
export class PermissionIfDirective implements AfterContentInit {
private _permissionIf:string[];
#Input()
set permissionIf(value: string[]) {
this._permissionIf=value;
console.log('permissionIf: ', value);
}
private _except:string;
#Input()
set permissionIfExcept(value: string) {
this._except = value;
console.log('except: ', value);
}
}
To use them with the '*' syntax:
<div *permissionIf="permissions;except:'Read'"></div>
Note here you're using the name following the prefix uncapitalized (i.e. except). Also note the : in the assignment.
The explicit syntax (using template) would look like this:
<template [permissionIf]="permissions" [permissionIfExcept]="'Read'">
</div></div>
</template>
but with <ng-container> it could look like
<ng-container *permissionIf="permissions;except:'Read'">
<div></div>
</ng-container>
Plunker example
See also the source of NgFor as an example.
#Günter Zöchbauer answer is almost correct.
Actually right now to make his answer working you need to explicitly rename the secondary #Input name.
So it should be:
#Input("permissionIfExcept")
set permissionIfExcept(value: string) {
this._except = value;
console.log('except: ', value);
}
Then one problem occurs: template syntax doesn't support pure key-value inputs:
True
A stupid temporary solution is add a useless variable declaration.
I think you are using this in a way it was not meant to be.
From the docs:
The microsyntax parser title-cases all directives and prefixes them with the directive's attribute name, such as ngFor. For example, the ngFor input properties, of and trackBy, become ngForOf and ngForTrackBy, respectively. That's how the directive learns that the list is heroes and the track-by function is trackById.
https://angular.io/guide/structural-directives#microsyntax-examples
Bottom line is in the context of your question, the microsyntax accepts "expression", followed by optional "keyed expression"s and I'm afraid those are your only options.
One could of course pass an object as the first expression—similar to ngIf—, the difference being you can teach your directive how to evaluate the expression:
*permissionIf="{ only: 'whatever', except: ['things', 'stuff'] }"

Variables variables possible in angularjs?

Im working in a basic translation service for angularjs, I have an object in the view with the translations this way:
var translations = {"name":"Name", "address":"Address", "phone":"Telephone"};
So I want to replace if I found the {{phone}} in the view with the value of its translation: "Telephone".
Is there some way to call variables variables while iterate, like this:
<div class="item item-text-wrap" ng-repeat="(k, v) in profile_fields">
<b>{{translations. {{k}} }}</b>
</div>
Thanks in advance!
Sure, just use this:
<b>{{translations[k]}}</b>
Basically, access the translations object like you would in JavaScript, using the k variable.
Keep in mind that you never have to nest those brackets ({{}}) deeper than this.
Also, Angular Translate is a pretty nice translations library. You might want to have a look at it.

angular.js: reference the current model by short variable name

suppose I am binding to a model using a long expression such as
<ul class="container" ui-sortable ng-model="cssRules.categories['sd-text-highlight- color']" ng-class="{selected: cssRules.categories['sd-text-highlight-color'] == selectedCategory}" ng-click="selectCategory(cssRules.categories['sd-text-highlight-color'])">
is there a way to not repeat cssRules.categories['sd-text-highlight-color'],
and just bind to the currently referenced model using some keyword or assigned variable name?
Try it with ng-init :
<ul
class="container"
ui-sortable
ng-init="mymodel = cssRules.categories['sd-text-highlight-color']"
ng-model=""
ng-class="{selected: mymodel == selectedCategory}"
ng-click="selectCategory(mymodel )"
>
Hope this helps
Why don't you expose it trough the controller like:
$scope.cssCategory = cssRules.categories['sd-text-highlight-color']
[UPDATE]
With regards to ng-init alternative (from Angular documentation):
The only appropriate use of ngInit is for aliasing special properties
of ngRepeat, as seen in the demo below. Besides this case, you should
use controllers rather than ngInit to initialise values on a scope.
More info.
This might be a reason that it doesn't work well with ui-sortable so I guess this approach should be avoided.

How do I get the current child-scope of a ngRepeated element in AngularJS?

This seems like a simple question, but I've been googling around for a while and can't seem to find it.
In my JS I have something called parseTags(book) that takes a JSON comma-separated list of tags (book.tags) and parses it into an array:
$scope.parseTags = function(book){
book.tags = book.tags.split(',');
};
In my HTML I have something like this:
<div ng-repeat="book in books" ng-init="parseTags(book)">{{book.title}}</div>
Is there a way just to get the child scope from within the $scope.parseTags function? Instead of passing in book each time?
Something like:
$scope.parseTags = function($childScope){
$childScope.tags = $childScope.tags.split(',');
}
<div ng-repeat="book in books" ng-init="parseTags()">{{book.title}}</div>
parseTags function is executed in context of the current child scope. So parseTags can also be written as:
$scope.parseTags = function() {
this.book.tags = this.book.tags.split(',');
};
Demo: http://plnkr.co/edit/DUkDVOMjjj0khh5KCYo7?p=preview
you should only use ng-init for special cases when you need to use a property of the ng-repeat, I think you will better doing this kind of functionality in the controller. Unless there is a really specific reason you can't do that. I haven't seen your use of tags in the html, but looks like the kind of functionality a filter would do.
From angularjs docs:
The only appropriate use of ngInit is for aliasing special properties
of ngRepeat, as seen in the demo below. Besides this case, you should
use controllers rather than ngInit to initialize values on a scope.
source: angularjs docs

using javascript inline in angularjs

I have an array in my controller containing date-objects called $scope.events. I would like to iterate these events and and print them out in a certain format, which I use momentjs for.
Now the thing is, I cannot get it to actually use momentjs.
I have tried the following:
<table>
<tr ng-repeat="ev in events">
<td>{{ moment(ev).format("HH") }}</td>
</tr>
</table>
but this just prints an empty cell.
So my question is, how do I use javascript, momentjs, inline in my angular-binding ?
thanks
Thomas
Assuming moment is a property of window, you'll need to create a reference in this object's $scope that references moment.
Very simply:
$scope.moment = window.moment;
Here's a plunkr showing internal $scope methods vs. a $scope property referencing a method on window:
http://embed.plnkr.co/PWFK80/preview
That's the simple answer, but you'd likely want to wrap this library into its own directive or service, so that you could use it without coupling higher-level objects to the window object unnecessarily.
Wrap the code in a function in your controller for a quick answer.
controller('Ctrl', function($scope) {
$scope.moment = moment;
});
I edited this to match a comment by #Stewie because it looks better
and in your html:
<td>{{moment(ev, 'HH'}}</td>
Or make a service/directive for moment for something better.

Categories

Resources