How can I stop input text from writing in 2 inputs? - html

I have a problem in input text, when I write in one input it changes in the other input as well ! how can I fix that
here is it my code:
<mat-expansion-panel class="childPanel" [expanded]="true" hideToggle disabled style=" background:#F2EDFF;" *ngFor="let i of listSousCategorie" >
<div class="childPanel_body2 mat-badge-large" fxLayout="row" fxFlex fxLayoutAlign="start center" >
<div style="padding-left:1em" fxFlex="grow" >{{i.libelle}} {{i.id}}</div>
<mat-form-field>
<form [formGroup]="form" (ngSubmit)="onSubmit(i.id)">
<mat-label>Note</mat-label>
<input type="text" matInput formControlName="Note" [(ngModel)]="Note" >
</form>
</mat-form-field>
</div>
</mat-expansion-panel>
TS File:
Note:string;
ngOnInit() {
this.form = this.formBuilder.group({
Note: [''],
});
}
onSubmit(id:number) {
let obj = this.form.value;
obj.Note = this.Note;
console.log(this.Note);
this.LExamenSousCategorieService
.updateSouCategorie(obj,id)
.then(res => {});
}

<mat-expansion-panel class="childPanel" [expanded]="true" hideToggle disabled style=" background:#F2EDFF;" *ngFor="let i of listSousCategorie" >
<div class="childPanel_body2 mat-badge-large" fxLayout="row" fxFlex fxLayoutAlign="start center" >
<div style="padding-left:1em" fxFlex="grow" >{{i.libelle}} {{i.id}}</div>
<mat-form-field>
<mat-label>Note</mat-label>
<input type="text" matInput [(ngModel)]="i.Note">
</mat-form-field>
<button type="button" (click)="onSubmit(i.id)">
</div>
</mat-expansion-panel>
ts
// All commented lines are not needed
// Note:string;
// ngOnInit() {
// this.form = this.formBuilder.group({
// Note: [''],
// });
// }
onSubmit(id:number) {
// let obj = this.form.value;
// obj.Note = this.Note;
console.log(this.listSousCategorie);
this.LExamenSousCategorieService
.updateSouCategorie(i.Note,id)
.then(res => {});
}
This is still not the best solution for you, but solves your problem. A better solution would require an extend refactoring.

Related

Angular Load new Autocomplete values on each select change event

I have several most important fields in my form. In first form, user should choose the company by mat-select-search. After that, by using (selectionChange) I'm calling API with selected value in the first field( this is company id) and return users from that company to autocomplete for recipient fields. It works perfectly, if user does not change his chosen company. But if he do that (selectionChange) method doesn't reload autocomplete with new value. There is a situation in which the user has another company selected, and autocomplete remains from the previous one. Event only loads new one if the previous output was empty.
Is it possible to change this behavior? May be it,s possible to delete previous results from api calling?
My form :
<form [formGroup]="orderForm" novalidate (ngSubmit)="createOrder()">
<div class="row">
<div class="col-md-12">
<mat-form-field class="example-full-width" formGroupName='Company'>
<mat-select ngDefaultControl placeholder="Company" #singleSelect formControlName="id" (openedChange)="onSelectionChange($event)" [(value)]="selectedVariable">
<mat-option>
<ngx-mat-select-search [formControl]="companySelectFilter" [searching]="searching" placeholderLabel="Find company..." noEntriesFoundLabel="'no matching companies found'" ></ngx-mat-select-search>
</mat-option>
<mat-option *ngFor="let company of filteredCompanies | async" [value]="company.id">
{{company.company_name| titlecase}} {{company.company_address| titlecase}}
</mat-option>
</mat-select>
<mat-error *ngIf="orderForm.hasError('notSame')">
Recipient has another company! Select again.
</mat-error>
</mat-form-field>
</div>
</div>
<div class="row" matAutocompleteOrigin #origin="matAutocompleteOrigin">
<div class="col-md-6">
<mat-form-field class="example-full-width" formGroupName='recipient' >
<input type="text"
matInput
formControlName="user_name"
placeholder="First Name"
[matAutocomplete]="auto"
[matAutocompleteConnectedTo]="origin">
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="setFormData($event)">
<mat-option *ngFor="let recipient of filteredOptions | async" [value]="recipient">
<div style="display:flex;flex-wrap: nowrap;align-items:center;justify-content:center;margin: auto;">
<span style="flex-grow: 1;flex: 1 1 33%;"> {{recipient.user_name.toString() | titlecase}} {{recipient.user_surname.toString() | titlecase}} {{recipient.company.company_name.toString() | titlecase}}
</span>
</div>
</mat-option>
</mat-autocomplete>
</mat-form-field>
</div>
<div class="col-md-6" >
<mat-form-field class="example-full-width" formGroupName='recipient'>
<input matInput formControlName="user_surname" placeholder="Last Name"[matAutocomplete]="auto"
[matAutocompleteConnectedTo]="origin" >
<mat-error *ngIf="orderForm.get('recipient.user_surname').hasError('required')">
Last Name is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
</div>
<div class="col-md-6" >
<mat-form-field class="example-full-width" formGroupName='recipient'>
<input matInput placeholder="Mobile Phone" type="text" formControlName="user_telephone" >
</mat-form-field>
</div>
OnSelected change method:
onSelectionChange(opened: boolean) {
console.log(`opened is : ${opened}`);
if (!opened && this.selectedVariable) {
this.orderForm.get('recipient.user_name').reset()
this.orderForm.get('recipient.user_surname').reset()
this.orderForm.get('recipient.user_telephone').reset()
this.filteredOptions = this.orderForm
.get('recipient.user_name')
.valueChanges.pipe(
startWith(""),
debounceTime(400),
distinctUntilChanged(),
switchMap((val) => {
return this.doFilter(this.selectedVariable, val || '');
})
);
}
}
And doFilter method :
doFilter(id: number, val: any): Observable<any[]> {
return this.recipientService.getRecipientsByCompany(id).pipe(
map((response) =>
response.filter((option) => {
return (
option.user_name
.toString()
.toLowerCase()
.indexOf(val.toString().toLowerCase()) === 0 ||
option.user_surname
.toString()
.toLowerCase()
.indexOf(val.toString().toLowerCase()) === 0
);
})
)
);
}
Finally, I found a solution. My mistake was I trying to retrieve the data from API after page loaded. Now, I load all the recipients in NgOnInit method and filter the data after user change the company.
LoadRecipients method which we put into ngOnInit:
this.recipientService
.getRecipientsObs()
.subscribe((recipients) => (this.recipients = recipients));
}
And new OnChange method :
selectedVariable: any;
onSelectionChange(opened: boolean) {
console.log(`opened is : ${opened}`);
console.log(this.selectedVariable)
if (!opened && this.selectedVariable) {
this.orderForm.get("recipient.user_name").reset();
this.orderForm.get("recipient.user_surname").reset();
this.orderForm.get("recipient.user_telephone").reset();
const formsInput = merge(
this.orderForm.get('recipient.user_name').valueChanges,
this.orderForm.get('recipient.user_surname').valueChanges
);
formsInput.pipe(
startWith(""),
debounceTime(400),
distinctUntilChanged(),
map((search) => {
if (!this.recipients) {
return [];
} else {
search = search.toLowerCase();
}
return this.recipients.filter(
(recipient) =>
recipient.company.id === this.selectedVariable &&
recipient.user_name.toLowerCase().indexOf(search) > -1 &&
recipient.user_surname.toLowerCase().indexOf(search) > -1
);
}),
delay(500)
)
.subscribe(
(filteredRecipients) => {
this.searching = false;
this.filteredRecipients.next(filteredRecipients);
},
(error) => {
this.searching = false;
}
);
}
}

Nested FormArray Error: "cannot find control with path:"

I am developing an APP Using Angular 9.
But I'm facing a problem with nested formArray.
I'm having the error: "Cannot find control with path: 'arrayProcedimientos -> subprocedimientos'"
The goal of this form is that arrayProcedimientos is an array, and inside that there are subprocedimientos which is an array too.
Below is my code:
HTML:
<form #f="ngForm" [formGroup]="registros" class="fila-contenido">
<!-- A lot of mat-form-field -->
<div formArrayName="arrayProcedimientos"
*ngFor="let procedimientoArr of registros.get('arrayProcedimientos')['controls']; let i = index">
<div [formGroupName]="i">
<!-- Procedimientos ESTO ES UN ARRAY-->
<mat-form-field appearance="">
<mat-label>Procedimientos</mat-label>
<mat-select formControlName="procedimiento">
<mat-option *ngFor="let procedimiento of procedimientos"
[value]="procedimiento">
{{procedimiento}}
</mat-option>
</mat-select>
</mat-form-field>
<!-- Contenido ESTO ES UN ARRAY-->
<mat-form-field>
<mat-label>Fecha Inicio</mat-label>
<input matInput [matDatepicker]="picker8"
formControlName="fechaInicioProcedimiento">
<mat-datepicker-toggle matSuffix [for]="picker8"></mat-datepicker-toggle>
<mat-datepicker #picker8></mat-datepicker>
</mat-form-field>
<!-- Contenido ESTO ES UN ARRAY-->
<mat-form-field>
<mat-label>Fecha Fin</mat-label>
<input matInput [matDatepicker]="picker9" formControlName="fechaFinProcedimiento">
<mat-datepicker-toggle matSuffix [for]="picker9"></mat-datepicker-toggle>
<mat-datepicker #picker9></mat-datepicker>
</mat-form-field>
<!-- Notas -->
<mat-form-field appearance="fill">
<mat-label>Notas</mat-label>
<textarea matInput formControlName="notasProcedimiento"></textarea>
</mat-form-field>
</div>
{{registros.value | json}}
<div formArrayName="subprocedimientos"
*ngFor="let subprocedimiento of procedimientoArr.get('subprocedimientos')['controls']; let j = index">
<div [formGroupName]="j">
{{registros.value | json}}
</div>
</div>
</div>
</form>
TS:
export class AddRegistroComponent implements OnInit {
data = {
procedimientos: [
{
procedimiento: "",
fechaInicioProcedimiento: "",
fechaFinProcedimiento: "",
notasProcedimiento: "",
subprocedimientos: [
{
subprocedimiento: "",
fechaInicioSubprocedimiento: "",
fechaFinSubprocedimiento: "",
fechaLimiteSubprocedimiento: "",
notasSubprocedimiento: ""
}
]
}
]
}
registros: FormGroup;
constructor(public dialog: MatDialog, private service: AddRegistroService, private fbuilder: FormBuilder) {
this.inicializaFormGroup();
}
inicializaFormGroup() {
this.registros = this.fbuilder.group({
arrayProcedimientos: this.fbuilder.array([]),
});
this.setProcedimientos();
}
setProcedimientos() {
let control = <FormArray>this.registros.controls.arrayProcedimientos;
this.data.procedimientos.forEach(x => {
control.push(this.fbuilder.group({
procedimiento: x.procedimiento,
fechaInicioProcedimiento: x.fechaInicioProcedimiento,
fechaFinProcedimiento: x.fechaFinProcedimiento,
notasProcedimiento: x.notasProcedimiento,
subprocedimientos: this.setSubProcedimientos(x)
}))
})
}
setSubProcedimientos(x) {
let arr = new FormArray([])
x.subprocedimientos.forEach(y => {
arr.push(this.fbuilder.group({
subprocedimiento: y.subprocedimiento,
fechaInicioSubprocedimiento: y.fechaInicioSubprocedimiento,
fechaFinSubprocedimiento: y.fechaFinSubprocedimiento,
fechaLimiteSubprocedimiento: y.fechaLimiteSubprocedimiento,
notasSubprocedimiento: y.notasSubprocedimiento
}))
})
return arr;
}
}
My solution is based on an stackblitz that a mate from StackOverflow made, here is the link of his solution:
https://stackblitz.com/edit/angular-dffny7?file=app%2Fapp.component.html
Thanks for your time.
Alberto.

Material angular auto complete: how to remains the mat-option in the screen after selecting an option?

I'm using the Material auto complete angular.
After i select one option in my mat-option list, my mat-options hide. I need to remain the options after select. Gif showing the problem:
My html
<div class="col-xl-6 margemColunas">
<label>Anunciaremos nas contas *</label>
<input class="form-control inputContasB2W"
#inputContasB2W
[formControl]="contasB2WControl"
[matAutocomplete]="auto"
(matChipInputTokenEnd)="add($event)">
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event, i)">
<mat-option *ngFor="let contaB2W of contasFiltradas | async" [value]="contaB2W">
{{contaB2W}}
</mat-option>
</mat-autocomplete>
</div>
<div class="col-xl-6 alinhaChips">
<mat-chip-list>
<mat-chip
*ngFor="let contaB2W of produto.contasAnunciarB2W; let j = index"
[selectable]="selected"
[removable]="removable"
(removed)="removeTag(i, j)">
{{contaB2W}}
<mat-icon matChipRemove><i class="fa fa-close"></i></mat-icon>
</mat-chip>
</mat-chip-list>
</div>
My ts:
constructor(){
this.contasFiltradas = this.contasB2WControl.valueChanges.pipe(
startWith(null),
map((nomeConta: string | null) => nomeConta ? this._filter(nomeConta) : this.contasB2W.slice()));
}
private _filter(value: string): string[] {
const filterValue = value.toLowerCase();
return this.contasB2W.filter(conta => conta.toLowerCase().indexOf(filterValue) === 0);
}
selected(event: MatAutocompleteSelectedEvent, index: number): void {
this.produtosConfirmadosAnuncio[index].contasAnunciarB2W.push(event.option.viewValue);
this.inputContasB2W.nativeElement.value = '';
}
}
I used workaround to blur() and then focus() again the input.
In ts:
#ViewChild('inputContasB2W') autocompleteInput: ElementRef;
selected(event: MatAutocompleteSelectedEvent) {
[..do your stuff...]
this.autocompleteInput.nativeElement.blur();
setTimeout(() => {
this.autocompleteInput.nativeElement.focus();
}, 100
);
}
If you want to preserve input text you have to save it in _filter() and set the value again before blur() like this.contasB2WControl.setValue(this.lastFilterValue);
<mat-form-field>
<input
matInput
placeholder="New fruit..."
#input
#trigger="matAutocompleteTrigger" // <-- get trigger ref
[formControl]="control"
[matAutocomplete]="auto"
/>
<mat-autocomplete
#auto="matAutocomplete"
(optionSelected)="selected($event)"
>
<mat-option
*ngFor="let option of filteredOptions | async"
[value]="option"
(click)="$event.stopPropagation(); trigger.openPanel()" // <-- use it here
>
{{ option }}
</mat-option>
</mat-autocomplete>
</mat-form-field>

Md-dialog close after notification

I have dialog box where user can enter data and after clicking on "create", my dialog box gets close and user gets notification. I want to close my dialog box after user get notification and if user enters wrong data, user should get notification for that too and dialog box should not get close.
Currently, everything is working properly but I want my dialog box should disappear after notification (toster service).
Can anyone help me with this thing so that my dialog box will stay till i get notification for success and also for error?
exhibit.component.ts (main component)
createExhibit(event: any) {
let context = this;
this.createDialogRef = this.dialog.open(CreateExhibitDialogComponent, { width: '45em', data: {} });
this.createDialogRef.afterClosed().subscribe(
(newExhibit: Exhibit) => {
if (newExhibit.latitude) { newExhibit.latitude = newExhibit.latitude.toString().replace(/,/g, '.'); }
if (newExhibit.longitude) { newExhibit.longitude = newExhibit.longitude.toString().replace(/,/g, '.'); }
if (newExhibit) {
this.exhibitService.createExhibit(newExhibit)
.then(
() => {
this.toasterService.pop('success', this.translate('exhibit saved'));
setTimeout(function () {
context.reloadList();
}, 1000);
}
).catch(
error => this.toasterService.pop('error', this.translate('Error while saving'), error)
);
}
this.createDialogRef = null;
}
);
}
createExhibit.component.ts
<h1 md-dialog-title>{{ 'create exhibit' | translate }}</h1>
<md-dialog-content>
<form id="new-exhibit-form">
<md-input-container>
<input mdInput placeholder="{{ 'name' | translate }}" [(ngModel)]="exhibit.name" name="name" required>
</md-input-container>
<md-input-container>
<textarea
mdInput
mdTextareaAutosize
#autosize="mdTextareaAutosize"
placeholder="{{ 'description' | translate }}"
[(ngModel)]="exhibit.description"
name="desc"></textarea>
</md-input-container>
<div layout="row" layout-align="start center" flex>
<md-icon _ngcontent-c7="" class="mat-icon material-icons centered" role="img" aria-hidden="true">search</md-icon>
<md-input-container>
<input mdInput placeholder="search for location" autocorrect="off" autocapitalize="off" spellcheck="off" type="text" class="form-control" #search [formControl]="searchControl">
</md-input-container>
<md-input-container>
<input (blur)="updateMap()" mdInput type="number" min="-90" max="90" step="0.000001"
placeholder="{{ 'latitude' | translate }}" [(ngModel)]="exhibit.latitude" name="lat">
</md-input-container>
<md-input-container>
<input (blur)="updateMap()" mdInput type="number" min="-180" max="180" step="0.000001"
placeholder="{{ 'longitude' | translate }}" [(ngModel)]="exhibit.longitude" name="long">
</md-input-container>
<md-select class="align-right" placeholder="{{ 'status' | translate }}" [(ngModel)]="exhibit.status" name="status">
<md-option *ngFor="let statusOption of statusOptions" [value]="statusOption">{{ statusOption | translate }}</md-option>
</md-select>
</div>
<agm-map (mapClick)="selectLocation($event)" [zoom]=15 [latitude]="lat" [longitude]="lng">
<agm-marker [iconUrl]="'../../../images/map-marker.png'" *ngIf="exhibit.longitude && exhibit.latitude" [latitude]="exhibit.latitude" [longitude]="exhibit.longitude"></agm-marker>
</agm-map>
</form>
</md-dialog-content>
<md-dialog-actions align="end">
<button md-dialog-close md-raised-button>
{{ 'cancel' | translate }}
<md-icon>cancel</md-icon>
</button>
<button md-raised-button [disabled]="!exhibit.isValid()" color="primary" (click)="dialogRef.close(exhibit)">
{{ 'create' | translate }}
<md-icon>add_circle</md-icon>
</button>
</md-dialog-actions>
how to do this?
As windmaomao stated you need to manulally call dialog close() method. Material Dialog component proivide Observable only from afterClose() or beforeClose() and these methods listen to data passed through close() method. The close() method closes the dialog ofcourse what is not our expectation. You need to implement your own communication system between component and dialog build with kind of Observable or EventEmitter.
I have prepared simplified solution of your problem. The trick is you can obtain reference to any field or method of your dialog component with 'componentInstance' getter.
Dialog component
import {Component, EventEmitter, OnInit} from '#angular/core';
import {MatDialogRef} from "#angular/material";
#Component({
selector: 'app-simple-dialog',
template: `<h2 mat-dialog-title>Entering some data</h2>
<mat-dialog-content>Is data OK?</mat-dialog-content>
<mat-dialog-actions>
<button mat-button (click)="actionNo()">No</button>
<button mat-button (click)="actionYes()">Yes</button>
</mat-dialog-actions>`,
styleUrls: ['./simple-dialog.component.css']
})
export class SimpleDialogComponent implements OnInit {
private _action: EventEmitter<boolean> = new EventEmitter<boolean>();
answer = this._action.asObservable();
constructor(public dialogRef: MatDialogRef<SimpleDialogComponent>) { }
actionYes() {
this._action.next(true);
}
actionNo() {
this._action.next(false);
}
closeDialog() {
this.dialogRef.close();
}
ngOnInit() {
}
}
And HTML template excerpt code to include in your main component:
<button (click)="openDialog()">Open Dialog</button>
The code of openDialog() method:
openDialog() {
let dialogRef = this.dialog.open(SimpleDialogComponent);
dialogRef.componentInstance.answer.subscribe( answer => {
console.log('Answer from Dialog: ' + answer);
switch(answer) {
case true:
console.log('Data is OK. Closing dialog');
//do some complicated stuff
dialogRef.componentInstance.closeDialog();
//can be simple: dialogRef.close();
break;
case false:
console.log('Data is not OK. Not closing dialog. Show some toaster');
break;
}
}
)
}

How to create repeater control in child component using Reactive form

I am working in Angular project and I am creating form using Reactive approach. I want to create repeater control in child component. Please find the code below.
MainComponent.html
<div [formGroup]="myForm">
<input type="text" class="form-control" placeholder="Name"
formControlName="name" />
<div formArrayName="measurements">
<div *ngFor="let ctrl of myForm.get('measurements').controls; let i=index">
<div [formGroupName]="i">
<measurement-edit [measurementForm]="myForm.controls.measurements.controls[i]">
</measurement-edit>
</div>
</div>
</div>
<div formArrayName="thresholds">
<div *ngFor="let ctrl of myForm.get('thresholds').controls; let i = index">
<div [formGroupName]="i">
<threshold-edit
[thresholdForm]="myForm.controls.thresholds.controls[i]"
[valueType]="myForm.controls.valueType.value">
</threshold-edit>
</div>
</div>
</div>
</div>
ThresholdEditComponent.html
<div [formGroup]="thresholdForm">
<input type="checkbox"
[value]="isThresholdRequired"
formControlName="isThresholdRequired">
<!-- Here I want to use repeater controls and these controls should be accessed from parent (main) commponent -->
<div formArrayName="redThresholdItems">
<div *ngFor="let itemRow of thresholdForm.get('redThresholdItems').controls; let j = index;">
<div [formGroupName]="j">
<input type="text"
class="form-control txt-thr-val-width"
formControlName="thresholdValue1" />
</div>
</div>
</div>
</div>
MainComponent.ts
ngOnInit(): void {
this.createForm();
}
createMetricForm(): void {
this.myForm = this.fb.group({
name: ['', Validators.required],
easurements: this.fb.array([
this.initMeasurements()
]),
thresholds: this.fb.array([
this.initThresholds()
])
});}
initMeasurements(): FormGroup {
return this.fb.group({
isDerivedMetric: [false],
baselineMetricId: [''],
metricCalculation: [''],
metricSelection: ['']
});}
initThresholds(): FormGroup {
return this.fb.group({
isThresholdRequired: [false],
redThresholdItems: this.fb.array([
this.initThresholdRatings()
])
});}
initThresholdRatings(): FormGroup {
return this.fb.group({
thresholdValue1Operator: ['-1'],
thresholdValue1: [''],
compoundOperator: ['-1'],
thresholdValue2Operator: ['-1'],
thresholdValue2: ['']
});}
Please help me to create repeater controls in child component using the above code.