I am new to angular , can someone tell me if I can use variable inside variable in angular.
Explaination:
I am creating one dropdown input component where it will make api call to get data.
There is an #Input() selector:string = "" which will tell what to select from data
Inside template it will run *ngFor loop, then inside html I want to display as kind of like that:
<option *ngFor="let item of data" [value]="item.id">
{{ item.{{selector}} }}
</option>
In other module it will be used as:
In one module <app-input [selector]="'name'"></app-input>
Another one. <app-input [selector]="'id'"></app-input>
How Can I use selector inside this any way?
{{ item[selector] }}
use the bracket syntax for accessing a property with a variable key.
Related
I need to use a function expression in *ngFor like this,
<select>
<option *ngFor="let item of getItems(someValue)">
..........
</option>
</select>
Is there any issue in using a function like getItems(someValue) where I get the items based on the 'someValue'.
Is this approach ok or should I use some other way?
Its recommended that you dont use functions directly when binding to the UI, as these will be run every time the change detection lifecyle runs, possibly leading to poor performance.
*ngFor should be used only for binding with an array declared in your component. If this array changes over time, that's ok because Angular knowns when to redraw the view based on your variables.
You can do this in your component:
public items: YourObject[];
You can assign the values in the constructor, or any other place:
constructor(){
this.items = this.getItems(someValue);
}
Then just use it in the *ngFor like this: let item of items
In my first Angular 4 application, I defined a list component :
<edm-document *ngFor="let document of documents" class="column is-one-quarter"></edm-document>
Document is an interface :
export interface Document {
id?: Number,
name: string,
filePath: string
}
All is working as expected, i.e I get my documents list. But now I would like to access to document variable inside my DocumentComponent (the edm-document tag component)
Inside my DocumentComponent template, if I try this it's not working :
<p>{{ document.name }}</p>
I get this error : DocumentComponent.html:1 ERROR TypeError: Cannot read property 'name' of undefined.
I need to enforce document definition like this, and specify document as an input :
<edm-document *ngFor="let document of documents" [document]="document" class="column is-one-quarter"></edm-document>
Now it works but seems a bit redundant to me as I defined a let in loop. Does that mean the variable defined with let is only available in tag where ngFor directive is set ?
Am I missing something?
Thanks,
Nicolas
it works but seems a bit redundant to me as I defined a let in loop
It is not as redundant as it might seem, which becomes obvious when rewriting things a bit:
When not explicitly defining what the component should use (with [document]="document" in your example) then how would your component know that the parent variable is named document? Consider:
<edm-document *ngFor="let d of documents" [document]="d"></edm-document>
One could argue that Angular could introduce some parent variable to access the outer loop variable, but then the component would know how it's going to be used, and could only be used in a loop. Reusable components should not be aware of that.
How would it know that it can use that loop variable directly, and does not need some child property instead? Like:
<edm-document *ngFor="let d of documents" [document]="d.text"></edm-document>
So: your code is just fine.
Initially during DOM rendering the documents object will undefined
Use a typesafe ? operator
<p>{{ document?.name }}</p>
Use a *ngIf with a array length condition as below,
<span *ngIf="documents.length > 0">
<edm-document *ngFor="let document of documents" [document]="document"
class="column is-one-quarter"></edm-document>
</span>
well you can also do something like this
<edm-document *ngFor="let document of documents" class="column is-one-quarter">
<span class="something">{{document.name}}</span>
</edm-document>
and in the edm-document.component.html do something like
<ng-content select=".something"></ng-content>
The value (document) of the loop is valid inside of that block where the *ngFor placed. In your case between: <edm-document>..</edm-document>
In your example:
<edm-document *ngFor="let document of documents"class="column is-one-quarter">
<p>{{ document.name }}</p> <!-- document.name is valid -->
</edm-document>
<p>{{ document.name }}</p> <!-- document.name is invalid -->
I have the following markup:
<p class="{{ UserMessageStyle }}" > Some Message </p>
And then in Angular Controller When I want to pass a specific class name to the
variable UserMessageStyle, I do the following:
$scope.UserMessageStyle = [];
if(condition_is_met)
{
$scope.UserMessageStyle.push("alert-danger");
}
But my problem is that, when once the above statement is run, then I cannot change it. It has a reason because I have declared UserMessageStyle as [] which mean on each instance of push() a new key&value pair will be added.
My Question is that, how should I use push() where I don't want my UserMessageStyle to be an object or an array. I want it to be a simple variable which is overriden on each instance call of push(). Something like this:
// the declaration of the variable as an object is removed.
if(condition_is_met)
{
$scope.UserMessageStyle.push("alert-danger");
}
But the above statement causes this error:
cannot read the property `push()` of undefined.
What should I do?
It sounds to me that you are looking for a normal variable instead of an array.
why dont you do something like this:
if(condition_is_met)
{
$scope.UserMessageStyle = "alert-danger";
}
First of all you do not need array here, class attribute understand space separated list
$scope.mystyles = "alert-danger alert-danger-bright"
and layout
<p class="{{ UserMessageStyle }}" > Some Message </p>
Also consider using ng-class directive for that
<p ng-class="UserMessageStyle" > Some Message </p>
I have a variable param in scope as $scope.param that is always either foo or bar. I'm creating a table with rows defined by <tr ng-repeat="d in data">.
When I have the following: <td>{{d.foo}}</td> or <td>{{d.bar}}</td> everything works and the data shows up fine. However, when I have <td>{{d.param}}</td> angular can't find anything and the cell is blank.
In other words, I'm trying to access an object value using a variable as the key rather than the key itself. Any idea how to do this?
Use bracket notation:
{{d[param]}}
This should be an extremely simple question, but all of the workarounds I've found are complex. I'm looping through an array of objects in using ng-repeat in a template as follows:
<div class="row-fluid" ng-repeat="message in messages.current|filter:'draft'">
{{ message.subject }} ... {{ campaign.name }} ...
</div>
Since the ng-repeat creates a new scope, the 'campaign' object from the controller doesn't seem to be accessable. Is there any way (aside from adding the campaign object to every item in my array) of getting that value?
Thanks in advance.
You can access the parent scope by using $parent
<div class="row-fluid" ng-repeat="message in messages.current|filter:'draft'">
{{ message.subject }} ... {{ $parent.campaign.name }} ...
</div>
This is a way that works that doesn't use $parent. It searches upwards through the nested scopes to find the object you're using, however many scopes it has to go through.
In the scope that contains the list, you can define an object with the list as a property, like this:
$scope.obj = {};
$scope.obj.items = ['item1','item2','item3'];
Then have the ng-repeat look like this:
<div ng-repeat="item in obj.items | filter:'item3' track by $index">
{{obj.items[ obj.items.indexOf(item) ]}}
</div>
(you need to use obj.items[ obj.items.indexOf(item) ] rather than obj.items[ $index ] because $index is the index of the filtered array, not the original)
The reason this works is because while obj doesn't exist in the current scope, as you attempt to access its property, Angular will look above the current scope rather than give you an error (if you just tried {{obj}} it would be undefined, and Angular would be happy with giving you nothing instead of looking through higher scopes). This is a helpful link about nested scopes: http://www.angularjshub.com/examples/basics/nestedcontrollers/
In my case I needed the track by $index, because I had an input with ng-model bound to an item in the array, and whenever the model updated, the input would blur because I think the HTML was being re-rendered. A consequence of using track by $index is that items in the array with identical values will be repeated. If you modify one of those other than the 1st one, weird things will happen. Maybe you can filter for uniqueness to avoid that.
I'm relatively new to AngularJS, so please comment if there is anything big I'm missing. But this works, so I'm using it at least.
Another method might be to pass parent scope as a scope variable to the directive i.e.
<my-directive
md-parent-scope="this"
ng-repeat="item in items"></my-directive>
It's a bit messy, but you have more control over what the parent actually is and can pass anything in.