I made this dropdown with a textarea for input in angular material dialog. There I have only three options in the dropdown(so far)-'English','French' and 'Canadian French'. I have already disabled 'English' by default. Now, for the rest of the remaining options, when I click 'Add new Language' button and select an option(french ; say) and add text I am able to disable the selected option so that when the user adds the third language they cannot select it again. It works fine. Like this (This is without hitting the save button)
Now the problem starts here. When I select 'French'(say) and hit the save button. And again open the dialog-box. I am again seeing 'French' along with 'Canadian French' in the options for the adding a third language. What do I do to make it inactive and grey-ish like 'English'?
This the ts code:
export class ModalAllComponent implements OnInit {
dialogData: DialogDataModel;
languages: any[];
rows: any[];
item!:any[];
constructor(
public dialogRef:MatDialogRef<ModalAllComponent>,
@Inject(MAT_DIALOG_DATA) public data: DialogDataModel) {
this.dialogData = data;
this.rows = this.dialogData.localisedEntities.filter(lang => lang.value,)
this.languages = this.dialogData.localisedEntities.map(item => ({ code: item.code, title: item.title, canEdit: item.canEdit }))
console.log(this.dialogData)
}
ngOnInit(): void {
}
addNewLanguage() {
this.rows.push({
code: '',
title: '',
value: '',
canEdit: true
});
}
onChangeValue(ev: any){
this.rows = this.rows.map(row => {
if (row.code == ev.value) {
const lang = this.languages.find(lang => lang.code == ev.value);
row.title =lang.title;
}
return row;
})
console.log(this.rows)
this.languages = this.languages.map(lang => {
if (lang.code == ev.value) {
lang.canEdit = false;
console.log(lang);
}
return lang;
});
this.isDisabled()
}
isDisabled(){
return this.rows.filter((item) => item.value == '' || item.code == '')
.length > 0
? true
: false;
}
submit(ev:any){
this.dialogRef.close({data: this.rows});
}
back(){
this.dialogRef.close()
}
removeBtn(index:number){
this.rows.splice(index, 1);
}
}
I was console-logging at a lot of places and finally manage to draw down to these place where the problem might be happening.
In console.log(lang)
, I saw that, when I selected 'French',the flag canEdit turned to false.But in console.log(this.rows)
, when I selected 'French',the flag canEdit did not turned to false
How to solve the issue?
The HTML code:
<div>
<table class="justify-content-between">
<tr *ngFor="let entity of rows; let i = index">
<td class="col-1" *ngIf="entity.value!=null">
<mat-select [(ngModel)]="entity.code" [disabled]="!entity.canEdit" (selectionChange)="onChangeValue($event)">
<mat-option *ngFor="let lang of languages" [disabled]="!lang.canEdit" [value]="lang.code">{{ lang.title }}</mat-option>
</mat-select>
<!-- <mat-error *ngIf="entity.code.hasError('required')">Please choose an language</mat-error> -->
</td>
<td class="col-1" *ngIf="entity.value!=null">
<textarea style="height: 2rem" class="pl-5" [disabled]="!entity.canEdit" [(ngModel)]="entity.value">{{ entity.value }}</textarea>
<mat-icon class="pl-2" style="color: red;font-size: 2rem;cursor: pointer;" (click)="removeBtn(i)">close</mat-icon>
</td>
</tr>
</table>
<div class="d-flex flex-column align-items-center mt-2">
<button class="form-control" (click)="addNewLanguage()" *ngIf="rows.length < dialogData.localisedEntities.length" [disabled]="isDisabled()">Add new language</button>
<div class="d-flex pt-2">
<button class="form-control" [disabled]="isDisabled()" (click)="back()">Discard</button>
<button class="form-control ml-4 pl-4 pr-4" [disabled]="isDisabled()" (click)="submit($event)">Save</button>
</div>
</div>
</div>
This is where the modal is opening:
localiseFoodName() {
const dialogData = < DialogDataModel > {
localisedEntities: this.foodModel.localisedName.map((item: any) => {
if (item.code == 'en') {
item.canEdit = false;
} else {
item.canEdit = true;
}
return item;
}),
};
let dialogRef = this.dialog.open(ModalAllComponent, { width: '26.5rem', data: dialogData });
dialogRef.afterClosed().subscribe(res => {
if (res && res.data) {
console.log(res)
console.log(res.data)
let temp:any
this.foodModel.localisedName.map((item:any)=>{
temp = res.data.find((element:any)=> element.code === item.code);
if(temp){
item.value = temp.value
item.canEdit = temp.canEdit = false
}
//console.log(temp)
})
const food = this.foodModel.localisedName
console.log(food)
}
})
}
When I console.log(food)
I could see the canEdit:false
. But when I open the modal again, in console.log(this.dialogData)
the canEdit
is again "True" for the selected item
Thanks in advance for the help!
Before diving in...
Because, like you said, it's a big project, I feel like it might be hard to diagnose, but I'll point out what I think could be the culprit based on things I've seen before, and I'll update this answer if more details/context come(s) to light.
In the meantime, please check this out: https://stackoverflow.com/help/minimal-reproducible-example.
Look, I know, it's all obvious stuff, I don't mean to insult, but us programmers will often forget these things when we're zeroed-in on a problem and a reminder is often helpful. Bottom line, a minimal reproducible example goes miles for helping the answerers and the question-asker in turn. Whatever progress you can make on that front is deeply appreciated!
What might be going on
This problem feels much better suited to reactive forms than template-driven forms, but you can use template forms. You'd want some logic in the setter that's attached to your 2-way
ngModel
binding. That's where you'll need to update thecanEdit
status.Also I'm suspicious of your subscription. Depending what
dialogRef.afterClosed()
returns (in terms of how many emissions, what its source is, etc), you might have a lingering subscription problem here. The subscription does not die with the component. And even if it doesn't cause a logic bug, it's going to bloat your memory, slowing down your app.And in general, I'd recommend finding a way to not be subscribing explicitly, but seeing if you can instead leverage the
async
pipe and just push any changes via asubject
that you.next
at the specific point in your event-listening code (i.e. yourngModel
-bound setter). The other benefit is thatasync
pipe handles subscription for you, but it also will unsubscribe when the component dies.I'll come back to update if this doesn't solve your problem and if you are able to provide more context. Sorry, it's just a lot of guesswork right now. More context would be really helpful!