Setting value of FormControl does not trigger valueChanges - html

I have an Angular Material Autocomplete and I am trying to trigger the valueChanges subscription by setting a value in ngAfterViewInit. But nothing happens and I dont know why. I also checked similar questions and the answers didnt help me out so far.
This is the relevant code:
options$?: Observable<GroupOption[]>;
/** interne Liste der Optionen zum Filtern, Prüfen etc. von Items */
private options: GroupOption[] = [];
private emptyOption: OptGroupItem = {
group: '',
name: '',
id: '',
};
control = new FormControl('');
ngOnInit(): void {
this.options$ = this.control.valueChanges.pipe(
startWith(''),
map(value => this.createGroupOptions(value)),
tap(value => console.log(value))
);
this.options$.subscribe(res => {
console.log("options");
console.log(res);
})
}
ngAfterViewInit() {
console.log('what is inside field?');
console.log(this.control.value);
this.control.setValue(this.emptyOption);
}
And my html:
<mat-form-field appearance="outline">
<mat-label>{{ label }}</mat-label>
<ng-container>
<input
type="text"
matInput
[formControl]="control"
[matAutocomplete]="auto"
/>
<mat-autocomplete
#auto="matAutocomplete"
(optionSelected)="onDropdownChange($event)"
[displayWith]="displayName"
>
<mat-optgroup
*ngFor="let option of options$ | async"
[label]="option.group"
>
<mat-option *ngFor="let item of option.items" [value]="item">
{{ item.name }}
</mat-option>
</mat-optgroup>
</mat-autocomplete>
</ng-container>
</mat-form-field>
Maybe you know whats going on? Thanks for every help!

Related

Angular set focus to select element on page load

I can set focus to an input field, but not a select. What am I missing?
This works:
<input matInput formControlName="name" required maxlength="100" id="elementFocus" appElementFocus="true" />
This does not
<mat-select formControlName="countryId" required id="elementFocus" appElementFocus="true">
Here's the entire section that exists now, and when the page loads, the 2nd form element (Name) has focus. I need the select to have focus.
<mat-card-content>
<div fxLayout="column" fxLayoutGap="25px" class="container">
<mat-form-field appearance="standard">
<mat-label>Countries</mat-label>
<mat-select formControlName="countryId" #countrySelectRef required>
<mat-option *ngFor="let c of countries" [value]="c.id"
>{{ c.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="hasError(f.countryId)">{{
getErrorMessage(f.countryId)
}}</mat-error>
</mat-form-field>
<mat-form-field appearance="standard">
<mat-label>Name</mat-label>
<input matInput formControlName="name" required maxlength="100" />
<mat-hint align="end">{{ f.name.value.length }} / 100</mat-hint>
<mat-error *ngIf="hasError(f.name)">{{
getErrorMessage(f.name)
}}</mat-error>
</mat-form-field>
</div>
</mat-card-content>
.ts code (narrowed down to all that is relevant, I think).
#ViewChild(MatSelect) countrySelectRef: MatSelect;
constructor(
private formBuilder: FormBuilder,
private activatedRoute: ActivatedRoute,
private router: Router,
private matDialog: MatDialog,
private readonly countryService: CountryService,
private readonly stateService: StateService,
private messageService: UIMessageService) { }
ngOnInit(): void {
const paramId = this.getParam('id');
this.isAdd = !paramId
this.id = !paramId ? uuid() : paramId
this.heading = this.isAdd ? `Add ${this.headingName}` : `Edit ${this.headingName}`
this.initForm();
if (!this.isAdd) {
this.patchForm()
}
}
ngAfterViewInit(): void {
if (this.countrySelectRef) {
this.countrySelectRef.focus();
}
}
Your code has a problem, in the sense that it can trigger a ExpressionChangedAfterItHasBeenCheckedError error, but even with that error, it should work fine. Your code and the error message, described in your comment, don't match up and may indicate that the issue is elsewhere.
I do provide an alternative, which hopefully, will solve your problem. You need to add the A11yModule to the imports array of the module that declare the component that use that template.
Template
<mat-form-field appearance="standard" cdkMonitorSubtreeFocus>
<mat-label>Countries</mat-label>
<mat-select formControlName="countryId" #countrySelectRef required
cdkTrapFocusAutoCapture
cdkTrapFocus="false">
<mat-option *ngFor="let c of countries" [value]="c.id">
{{ c.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="hasError(f.countryId)">
{{ getErrorMessage(f.countryId) }}
</mat-error>
</mat-form-field>
Note the 2 (with 3 inputs in total) directives added:
cdkMonitorSubtreeFocus: considers an element focused if it or any of its children are focused
cdkTrapFocusAutoCapture: Whether the directive should automatically move focus into the trapped region upon initialization and return focus to the previous activeElement upon destruction.
cdkTrapFocus="false": If this value is true, it will trap the focus on that directive indefinitely.
Ref: Material documentation

Mat Select multiple set selected value

I want to set some items in the list to be selected when I open the list.
my page:
target page:
html
<mat-form-field
appearance="fill"
color="accent"
class="w-100 ps-lg-4 ps-0"
>
<mat-label>Applicativo Di Vendita</mat-label>
<mat-select
[formControl]="applicativoDiVenditaControl"
multiple
required
(closed)="selectOnClose()"
>
<mat-select-trigger>
{{
applicativoDiVenditaControl.value
? applicativoDiVenditaControl.value[0]
: ""
}}
<span
*ngIf="applicativoDiVenditaControl.value?.length > 1"
class="additional-selection"
>
(+{{ applicativoDiVenditaControl.value.length - 1 }}
{{
applicativoDiVenditaControl.value?.length === 2
? "altro"
: "altri"
}})
</span>
</mat-select-trigger>
<mat-option *ngIf="applicativoDiVenditaList.length <= 0" disabled
>Nessun Elemento</mat-option
>
<mat-option
*ngFor="let applicativoDiVendita of applicativoDiVenditaList"
[value]="applicativoDiVendita.value"
>{{ applicativoDiVendita.value }}</mat-option
>
</mat-select>
<mat-error *ngIf="applicativoDiVenditaControl.invalid">{{
getRequiredErrorMessage()
}}</mat-error>
</mat-form-field>
TS
applicativoDiVenditaControl: FormControl;
applicativoDiVenditaList: IdValueObj[] = [];
constructor(
#Inject(MAT_DIALOG_DATA) public product: productClass,
public dialogRef: MatDialogRef<CatalogoProdottiComponent>,
private productCharService: ProductCharService,
) {
this.applicativoDiVenditaControl = new FormControl('', Validators.required);
this.getSalesApplication();
}
getSalesApplication() {
this.productCharService.getChar('sales').subscribe(
(data) => {
this.applicativoDiVenditaList = data;
},
(error) => {
console.log(error);
}
);
}
i tried with [(ngModel)] amd with [compareWith]="comparer", but dont works
i tried this StackBlitz, but dont work too
https://stackblitz.com/edit/angular6-mat-select-with-obj-default-value?file=app%2Fselect-multiple-example.html
angular version 12.2.3
On TS
export class YourForm{
selectedValue: string[]=['0','1']
yourValues= [
{value: '0', viewValue: 'Value 0'},
{value: '1', viewValue: 'Value 1'},
{value: '2', viewValue: 'Value 2'}
];
}
On HTML
<mat-form-field>
<mat-select placeholder="Your options" [(ngModel)]="selectedValue" name="values" multiple>
<mat-option *ngFor="let v of yourValues" [value]="v.value">
{{v.viewValue}}
</mat-option>
</mat-select>
</mat-form-field>

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>

Angular Materials autocomplete data loading but not filtering working perfectly angular 6

This is my HTML code. form the example I took the code. sample code. But filtering when typing is not working
<mat-form-field appearance="outline">
<input type="text" formControlName="planClient" matInput placeholder="Plan Client" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let client of filteredOptions | async" [value]="client">
{{client}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
This is my .ts file
filteredOptions: Observable<string[]>;
clients: string[] = [
'Alabama',
'California',
'Colorado',
'Connecticut',
'SelectHealth',
'UMR'
];
planClient = new FormControl();
this.filteredOptions = this.planClient.valueChanges
.pipe(
startWith(''),
map(value => this._filter(value))
);
public _filter(value: string): string[] {
const filterValue = value.toLowerCase();
return this.clients.filter(client => client.toLowerCase().includes(filterValue));
}
Here you need to change the formControlName to property binding using follwing way.
<input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
Here is Demo on Stackblitz of your code
Change formControlName to [formControl]
<input type="text" [formControl]="planClient" matInput placeholder="Plan Client" [matAutocomplete]="auto">

How to get index of selected option outside the select in Angular?

I have a simple <select> in Angular (with Material) as follows:
<mat-form-field>
<mat-label>Type</mat-label>
<mat-select placeholder="Type" formControlName="type" name="type" id="name">
<mat-option>None</mat-option>
<mat-option *ngFor="let t of types" [value]="t">
{{t}} <-- it is enum -->
</mat-option>
</mat-select>
</mat-form-field>
I would like to use index of selected type in the other part of code.
To be more precise: in *ngFor of another select. Therefore, I can't use documentById.
In addition, I don't want to install jQuery just for do this.
Is it possible?
As suggested by this Angular Material Documentation example, you can bind to [(value)] two ways:
<mat-form-field>
<mat-select [(value)]="selected">
<mat-option>None</mat-option>
<mat-option *ngFor="let t of types; let i = index" [value]="i">{{t}}</mat-option>
</mat-select>
</mat-form-field>
<p>You selected: {{selected}}</p>
selected will be a property on your class.
Here's a Working StackBlitz from the Angular Team for your reference.
You can simply set [(ngModel)] variable and use the variable to get your index
<mat-select placeholder="Type" [(ngModel)]="selected" formControlName="type" name="type" id="name">
<mat-option>None</mat-option>
<mat-option *ngFor="let t of types" [value]="t">
{{t}} <-- it is enum -->
</mat-option>
</mat-select>
and then in the component, use
this.index = this.types.findIndex(item => item === selected);
Per Sajeetharans answer, using ngModel together with reactive form is not recommended, and is also deprecated. Instead watch for the changes of the form control and find the index and store it in a variable for later use. Here's a sample:
myForm: FormGroup;
idx: number;
foods = ['Steak', 'pizza-1'];
constructor(private fb: FormBuilder) { }
ngOnInit() {
this.myForm = this.fb.group({
mySelect: ['']
});
this.myForm.get('mySelect').valueChanges.subscribe((value) => {
this.idx = this.foods.findIndex(val => val === value);
console.log(this.idx)
});
}