Create new instace of object every time? - html

I have this in my HTML:
<div class="ui-g" *ngFor="let orderTracking of orderTrackings, let i=index" (click)="selectItemForRequest(i)" [ngClass]="{'active' : selectedOrderTracking===i }">
<technical-destinations *ngIf="orderTrackings.length" class="technical_destinations {{i}}" [orderTracking]="orderTracking" [groupDestinations]="groupDestinations"></technical-destinations>
</div>
In technical destinations I have dropdown, but when i change one every of them changes. Problem is that in orderTracking all object are same. Its just copy.
Any suggestion for how can I fix that?

I tried to reproduce your issue by iterating over a component with #Inputs.
So you said the objects in the collection you're iterating over are the same, so let's take something like:
items = [
{id:1, name: 'test'},
{id:1, name: 'test'},
{id:1, name: 'test'}
];
If you're looping over the above array, Angular is designed to create new instances of the repeated component for you:
<div *ngFor="let item of items">
<app-object [item]="item"></app-object>
</div>
Working example here: https://stackblitz.com/edit/angular-w8s6qt
If I missed something just tell me.

Related

Access an Array inside an Object with square brackets inside a ngFor

Basically, I've created a HTML that similar to the example :
<div *ngFor="let formElements of formTemplate">
<div *ngFor="let opt of staticEnumVals[formElements.testId]">
<!-- do smth here -->
</div>
</div>
Basically formTemplate is an Array with Objects, each Object has a property called "testId".
staticEnumVals is an Object thats build like that
{
"testId": [ {},{},{} ],
"testId2" [ {},{},{} ],
}
The keys "testId" and "testId2" are actual keys that match the Keys from formTemplate[i].testId.
Basically I want to iterate through an array out of my staticEnumVals and the array is selected dynamically based on the id from the first *ngFor
Basically im looking for an elegant way to achieve my second iteration, square brackets doesnt work.
I think my problem is clear enough, im sorry for the weird title.
Thanks in advance
After adding types to the staticEnumVals this worked for me.
#Component({
selector: 'test',
template: `
<div *ngFor="let formElements of formTemplate">
<div *ngFor="let opt of staticEnumVals[formElements.testId]">
{{opt.name}}
</div>
</div>
`
})
export class TestComponent {
private formTemplate: { testId: string }[] = [
{testId: '3'}, {testId: '2'}, {testId: '3'}
];
private staticEnumVals: { [id: string]: [any] } = {
'1': [{name: 'id1'}],
'2': [{name: 'id2'}],
'3': [{name: 'id3'}],
};
}

Angular: Show list in view with different object keys

I have the following JSON object:
header: {
first: {
title: {
name: "Test
}
},
second: {
title: {
name: "Test 2"
},
desc: {
name: "Description"
}
}
}
Now I want to show it in view like that:
How can I manage to do that in the view? Since the keys will vary every time. For example once you have header, another time main and so on... Something like JSONEditor, where you can edit JSON objects. But I want to create something like that with my own design. I tried with ngIf but it seems really difficult. I'd be really thankful for any suggestions or help.
You can use the KeyValuePipe to iterate over objects using ngFor.
<!-- app-your-node component -->
<div *ngFor="let item of object | keyvalue">
{{ title }}
<app-your-leaf *ngIf="yourLogicThatThisIsALeaf(item); else node"
[title]="item.key" [object]="item.value"><app-your-leaf>
<ng-template #node>
<app-your-node [title]="item.key" [object]="item.value"></app-your-node>
</ng-template>
</div>

Angular 6: How to build a simple multiple checkbox to be checked/unchecked by the user?

I am writing this post after having read several threads concerning this topic but no one of them gives me what I need. This post seems to have the solution but I do not have to read the checked values from the json.
All I need is to:
read countries from an array of objects
build dinamically a list of checkbox representing each country
user should check and uncheck each checkbox
bonus:
get the value of the checked input and send it outside the component
I know It might be really dumb to do but all I have accomplished untile now is to have a list of uncheckable checkboxes and nothing more.
Here is the code:
Template:
<div class="form-group">
<div *ngFor="let country of countries">
<input type="checkbox"
name="countries"
value="{{country.id}}"
[(ngModel)]="country"/>
<label>{{country.name}}</label>
</div>
</div>
And TS:
countries = [
{id: 1, name: 'Italia'},
{id: 2, name: 'Brasile'},
{id: 3, name: 'Florida'},
{id: 4, name: 'Spagna'},
{id: 5, name: 'Santo Domingo'},
]
I tried to use the reactive forms but that gave me more issues then template driven (surely because of bad implementation of mine).
Please, help me, I do not know where to bump my head anymore
Here is a working example, where you can observe that an additional 'checked' value is added to each country, and bound to the value of each checkbox with [(ngModel)].
Stackblitz live example
template:
<p>
Test checkboxes
</p>
<div *ngFor="let country of countries; let i = index;">
<input type="checkbox" name="country{{country.id}}" [(ngModel)]="countries[i].checked">
<label for="country{{country.id}}">{{country.name}}</label>
</div>
<button type="button" (click)="sendCheckedCountries()" *ngIf="countries">Click to send the selected countries (see your javascript console)</button>
<p *ngIf="!countries">loading countries, please wait a second...</p>
<p *ngIf="countries">Debug info : live value of the 'countries' array:</p>
<pre>{{ countries | json }}</pre>
component :
//...
export class AppComponent implements OnInit {
public countries: Country[];
constructor(private countryService: CountryService) {}
public ngOnInit(): void {
// loading of countries, simulate some delay
setTimeout(() => {
this.countries = this.countryService.getCountries();
}, 1000);
}
// this function does the job of sending the selected countried out the component
public sendCheckedCountries(): void {
const selectedCountries = this.countries.filter( (country) => country.checked );
// you could use an EventEmitter and emit the selected values here, or send them to another API with some service
console.log (selectedCountries);
}
}
To use some proper TypeScript, I made an interface Country :
interface Country {
id: number;
name: string;
checked?: boolean;
}
I hope you get the idea now.
Note : the checked value is not "automatically there" at the beginning, but it doesn't matter.
When not there, it is the same as undefined, and this will be treated as false both in the checkbox and in the function that reads which country is checked.
For the "sending value" part :
The button will output the selected value to the browser's console, with some filter similar to what #Eliseo's answer suggests (I just used full country objects instead of ids)
For "real usecase" situation, you could use Angular's EventEmitters and have your component "emit" the value to a parent component, or call some service function that will make a POST request of your values to another API.
Your countries like
{id: 1, name: 'Italia',checked:false},
Your html like
<div *ngFor="let country of countries">
<input type="checkbox" [(ngModel)]="country.checked"/>
<label>{{country.name}}</label>
</div>
You'll get an array like, e.g.
[{id: 1, name: 'Italia',checked:false},{id: 2, name: 'Brasile',checked:tue}..]
you can do
result=this.countries.filter(x=>x.checked).map(x=>x.id)
//result becomes [2,...]
I had an error using [(ngModel)]
In case it serves anyone, I have solved the problem changing
[(ngModel)]
to:
[checked]="countries[i].checked" (change)="countries[i].checked= !countries[i].checked"

Working with custom components for input generates "No value accessor for form control with path X->0->Y"

I have a working form taking the following HTML markup. No errors or warnings.
<div class="input-element">
<div class="input-caption">Title</div>
<input type="text"
formControlName="targetField"
class="form-control">
</div>
I transformed it into a custom component, which also works, as shown below.
<app-input-text [info]="'Title'"
formControlName="targetField"
ngDefaultControl></app-input-text>
In my next view, I need to use FormArray as follows - still working code.
<div formArrayName="stuff">
<div *ngFor="let thing of form.controls.stuff.controls; let i = index;"
[formGroupName]=i>
<div class="input-element">
<div class="input-caption">Title</div>
<input type="text"
formControlName="targetField"
class="form-control">
</div>
</div>
</div>
Now, I expected that combining both (i.e. being able to use custom input component and being able to form array for components) would post no problem. However, the sample below doesn't work.
<div formArrayName="stuff">
<div *ngFor="let thing of form.controls.stuff.controls; let i = index;"
[formGroupName]=i>
<app-input-text [info]="'Title'"
formControlName="targetField"
class="col-sm-6"></app-input-text>
</div>
</div>
It generates the following error.
No value accessor for form control with path: 'stuff -> 0 -> targetField'
The custom component is design like this (although given that it works in the explicit markup example, I'm not sure if it's relevant information). The only (wild) guess I have might be that value isn't jacked into the form array field somehow.
export class InputTextComponent implements OnInit {
constructor() { this.value = new EventEmitter<string>(); }
#Input() info: string;
#Output() value: EventEmitter<string>;
onEdit(value: any): void { this.value.emit(value); }
}
The group and array creating in the current view is done like this (not sure if this is of any relevance neither, as it works for the explicit HTML markup case).
this.form = builder.group({
id: "",
stuff: builder.array([
builder.group({ targetField: "aaa" }),
builder.group({ targetField: "bbbb" }),
builder.group({ targetField: "cc" })
])
});
Is there a limitation in Angular in this regard that I'm not aware of? I'm rather sure there's not and that I'm just doing something fairly clever simply missing a tiny detail.
I do understand the error but I can't see how it relates to the code. The form can't find the 0th element in the array or that element has no field of that name. Since I do get to see a few rows, I know there must be a 0th element. Since I specified the name of the field, I know there is indeed such. What else am I missing?

Accessing Object properties, from HTML Select.

Scenario:
I am trying to access a property (code) of a ng-Model object (myRide).
I've tried doing this by
<select ng-model = "myRide"
ng-change = "getCode(myRide.code)">
...and at getCode,
alert(parameter) //which should be myRide.code.
I've also tried to do this by
<select ng-model = "myRide"
ng-change = getCode(myRide)
(Note: 'myRide' is passed, not 'myRide.code') ...and at getCode,
alert(myRide.code).
myRide does indeed contain a property called 'code', which is not undefined.
Problem: Both tries do not produce the wanted outcome.
How can I make it display the property (code)?
Here is the JSFiddle: http://jsfiddle.net/a2J6z/1/
The better way to do this is to restructure the view. Instead of using ng-repeat on options inside of a select, use the ng-options directive. Then, you can bind the actual object to the model instead of just a JSON string, which is what your current fiddle is doing.
Your new select looks like
<select ng-options="car.Name for car in myGarage" ng-model="myRide" ng-change="getCode(myRide)">
</select>
Then in your controller
$scope.getCode = function(car){
alert(car.code);
}
An updated fiddle:
http://jsfiddle.net/a2J6z/5/
I updated the fiddle:
http://jsfiddle.net/a2J6z/3/
var ngApp = angular.module('ngAppl',[]);
function aControlla($scope){
$scope.myRide = "bus";
$scope.getCode = function(car){
alert(JSON.parse(car).code);
}
$scope.myGarage = [
{Name: "Toyota 86", code:"1"},
{Name: "Hyundai Genesis Coupe", code:"2"},
{Name: "Nissan GTR", code:"3"},
{Name: "Veyron", code:"4"}
];
};
And
<div ng-app="ngAppl">
<div ng-controller="aControlla">
<select ng-model="myRide" ng-change="getCode(myRide)">
<option ng-repeat="car in myGarage">{{car}}</option>
<!--Note: value of myRide MUST be simply 'car' (not say, 'car.Code')-->
</select>
<br/> This shows that 'myRide' contains the property called 'Name', and importantly, 'code':<br/> {{myRide}}
</div>
</div>
Basically I just had it alert what car was with myRide as the parameter, it showed the JSON string so I added the parse to get it to give me the code. There may be a better way I'm an AngularJS noob so to speak.