Cannot use child input value in parent's [ngClass] logic - javascript

I am attempting to pass the user input value from my input through to the parent and use this value to determine which row to highlight within my <li *ngFor... element.
I can actually pass the value through successfully so it seems, as I have set up a console.log to catch what is first emitted by the child component and then also what is caught by the parent, but I just cannot get it to be seen by my [ngClass] logic section...
The logic does work correctly if I hardcode the value of the row I wish to highlight in, but of course, I want to do this programatically.
parent.component.ts
rowId: number;
childToParent(val){
console.log('received from child: ' + val);
this.rowId = val;
}
parent.component.html
<app-child (childToParent)="childToParent($event)"></app-child>
<li *ngFor="let item of items" attr.id="item-{{item.id}}" [ngClass]="{'active': item.id === rowId}">
<div class="item">
...
</li>
child.component.ts
#Output() childToParent = new EventEmitter<String>();
sendToParent(id){
console.log('sending to parent: ' + id)
this.childToParent.emit(id);
}
child.component.html
<input [(ngModel)]="val" (input)="sendToParent(val)"/>
Stackblitz

are you sure item.id and rowId both are number type? you can change "===" into "==" and it will work for both string and number.

Maybe just parseInt val
this.rowId = parseInt(val, 10);

Related

How to do pop operation in javascript when unchecked checkbox?

I have just the two checkbox whose codes looks like:
<div *ngFor="let item of documents">
<label>
<input type="checkbox" value="{{item.docId}}" [(ngModel)]="item.checked" [name]="item.docName"
(change)="editPartyRolesSubmit($event)"
/>
<span innerHTML="{{item.docName}}"></span>
</label>
</div>
Here i have only used two checkbox as:
The function editPartyRolesSubmit($event) called is:
public documents: Array<Document> = []
public onlyTruedocuments: Array<Document> = [];
editPartyRolesSubmit(event) {
this.documents.filter(x => x.checked).map(x => {
console.log("entered in check");
this.onlyTruedocuments.push(x);
console.log(x);
})
}
The Json data is pushed three times as it should be only pushed two times though i have just clicked two times.
But when i print them in the .html page then though the checkbox is two,It is printed three times :
<li *ngFor="let ot of onlyTruedocuments; index as i">
{{ot.docName}}
</li>
It is printing like this:
How can i remove this redundant data?
If I understand correctly, I would do in this way,
I have used (ngModelChange) instead of (change)
I have passed the current item to the ngModelChange function.
HTML:
<div *ngFor="let item of documents">
<label>
<input type="checkbox" value="{{item.docId}}" [(ngModel)]="item.checked" [name]="item.docName"
(ngModelChange)="editPartyRolesSubmit($event,item)"/> // will pass an item to the function
<span innerHTML="{{item.docName}}"></span>
</label>
</div>
<li *ngFor="let ot of onlyTruedocuments; index as i">
{{ot.docName}}
</li>
TS file:
export class YourComponent {
documents = [{
docId: 1,
checked: false,
docName: 'Prashant'
},
{
docId: 2,
checked: false,
docName: 'Venkat'
}
, {
docId: 2,
checked: false,
docName: 'Perry'
}];
public onlyTruedocuments: any = [];
editPartyRolesSubmit(event, obj) {
// Take the index of an Item checked
let index = this.onlyTruedocuments.indexOf(obj);
// Check for event i.e it is checked or unchecked
if (event) {
if (index == -1) {
// If the index is -1 then that means its not a duplicate so push into an array
this.onlyTruedocuments.push(obj);
}
}
else {
// If it is unchecked then we surely know that the item has to be removed from the array so by an index of the particular item we can [splice][1] the item
this.onlyTruedocuments.splice(index, 1)
}
}
}
No need to filter the source array to get the checked items.
A Working StackBlitz Example with Sample data.
Have a unique check before you push to onlyTruedocuments. This way even when user click many times the object would still have unique values as expected.
editPartyRolesSubmit(event) {
this.documents.filter(x => x.checked).map(x => {
console.log("entered in check");
const exists = this.onlyTruedocuments.filter((f)=>f.docId == x.docId);
if(exists.length==0){
this.onlyTruedocuments.push(x);
}
console.log(x);
})
}
One way to collect unique elements only is to define this.onlyTruedocuments not as an array, but as a Set, initialised as:
this.onlyTrueDocuments = new Set();
Then in the event handler do:
editPartyRolesSubmit(event) {
this.documents.forEach(x => x.checked && this.onlyTruedocuments.add(x));
console.log([...this.onlyTrueDocuments]); // Use spread syntax to get converson to array
}

accessing *ngFor last in its component in angular 6

In angular 6 I want to access *ngFor last value as I want to operation if last value is set
eg
<li [ngClass]="list.mydata==1?'replies a':'sent a'" *ngFor="let list of chatlist; let last=last;">
<span [last]="last"></span>
<img src="{{list.profile_img}}" alt="" />
<div *ngIf="list.sender_type==0">
<p>{{list.message}}{{last}}</p>
</div>
<div *ngIf="list.sender_type==1">
<p style="background-color: burlywood;">{{list.message}}</p>
</div>
</li>
I want to do is [(myvar)]=last in place of let last=last
I want to bind the last variable so, I can access it is set or not in its component.
you can create a custom directive:
import { Directive, Output, EventEmitter, Input } from '#angular/core';
#Directive({
selector: '[onCreate]'
})
export class OnCreate {
#Output() onCreate: EventEmitter<any> = new EventEmitter<any>();
constructor() {}
ngOnInit() {
this.onCreate.emit('dummy');
}
}
and then you can use it in your *ngFor to call the method in your component:
<li [ngClass]="list.mydata==1?'replies a':'sent a'" *ngFor="let list of chatlist; let last=last;">
<span (onCreate)="onCreate(last)"></span>
<img src="{{list.profile_img}}" alt="" />
<div *ngIf="list.sender_type==0">
<p>{{list.message}}{{last}}</p>
</div>
<div *ngIf="list.sender_type==1">
<p style="background-color: burlywood;">{{list.message}}</p>
</div>
</li>
then in your component:
myvar: boolean = false;
onCreate(last) {
this.myvar = last;
}
checkout this DEMO.
Angular provides certain local variables when using *ngFor, one is for example last, which will (not as you expect currently) be a boolean value, being true if it is the last item. This is meant for adding specific stylings for the example to the last element of a list.
If you want that boolean you already use it correctly, but obviously the element using it should be a component. So instead of
<span [last]="last"></span>
it should be something like
<my-component [last]="last"></my-component>
where in my-component you define
#Input last: boolean;
and thus have access to it.
for example
<li [ngClass]="list.mydata==1?'replies a':'sent a'" *ngFor="let list of chatlist; let index=i;">
</li>
now you excess last elemnt like
<anyTag *ngIf="i === chatlist.length-1"></anyTag>

Hide other elements in list

I have the below code:
<li *ngFor="let item of Array let i = index">
<span>
<label (dblclick)="editTag($event,i)">
{{item.tag}}
</label>
<input type="text" #tagInput />
</span>
</li>
The code is in a for loop. When I click on a label, all labels should be hidden and the input should be visible. Currently, when I click on each label, the other remain open. How do I hide the other span when clicking on any item?
I have below code in .ts
#ViewChild('tagInput') tagNameTextInput: ElementRef;
editTag(event: any,index: any) {
//console.info(event);
this.tagNameTextInput.nativeElement.hidden = true;
this.tagNameTextInput.nativeElement.previousElementSibling.hidden = false;
let initialValue = event.target.childNodes[0].nodeValue.trim();
event.target.hidden = true;
event.target.nextElementSibling.hidden = false;
event.target.nextElementSibling.value = initialValue;
console.log(index);
// this.checkListNameHidden = true;
// this.checkListNameTextInput.nativeElement.value = initialValue;
// this.checkListNameTextInput.nativeElement.focus();
event.stopPropagation();
}
How to solve this?
You have multiple children, So you need to use #ViewChildren instead of #ViewChild.
Also in your ngFor loop you do not have unique template reference #tagInput. Use QueryList with ElementRef.
Try : #ViewChildren('tagInput') tagNameTextInput: QueryList<ElementRef>;
instead of
#ViewChild('tagInput') tagNameTextInput: ElementRef;.
Import QueryList from #angular/core.
Like this import { Component, QueryList } from '#angular/core';
the best aproach is add a new property to "item", (e.g. called "editing") so
<li *ngFor="let item of Array let i = index">
<span>
<label *ngIf="!item.editing" (dblclick)="item.editing=true;">
{{item.tag}}
</label>
<input *ngIf="item.editing" [(ngModel)]="item.tag" type="text" (blur)="item.editing=false" />
</span>
</li>
See several things:
1.-in a click of label, the variable becomes true, so the inpĆ¹t is showed
2.-in blur of item, the variable becomes false, so the label is showed
3.-Use [(ngModel)] to relation between the input and the value

Angular 2 input value not updated after reset

I have a simple input that I want to reset the value to empty string after I am adding hero. The problem is the value is not updated. why?
#Component({
selector: 'my-app',
template: `
<input type="text" [value]="name" #heroname />
<button (click)="addHero(heroname.value)">Add Hero!</button>
<ul>
<li *ngFor="let hero of heroes">
{{ hero.name }}
</li>
</ul>
`,
})
export class App {
name: string = '';
heroes = [];
addHero(name: string) {
this.heroes.push({name});
// After this code runs I expected the input to be empty
this.name = '';
}
}
You have one-way binding so when you're typing something in your input your name property isn't changed. It remains "". After clicking on Add hero! button you doesn't change it.
addHero(name: string) {
this.heroes.push({name}); // this.name at this line equals ''
this.name = ''; // it doesn't do any effect
}
Angular2 will update value property only if it is changed.
Use two-way binding which is provided by #angular/forms
[(ngModel)]="name"
to ensure that your name property will be changed after typing.
Another way is manually implementing changing
[value]="name" (change)="name = $event.target.value"
In Angular Template binding works with properties and events, not attributes. as per html attribute vs dom property documentation of angular so as you have used [value] binding its binding to attributes not to the property of that input and because of it value remain in it after you set this.name = "".

Bind click and change checkbox angular2

I have a strange problem with checkboxes using angular2 (beta 17).
I'm trying to set same checkboxes as elements has the array and bind the change or click event.
For example I have an array:
myObjects=[
{value:true},
{value:true},
{value:false},
{value:true}
];
And I'm using the ngFor to create all the checkboxes:
<div *ngFor="let myObject of myObjects; let i = index" >
<input
type="checkbox"
[checked]="myObject.value"
(change)="updateCheckedOptions(i, $event)"
/>
</div>
And finally I have a function in my component:
#Input() myObjects;
updateCheckedOptions(index,event){
this.myObjects[index].value=event.target.checked;
}
But the problem is that the checkbox don't change, I mean that when I click the checkbox, if the initial state is checked, always remain checked and vice versa.
I have tried using (click), (change), [(ngModel)] but it does not work any.
Thanks!
You can check ngModelChange:
//This is your component
#Input() myObjects;
myObjects=[
{value:true},
{value:true},
{value:false},
{value:true}
];
updateCheckedOptions(index,event){
this.myObjects[index].value=event.target.checked;
console.log(index + " ---- " + event);
}
<div *ngFor="let myObject of myObjects; let i = index" >
<input
type="checkbox"
[checked]="myObject.value"
(ngModelChange)="updateCheckedOptions(i, $event)"
/>
</div>

Categories

Resources