I have an autocomplete input, the problem is whenever I reset the input value it doesn't reset selection options. It only shows the latest selection.
Here's the GIF to illustrate the problem: https://recordit.co/aVXqlPYHFQ
I tried with this.form.get('panelForm').setValue(''); it reset the input but doesn't reset the options
Here's my code
<form *ngIf="options.length">
<mat-form-field [formGroup]="form">
<input type="text" [placeholder]="'Panel'" aria-label="Panel" matInput [formControl]="myControl" [formControlName]="'panelForm'" [matAutocomplete]="auto">
<button mat-button mat-icon-button matSuffix (click)="clear()" *ngIf="form.get('panelForm')">
<mat-icon>close</mat-icon>
</button>
<mat-autocomplete autoActiveFirstOption #auto="matAutocomplete" (optionSelected)='selectPanel($event.option.value)'>
<mat-option *ngFor="let option of filteredOptions | async" [value]="option.name">
{{option.name}} ({{option.genesCount}})
</mat-option>
</mat-autocomplete>
</mat-form-field>
</form>
myControl = new FormControl();
filteredOptions: Observable<Panel[]>;
public form: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.filteredOptions = this.myControl.valueChanges.pipe(
startWith(''),
map(value => { return this._filter(value)})
);
this.form = this.fb.group({
panelForm: new FormControl({ value: '', disabled: this.options.length < 1})
});
}
private _filter(value: string): Panel[] {
const filterValue = value.toLowerCase();
return this.options.filter(option => option.name && option.name.toLowerCase().includes(filterValue));
}
clear(){
this.form.get('panelForm').setValue('');
}
You don't need to use Form control if you are using Form control name (inside a FormGroup).
I recommend using the FormControl and set:
ngOnInit() {
this.myControl = new FormControl({'', disabled: this.options.length < 1});
this.filteredOptions = this.myControl.valueChanges.pipe(
startWith(''),
map(value => { return this._filter(value)})
);
}
clear() {
this.myControl.patchValue('');
}
Should work
Try to reset the value directly in html:
<form *ngIf="options.length">
<mat-form-field [formGroup]="form">
<input #myInput type="text" [placeholder]="'Panel'" aria-label="Panel" matInput [formControl]="myControl" [formControlName]="'panelForm'" [matAutocomplete]="auto"> // <-- set name here 'myInput'
<button mat-button mat-icon-button matSuffix (click)="clear()" *ngIf="form.get('panelForm')">
<mat-icon>close</mat-icon>
</button>
<mat-autocomplete autoActiveFirstOption #auto="matAutocomplete" (optionSelected)="selectPanel($event.option.value);myInput.value=''"> // <-- reset value here
<mat-option *ngFor="let option of filteredOptions | async" [value]="option.name">
{{option.name}} ({{option.genesCount}})
</mat-option>
</mat-autocomplete>
</mat-form-field>
</form>
Related
We are developing a site using Angular 9.
We have also integrated a Unity3D WebGL build in it.
When I try to type something in a text/password/number input field inside one of my forms, it doesn't write anything and the field doesn't seem to receive the input; also, the variable I bound the field to is not updated with the new value.
What makes it weirder is that:
I can select the input field (it gets highlighted as if I can start typing)
I can do CTRL+C on the field and what I copied somewhere else is pasted, as expected
I can use the type="number" arrow selectors to set the value of the field
I cannot type from the keyboard in the fields
I can interact as expected with other form tags, such as <select>
If I reload the page, it usually starts working as expected and I can type into the fields
Here is the code from my login form (component.ts above, template HTML below)
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../auth.service'
import { first } from 'rxjs/operators';
import { Router, ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.less']
})
export class LoginComponent implements OnInit {
email: string = "";
password: string = "";
returnUrl: string = "home";
constructor(private router: Router, private route: ActivatedRoute, private authService: AuthService) { }
ngOnInit(): void {
let tmpReturnUrl = this.route.snapshot.queryParams["returnUrl"];
if (tmpReturnUrl != undefined)
{
console.log("true");
this.returnUrl = tmpReturnUrl;
}
else
console.log("false");
setInterval(() => {
console.log("EMAIL: " + this.email);
}, 1000);
}
onSubmit(){
this.authService.login(this.email, this.password)
.pipe(first())
.subscribe(
result => {
console.log("CAIOAOAOAOOA");
this.router.navigate([this.returnUrl]);
},
err => console.log(err)
);
}
}
<div class="card z-depth-5 w-50">
<div class="card-body">
<div class="card-title">Log in</div>
<div class="card-text">
<form #companyLoginForm="ngForm" (ngSubmit)="onSubmit()">
<mat-form-field>
<mat-label>Email: </mat-label>
<input matInput required type="text" name="email" id="email" [(ngModel)]="email">
</mat-form-field>
<mat-form-field>
<mat-label>Password: </mat-label>
<input matInput required type="password" name="password" id="password" [(ngModel)]="password">
</mat-form-field>
<button type="submit" [disabled]="!companyLoginForm.form.valid">Login</button>
</form>
<a routerLink="/company-register">
<button mdbBtn type="button" color="primary" class="relative waves-light">Sign Up</button>
</a>
</div>
</div>
</div>
And here, the code from another form where I also use type="number" and <select> (component.ts above, template HTML below)
import { Component, OnInit, Output } from '#angular/core';
import { BlockFormService } from '../block-form.service';
import { BlockData } from '../blockCardData';
import { BlockUtilsService } from '../block-utils.service';
import { ApiService } from '../../core/api.service'
import { NgForm } from '#angular/forms';
#Component({
selector: 'app-block-form',
templateUrl: './block-form.component.html',
styleUrls: ['./block-form.component.less']
})
export class BlockFormComponent implements OnInit {
updateComplete : Boolean = false;
materials : string[];
products : string[];
varieties : string[];
nations : string[];
// companies : {name: string, id: string}[] = [];
company : string = "";
colors : string[] = ["White", "Grey", "Black", "Brown", "Red", "Green", "Yellow", "Blue"];
blockData : BlockData = {_id : "", blockId: "", company: "", material: "", product: "",
variety: "", color: "", nation: "", modelName : "", imagePreview : "",
price: null, blockNumber: "",
length: null, height: null, width: null,
weight: null
};
imagePreview: File = null;
zipFile: File = null;
invalidUpload: boolean = false;
constructor( private blockFormService: BlockFormService, public blockUtils: BlockUtilsService, private companiesUtils: ApiService )
{ }
ngOnInit(): void {
this.materials = this.blockUtils.getMaterials();
this.colors = this.blockUtils.getColors();
this.companiesUtils.getLoggedCompany().subscribe(companiesResult => {
this.blockData.company = companiesResult._id;
this.company = companiesResult.name;
});
}
onImageSelected(event){
console.log(event.target.files[0]);
if (event.target.files[0].type === "image/png")
{
if (this.invalidUpload)
this.invalidUpload = false;
this.imagePreview = event.target.files[0];
}
else{
if (!this.invalidUpload)
this.invalidUpload = true;
event.target.value = null;
}
}
onMaterialSet(newMaterial): void{
console.log("Material set");
this.products = this.blockUtils.getProducts(newMaterial);
//console.log(this.products);
// if (this.products.length > 0)
// this.blockData.product = this.products[0];
// else
this.blockData.product = "";
this.onProductSet(this.blockData.product);
}
onProductSet(newProduct): void{
console.log("Product set");
this.varieties = this.blockUtils.getVarieties(this.blockData.material, newProduct);
// if (this.varieties.length > 0)
// this.blockData.variety = this.varieties[0];
// else
this.blockData.variety = "";
this.nations = this.blockUtils.getNations(this.blockData.material, this.blockData.product);
if (this.nations.length > 0)
this.blockData.nation = this.nations[0];
else
this.blockData.nation = "";
this.onVarietySet(this.blockData.variety);
}
onVarietySet(newVariety): void{
console.log("Variety set");
// this.nations = this.blockUtils.getNations(this.blockData.material, this.blockData.product);
// if (this.nations.length > 0)
// this.blockData.nation = this.nations[0];
// else
// this.blockData.nation = "";
}
onSubmit(blockForm : NgForm, imageField, zipField): void{
this.blockFormService.sendBlock(this.blockData, this.imagePreview, this.zipFile)
.subscribe(res => {
console.log("Sent!");
this.updateComplete = true;
});
this.blockData = {
_id: "", blockId: "", company: "", material: "", product: "",
variety: "", color: "", nation: "", modelName: "", imagePreview: "",
price: null, blockNumber: "",
length: null, height: null, width: null,
weight: null
};
blockForm.resetForm();
imageField.value = null;
zipField.value = null;
this.imagePreview = null;
this.zipFile = null;
}
}
<div class="form-group">
<div class="text-center" *ngIf='updateComplete'>
Block added successfuly
</div>
<form #blockForm="ngForm" (ngSubmit)="onSubmit(blockForm, imageField, zipField)">
<div class="container">
<div class="row">
<div class="col-3">
<mat-form-field>
<mat-label>Company: </mat-label>
<!-- <mat-select required [(ngModel)]="blockData.company" name="company-field"
id="company-field">
<mat-option selected [value]="company.id">{{company.name}}</mat-option>
</mat-select> -->
<input matInput disabled [value]="company" type="text" name="company-field" id="company-field">
</mat-form-field>
</div>
<div class="col-3">
<mat-form-field>
<mat-label>Material: </mat-label>
<mat-select #matField required [(ngModel)]="blockData.material" name="kind-field"
id="kind-field" (selectionChange)="onMaterialSet(blockData.material)">
<mat-option *ngFor="let mat of materials" [value]="mat">{{mat}}</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="col-3">
<mat-form-field>
<mat-label>Product: </mat-label>
<mat-select required [(ngModel)]="blockData.product" name="product-field"
id="product-field" (selectionChange)="onProductSet(blockData.product)">
<mat-option *ngFor="let prod of products" [value]="prod">{{prod}}</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="col-3">
<mat-form-field>
<mat-label>Block Number: </mat-label>
<input matInput required [(ngModel)]="blockData.blockNumber" type="text" name="blockNumber-field" id="blockNumber-field">
</mat-form-field>
</div>
</div>
<div class="row">
<div class="col-3">
<mat-form-field>
<mat-label>Variety: </mat-label>
<mat-select required [(ngModel)]="blockData.variety" name="variety-field" id="variety-field"
placeholder="Variety" (selectionChange)="onVarietySet(blockData.variety)">
<mat-option *ngFor="let variety of varieties" [value]="variety">{{variety}}</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="col-3">
<!-- <label for="color-field">Color: </label> -->
<mat-form-field>
<mat-label>Color: </mat-label>
<mat-select required [(ngModel)]="blockData.color" name="color-field" id="color-field" placeholder="Color">
<mat-option *ngFor="let col of colors" [value]="col">{{col}}</mat-option>
</mat-select>
</mat-form-field>
<!-- <input #colField required [(ngModel)]="blockData.color" type="text" name="color-field" id="color-field" placeholder="Color"> -->
<!-- <color-circle #colorField [colors]='["#f44336", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4", "#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722", "#795548", "#607d8b"]' name="color-field" id="color-field" (onChange)="blockData.color = $event.color.hex"></color-circle> -->
</div>
<div class="col-3">
<mat-form-field>
<mat-label>Nation: </mat-label>
<!-- <mat-select required [(ngModel)]="blockData.nation" name="nation-field"
id="nation-field">
<mat-option *ngFor="let nat of nations" [value]="nat">{{nat}}</mat-option>
</mat-select> -->
<input matInput disabled [(ngModel)]="blockData.nation" type="text" name="nation-field" id="nation-field">
</mat-form-field>
</div>
<div class="col-3">
<mat-form-field>
<mat-label>Price: </mat-label>
<input matInput required [(ngModel)]="blockData.price" type="number" name="price-field" id="price-field">
</mat-form-field>
</div>
</div>
<div class="row">
<div class="col-3">
<mat-form-field>
<mat-label>Length: </mat-label>
<input matInput required [(ngModel)]="blockData.length" type="number" name="length-field" id="length-field">
</mat-form-field>
</div>
<div class="col-3">
<mat-form-field>
<mat-label>Width: </mat-label>
<input matInput required [(ngModel)]="blockData.width" type="number" name="width-field" id="width-field">
</mat-form-field>
</div>
<div class="col-3">
<mat-form-field>
<mat-label>Height: </mat-label>
<input matInput required [(ngModel)]="blockData.height" type="number" name="height-field" id="height-field">
</mat-form-field>
</div>
<div class="col-3">
<mat-form-field>
<mat-label>Weight: </mat-label>
<input matInput required [(ngModel)]="blockData.weight" type="number" name="weight-field" id="weight-field">
</mat-form-field>
</div>
</div>
<div class="row">
<div class="col-3">
<div class="file-field">
<div class="btn btn-primary btn-sm float-left">
<label for="image-field">Upload preview image: </label>
<input #imageField (change)="onImageSelected($event)" name="image-field" id="image-field" type="file" accept=".png, image/png" placeholder="Upload your file">
</div>
</div>
</div>
<div class="col-3">
<div class="file-field">
<div class="btn btn-primary btn-sm float-left">
<label for="zip-field">Upload models' zip: </label>
<input #zipField (change)="zipFile = $event.target.files[0];" name="zip-field" id="zip-field" type="file" placeholder="Upload your file">
</div>
</div>
</div>
</div>
<button type="submit" [disabled]="!blockForm.form.valid || imagePreview == null || zipFile == null || blockData.company === ''">Submit</button>
</div>
</form>
</div>
I hope I was clear enough, any help is appreciated :)
Finally I found the source of the problem. In our website we have integrated a Unity3D WebGL build and, if I moved from the web page with Unity to the login page, the Unity process was still running. Unity had the focus of every input of the keyboard, so it was catching all the inputs.
We resolved it by quitting the Unity application when we change page. This way, input fields can receive inputs from the keyboard again.
Another solution, maybe (I have not tested it), could be to not make Unity get the inputs, as discussed in this Unity forum's thread or by setting WebGLInput.captureAllKeyboardInput to false.
When i click on any button inside a form, all functions registered inside the form would be executed.
item.components.ts
import { Component, OnInit } from '#angular/core';
import { ItemsService } from '../../share/items.service';
import { MatDialogRef } from '#angular/material';
import { Item } from 'src/app/model/item';
#Component({
selector: 'app-item',
templateUrl: './item.component.html',
styleUrls: ['./item.component.scss']
})
export class ItemComponent implements OnInit {
constructor(public service:ItemsService,
public dialogRef: MatDialogRef<ItemComponent>) { }
ngOnInit() { }
onSubmit(model: Item, isValid: boolean){
console.log('!!! submit !!!');
}
addProperty(){
console.log('!!! add prop !!!');
}
onClear(){
console.log('!!! clear !!!');
}
onClose(){
console.log('!!! close !!!');
}
}
item.ts
export interface Item {
id: BigInteger | null
artNr: string;
name: string;
parentGroup: string;
childGroup: string;
properties: Array<{key: string, text: string}>;
}
items.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse} from '#angular/common/http';
import { Observable, from } from 'rxjs';
import { FormGroup, FormControl, Validators, FormBuilder, FormGroupDirective, FormArray } from '#angular/forms'
import { Address } from '../model/address';
import { Item } from '../model/item';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'my-auth-token'
})
};
interface ResponseItem{
results:Item[];
}
#Injectable({
providedIn: 'root'
})
export class ItemsService {
constructor(private http:HttpClient, private formBuilder:FormBuilder) { }
ngOnInit(){
this.initializeItemFormGroup();
}
//** ITEM AREA */
itemForm: FormGroup;
initializeItemFormGroup(){
this.itemForm = this.formBuilder.group({
id: [null],
artNr: ['', [Validators.required]],
name: ['', [Validators.required]],
parentGroup: ['', [Validators.required]],
childGroup: [''],
properties: this.formBuilder.array([this.addPropertyGroup()] )
});
}
addPropertyGroup(){
return this.formBuilder.group({
key: [''],
text: ['']
})
}
removeProperty(index){
this.property.removeAt(index);
}
addProperty() {
this.property.push(this.addPropertyGroup());
}
get property():FormArray {
return <FormArray>this.itemForm.get('properties');
}
}
item.component.html
<form [formGroup]="service.itemForm" class="normal-form" (ngSubmit)="onSubmit(service.itemForm.value, service.itemForm.valid)">
<mat-grid-list cols="2">
<mat-grid-tile>
<div class="controles-container">
<input type="hidden" formControlName="id" >
<mat-form-field>
<input formControlName="artNr" matInput placeholder="EA0123" >
<mat-error>Artikelnummer zwingend erforderlich</mat-error>
</mat-form-field>
<mat-form-field>
<input formControlName="name" matInput placeholder="Artikelbezeichnung" >
<mat-error>Bezeichnung ist erforderlich</mat-error>
</mat-form-field>
<mat-form-field>
<input formControlName="parentGroup" matInput placeholder="Karosserie" >
</mat-form-field>
<mat-form-field>
<input formControlName="childGroup" matInput placeholder="Schrauben" >
</mat-form-field>
</div>
</mat-grid-tile>
<mat-grid-tile>
<button mat-icon-button (click)="addProperty()"><mat-icon>add</mat-icon></button>
<div class="make-scrollable" formArrayName="properties">
<div *ngFor="let property of service.itemForm.controls.properties?.value; let i=index;" class="panel panel-default">
<ng-container [formGroupName]="i">
<mat-form-field>
<input formControlName="key" matInput placeholder="Titel" >
</mat-form-field>
<mat-form-field>
<input formControlName="text" matInput placeholder="Beschreibender Text" >
</mat-form-field>
</ng-container>
</div>
</div>
</mat-grid-tile>
<mat-grid-tile [colspan]="2">
<div class="buttom-row">
<button mat-raised-button color="primary" type="submit" [disabled]="service.itemForm.invalid">Speichern</button>
<button mat-raised-button color="warn" (click)="onClear()">Bereinigen</button>
</div>
</mat-grid-tile>
</mat-grid-list>
</form>
When i click any Button, the console is logging (currently I tried to add a property: (click)="addProperty()")
Console output:
item.component.ts:32 !!! add prop !!!
item.component.ts:26 !!! submit !!!
item.component.ts:44 !!! close !!!
But i expect the following output
Console output:
item.component.ts:32 !!! add prop !!!
Thanks to Julius Dzidzevičius.
He told me to set the button type="button"
Now everything works fine!!!
See below
´´´HTML
<form [formGroup]="service.itemForm" class="normal-form" (ngSubmit)="onSubmit(service.itemForm.value, service.itemForm.valid)">
<mat-grid-list cols="2">
<mat-grid-tile>
<div class="controles-container">
<input type="hidden" formControlName="id" >
<mat-form-field>
<input formControlName="artNr" matInput placeholder="EA0123" >
<mat-error>Artikelnummer zwingend erforderlich</mat-error>
</mat-form-field>
<mat-form-field>
<input formControlName="name" matInput placeholder="Artikelbezeichnung" >
<mat-error>Bezeichnung ist erforderlich</mat-error>
</mat-form-field>
<mat-form-field>
<input formControlName="parentGroup" matInput placeholder="Karosserie" >
</mat-form-field>
<mat-form-field>
<input formControlName="childGroup" matInput placeholder="Schrauben" >
</mat-form-field>
</div>
</mat-grid-tile>
<mat-grid-tile>
<button mat-icon-button type="button" (click)="addProperty()"><mat-icon>add</mat-icon></button>
<div class="make-scrollable" formArrayName="properties">
<div *ngFor="let property of service.itemForm.controls.properties?.value; let i=index;" class="panel panel-default">
<ng-container [formGroupName]="i">
<mat-form-field>
<input formControlName="key" matInput placeholder="Titel" >
</mat-form-field>
<mat-form-field>
<input formControlName="text" matInput placeholder="Beschreibender Text" >
</mat-form-field>
</ng-container>
</div>
</div>
</mat-grid-tile>
<mat-grid-tile [colspan]="2">
<div class="buttom-row">
<button mat-raised-button color="primary" type="submit" [disabled]="service.itemForm.invalid">Speichern</button>
<button mat-raised-button type="button" color="warn" (click)="onClear()">Bereinigen</button>
</div>
</mat-grid-tile>
</mat-grid-list>
</form>
´´´´
i have two Angular2 Material 2 Autocomplete fields: Customers and branches. So when the user now selects one specific customer in the first field, i want to retrieve the branches from my mongoDB who have the customerID from the customer the user selected.
But I'm not able to get the solution.
createHardware.component.ts
myControlCustomers: FormControl = new FormControl();
availableCustomers = [];
filteredCustomers: Observable<any[]>;
selectedCustomers = null;
selectedCustomersName = '';
this.availableCustomers = [];
this.customerService.getCustomers().subscribe(customers => {
this.availableCustomers = customers.customer;
});
this.filteredCustomers = this.myControlCustomers.valueChanges
.pipe(
startWith(''),
map(valCustomer => this.filterCustomers(valCustomer))
);
filterCustomers(valCustomer: any): any[] {
return this.availableCustomers.filter(customers => {
return customers.name.toLowerCase().indexOf(valCustomer.toLowerCase()) > -1;
});
}
createHardware.component.html
<div class="form-group">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Kunden auswählen" aria-label="Number" matInput [formControl]="myControlCustomers" [matAutocomplete]="auto2" [(ngModel)]="selectedCustomersName">
<mat-autocomplete #auto2="matAutocomplete">
<mat-option *ngFor="let customer of filteredCustomers | async" [value]="customer._id" (onSelectionChange)="getBranches()">
{{ customer.name }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</div>
Here I have made whole working example as per your question ,
Template side :
<form class="example-form">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Selct User" aria-label="Number" matInput [formControl]="usersControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete" (optionSelected)='getPosts($event.option.value)'>
<mat-option *ngFor="let user of users" [value]="user.id">
{{ user.name }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
<mat-form-field class="example-full-width">
<input type="text" placeholder="Posts From User" aria-label="Number" matInput [formControl]="postsControl" [matAutocomplete]="auto2">
<mat-autocomplete #auto2="matAutocomplete">
<mat-option *ngFor="let post of posts" [value]="post.id">
{{ post.title }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</form>
Component Side :
usersControl: FormControl = new FormControl();
postsControl: FormControl = new FormControl();
users:any[] = [];
posts:any[] = [];
constructor(private http : HttpClient){
}
ngOnInit(){
let url = 'https://jsonplaceholder.typicode.com/users/';
this.http.get(`${url}`).subscribe(users => {
this.users = [...users];
});
}
getPosts(userId){
let url = 'https://jsonplaceholder.typicode.com/posts?userId='+userId;
this.http.get(`${url}`).subscribe(posts => {
this.posts = [...posts];
});
}
WORKING EXAMPLE
Hope this will clear all your doubts.
Change your component.html to
<div class="form-group">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Kunden auswählen" aria-label="Number" matInput [formControl]="myControlCustomers" [matAutocomplete]="auto2" >
<mat-autocomplete #auto2="matAutocomplete" [displayWith]="displayFnCustomer">
<mat-option *ngFor="let customer of filteredCustomers | async" [value]="customer" (onSelectionChange)="getBranches(customer)">
{{ customer.name }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</div>
<div class="form-group">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Kunden auswählen" aria-label="Number" matInput [formControl]="myControlBranches" [matAutocomplete]="auto3" >
<mat-autocomplete #auto3="matAutocomplete" [displayWith]="displayFnBranch">
<mat-option *ngFor="let branch of filteredBranches | async" [value]="branch" (onSelectionChange)="getXXX(branch)">
{{ branch.name }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</div>
And define following in your component.ts
myControlCustomers: FormControl = new FormControl();
availableCustomers = [];
filteredCustomers: Observable<any[]>;
selectedCustomer: any;
myControlBranches: FormControl = new FormControl();
availableBranches = [];
filteredBranches: Observable<any[]>;
selectedBranch: any;
this.filteredCustomers = this.myControlCustomers.valueChanges
.pipe(
startWith(''),
map(val => this.filterCustomers(val))
);
filterCustomers(val: any): any[] {
let name = val.name ? val.name : val;
return this.availableCustomers.filter(customer => {
return customer.name.toLowerCase().indexOf(name.toLowerCase()) > -1;
});
}
this.filteredBranches = this.myControlBranches.valueChanges
.pipe(
startWith(''),
map(val => this.filterBranches(val))
);
filterBranches(val: any): any[] {
let name = val.name ? val.name : val;
return this.availableBranches.filter(branch => {
return branch.name.toLowerCase().indexOf(name.toLowerCase()) > -1;
});
}
displayFnCustomer(customer: any): string {
return customer? customer.name : customer;
}
displayFnBranch(branch: any): string {
return branch? branch.name : branch;
}
getBranches(customer) {
this.selectedCustomer = customer;
// here you can use customer._id to fetch branches
// this.http....
}
getXXX(branch) {
this.selectedBranch = branch;
// here you can use branch._id if you want for any purpose
}
Hope this would resolve your requirement.
I have been struggling for 1 and half day and still couldn't find any solution to the problem. I am working on simple form which has select and checkboxes element.When I try submitting the form I do not get the values of select and checkboxes but rather I just get true in the console.
I am working on template driven form.
<form (ngSubmit)="addSubcontractor(subcontractorForm)" #subcontractorForm="ngForm">
<h5>Type :</h5>
<div class="form-group">
<div *ngFor="let contractor_type of contractor_types;let i = index;" class="pull-left margin-right">
<label htmlFor="{{ contractor_type | lowercase }}">
{{ contractor_type }} :
</label>
<input type="checkbox" name="_contractor_type[{{i}}]" [value]="contractor_type" ngModel>
</div>
<div class="form-group">
<select class="form-control costcodelist" name="_cc_id[]" multiple="true" ngModel>
//When I put ngModel on select I just keep getting error
//TypeError: values.map is not a function
<option *ngFor="let costcode of costcodes" [selected]="costcode.id == -1" [value]="costcode.id">{{ costcode.costcode_description }}</option>
</select>
</div>
</form>
Component Section
export class SubcontractorComponent implements OnInit, OnDestroy {
private contractor_types = ['Subcontractor', 'Supplier', 'Bank', 'Utility'];
constructor(private costcodeService: CostcodeService,
private subcontractorService: SubcontractorService) {
this.subcontractorService.initializeServices();
}
ngOnInit() {
this.costcode_subscription = this.costcodeService.getAll()
.subscribe(
(costcodes) => {
this.costcodes = costcodes;
});
}
addSubcontractor(form: NgForm) {
console.log(form.value);
}
}
When I remove ngModel from select element the code runs fine and When I submit the form I get the following output.
_contractor_type[0]:true
_contractor_type[1]:true
_contractor_type[2]:true
_contractor_type[3]:""
I do not get the checkboxes values as well the selected options from select element.
Your comments and answer will appreciated a lot.
Thanks
Here is a simple selection that i use with my Template driven form.
<form #f="ngForm" (submit)="addOperator(f.valid)" novalidate>
<label>Name</label>
<input [(ngModel)]="operator.name" name="name">
<label >Service</label>
<select #service="ngModel" name="service" [(ngModel)]="operator.service">
<option *ngFor='let service of services' [ngValue]='service'>{{service.name}}</option>
</select>
<label >Enabled</label>
<label>
<input type="checkbox" name="enabled" [(ngModel)]="operator.enabled">
</label>
<button type="submit">Create Operator</button>
</form>
.ts
operator: Operator;
ngOnInit() {
this.operator = <Operator>{}; // // Initialize empty object, type assertion, with this we loose type safety
}
addOperator(isValid: boolean) {
if (isValid) {
this.operatorsService.addOperator(this.operator).then(operator => {
this.goBack();
});
}
}
Also im importing this
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
I try to build a 'task manager' to log the tasks that my customers send me.
I have my new-task.hbs form
<div id="new-task-form" class="col-md-12">
<form>
<div class="form-group">
<label>Customer</label>
{{input type="text" class="form-control" value=customer placeholder="Add Customer..."}}
</div>
<div class="form-group">
<label>Task</label>
{{textarea class="form-control" value=task placeholder="Add Task..."}}
</div>
<div class="form-group">
<label>Incoming</label>
{{input type="number" class="form-control" value=incoming placeholder="Bring it on..."}}
</div>
<div class="form-group">
<label>Pending</label>
{{input type="number" class="form-control" value=pending placeholder="Don't bring it on..."}}
</div>
<div class="form-group">
<label>Closed Date</label>
{{input type="date" class="form-control" value=closed_date placeholder="Please close me..."}}
</div>
<button {{action 'addTask'}} class="btn btn-primary">Submit</button>
</form>
My controller.
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
addTask: function(){
var customer = this.get('customer');
var task = this.get('task');
var incoming = this.get('incoming');
var pending = this.get('pending');
var closed_date = this.get('closed_date');
//Create new task
var newTask = this.store.createRecord('task',{
customer: customer,
task: task,
incoming: incoming,
pending: pending,
closed_date: closed_date
});
//save to db
newTask.save();
}
}
});
And the model
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
export default Model.extend({
customer: attr('string'),
task: attr('string'),
incoming: attr('number', { defaultValue: 0 }),
pending: attr('number', { defaultValue: 0 }),
closed_date: attr('date'),
created: attr('string', {
defaultValue: function(){
return new Date();
}
})
});
How can i set a model defaultValue for a the closed_date input to a string "Not entered yet"?
If i leave it like this and not enter a value i get an "Invalid Date".
closed_date: attr('date')
If i set this i get the current date.
closed_date: attr('date', { defaultValue: 'Not entered yet' })
From my experience I suggest you leave closed_date as it is (as date) and focus on DISPLAYING Not entered yet in each place that you want to show it when closed_date isn't entered.
For example when you show model values in template you can use:
Closed date: {{if model.closed_date model.closed_date 'Not entered yet'}}
Consider using ember-pikaday for a nice date-selection experience (which also gives you the placeholder functionality you are looking for!).
Furthermore, I'd suggest that you use the model hook of your new-task route to do your model setup. Combine that with ember-data-route to do cleanup on route exit, and you should be good to go:
router.js:
this.route('tasks', function() {
this.route('new');
});
routes/tasks/new.js:
import Ember from 'ember';
import DataRoute from 'ember-data-route';
export default Route.extend(DataRoute, {
model() {
return this.store.createRecord('task');
}
});
Note below how the form field values have been updated to model.fieldName. These values are bound to the model you created in your route's model hook.
templates/tasks/new.hbs:
<div id="new-task-form" class="col-md-12">
<form>
<div class="form-group">
<label>Customer</label>
{{input type="text" class="form-control" value=model.customer placeholder="Add Customer..."}}
</div>
<div class="form-group">
<label>Task</label>
{{textarea class="form-control" value=model.task placeholder="Add Task..."}}
</div>
<div class="form-group">
<label>Incoming</label>
{{input type="number" class="form-control" value=model.incoming placeholder="Bring it on..."}}
</div>
<div class="form-group">
<label>Pending</label>
{{input type="number" class="form-control" value=model.pending placeholder="Don't bring it on..."}}
</div>
<div class="form-group">
<label>
Closed Date:
{{pikaday-input value=model.closedDate placeholder="Please close me..."}}
</label>
</div>
<button {{action 'addTask'}} class="btn btn-primary">Submit</button>
</form>
Note: prefer camelCasedMultipleWordModelAttributeName vs underscored_attribute_name
models/task.js:
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
export default Model.extend({
customer: attr('string'),
task: attr('string'),
incoming: attr('number', { defaultValue: 0 }),
pending: attr('number', { defaultValue: 0 }),
closedDate: attr('date', {
defaultValue() { return new Date(); }
}),
created: attr('string', {
defaultValue() { return new Date(); }
})
});
Now the nice part. Here's what your controller action looks like when you do your setup in your route's model hook:
controllers/tasks/new.js
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
addTask: function(){
this.get('model').save();
}
}
});
and for extra credit you could install ember-route-action-helper and move the controller action onto the route and remove the controller completely.