Angular/material autocomplete: options do not render - autocomplete

Angular version: 5.0.0
angular/material version: 5.2.4
I've got this form:
<form #updateForm="ngForm">
<mat-form-field>
<input type="text" matInput [formControl]="studentFormControl [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let student of students" [value]="student">
{{student}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</form>
And I've got this component:
import { FormGroup, FormControl, FormsModule, ReactiveFormsModule } from '#angular/forms';
import { MatInputModule, MatAutocompleteModule, MatFormFieldModule } from '#angular/material';
export class DashboardComponent implements OnInit {
this.studentFormControl = new FormControl();
this.students = ['hi', 'hello'];
...
}
Although the form shows up, the autocomplete box remains dimensionless without options rendered within:
screenshot of input/autocomplete box
It should be noted as well that the form is nested within an ngbModal. Any ideas what could be happening here?

So I'm no expert here, but from what I understand, the autocomplete function requires an observable of sorts, at least if you want to be able to filter results as you type, to be selected. Since you are using an array you need to first turn it into an observable. As your array is loaded immediately you would need to use RXJS's subjects and more specifically the BehaviorSubject.
Once it becomes an observable you can then call it using the asyncpipe on your form.
This is not to say you couldn't have done it with an array and not used the asyncPipe, however that array would have to be loaded in OnInit.
See the example for both
import { Observable, from, Subscription, BehaviorSubject } from 'rxjs';
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormControl, FormsModule, ReactiveFormsModule } from '#angular/forms';
import { MatInputModule, MatAutocompleteModule, MatFormFieldModule } from '#angular/material';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit {
studentFormControl = new FormControl();
studentFormControl1 = new FormControl();
array = ['test','one','two'];
array1=[];
arrayObs: Observable<any>;
arrayBehObs = new BehaviorSubject(this.array);
constructor() { }
ngOnInit() {
this.array1.push('test','one','two');
this.arrayObs = this.returnAsObs();
}
private returnAsObs() {
return this.arrayBehObs.asObservable();
}
}
<form #updateForm="ngForm">
<input type="text" matInput [formControl]="studentFormControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let student of arrayObs|async" [value]="student">
{{student}}
</mat-option>
</mat-autocomplete>
<input type="text" matInput [formControl]="studentFormControl1" [matAutocomplete]="auto1">
<mat-autocomplete #auto1="matAutocomplete">
<mat-option *ngFor="let student of array1" [value]="student">
{{student}}
</mat-option>
</mat-autocomplete>
</form>

Related

How to transfer the value of an array between components SAC [Angular 15]

I'm looking to transfer the value of an array between two components. My application is a basic crud application that will store accounts with an id, name and date.
I have a service that has 2 basic functions and an array to pull from -
The array is holding the accounts. It is acting as my database for right now.
account-crud.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http'
import { Account } from '../models/Account';
import { AddAccountComponent } from '../dashboard/add-account/add-account.component';
#Injectable({
providedIn: 'root'
})
export class AccountCrudService {
private accounts:Account[] = [];
addAccount(account: Account){
this.accounts.push(account);
};
getAccounts(): Account[] {
console.log(this.getAccounts)
return this.accounts;
}
}
This service is used by 2 components. The desired behavior is for me to use the AddAccountComponent to add components, which will then push to the array in our service and then display all the accounts in the AllAccountsComponent.
Here is the add account component -
add-account.component.ts
#Component({
selector: 'app-add-account',
standalone: true,
imports: [FormsModule, ReactiveFormsModule, JsonPipe, RouterModule],
providers: [AccountCrudService],
templateUrl: './add-account.component.html',
styleUrls: ['./add-account.component.scss']
})
export class AddAccountComponent {
constructor(titleService:Title, private AccountCrudService: AccountCrudService) {
titleService.setTitle("Add Account")
}
public printAccounts() {
console.log('accounts :');
console.log(this.AccountCrudService.getAccounts());
}
private account:Account = {id: 0, name:'', date:''};
public get Account():Account{
return this.account;
}
public addAccount(){
const currentAccount:Account = {
id: this.account.id,
name: this.account.name,
date: this.account.date
};
this.AccountCrudService.addAccount(currentAccount);
}
And here is the HTML for the add account component -
add-account.component.html
<form #addAccountForm="ngForm">
<label for="name">Account Name</label>
<input name="accountName" type="text" [(ngModel)]="Account.name">
<br>
<label for="date">Initial Email Date</label>
<input name="date" [(ngModel)]="Account.date">
<br>
<button type="submit" (click)="addAccount()">Submit</button>
<button type="button" (click)="printAccounts()">console log accts</button>
<button type="button" routerLink="/dashboard">back</button>
<!-- this is for debugging purposes -->
{{ addAccountForm.value | json }}
</form>
And this component is where I plan to display all the accounts from that array. The line that I suspect is not working is the
this.accounts = this.AccountCrudService.getAccounts();
line in the getAllAccounts() function. -
all-accounts.component.ts
#Component({
selector: 'app-all-accounts',
templateUrl: './all-accounts.component.html',
styleUrls: ['./all-accounts.component.scss'],
imports: [AccountCardComponent, RouterModule, CommonModule, AddAccountComponent, FormsModule],
providers: [AccountCrudService],
standalone: true
})
export class AllAccountsComponent {
accounts: Account[];
constructor(titleService:Title, public AccountCrudService: AccountCrudService) {
titleService.setTitle("Dashboard")
this.getAllAccounts();
}
//this is a debugging function
public printAccounts(){
console.log('accounts :');
console.log(this.AccountCrudService.getAccounts());
}
public getAllAccounts() {
//I want the value of accounts to be equal to the value of getAccounts()
this.accounts = this.AccountCrudService.getAccounts();
return this.accounts;
}
}
all-accounts.component.html
<div class="all-accounts-header">
<h1>All Accounts</h1>
<div class="all-accounts-options">
<button class="all-accounts-addBtn" routerLink="/add-account">Add</button>
</div>
</div>
<br>
<div class="all-accounts-cards">
<app-account-card [accounts]='accounts'></app-account-card>
</div>
I send this array to the account-card component where it uses an ngFor to display all the accounts. This functionality works when given a full array of accounts manually.
from account-card.component
<div *ngFor="let account of accounts" class="account-card-container">
<div class="account-card-name">
<div class="account-card-header">
<h2>{{ account.name }}</h2>
<div class="account-card-date">
<p></p>
</div>
</div>
</div>
And here is the .ts file.
account-card.component.ts
#Component({
selector: 'app-account-card',
templateUrl: './account-card.component.html',
styleUrls: ['./account-card.component.scss'],
imports: [AllAccountsComponent, CommonModule],
providers: [AccountCrudService],
standalone: true
})
export class AccountCardComponent {
#Input() accounts: any[];
}
When I console log the array in the add-account component, it displays my array just fine. But once I do it anywhere else, the whole array is gone.
I have used #Input() to try and input the array directly from the service, but that didn't work.
Any help is appreciated. Thanks!

Angular 6 asynchronous autocomplete not working but it displaying items without changing value

Angular 6 asynchronous autocomplete is not working but it displays items without changing value and does not reduce the list of proposed values
component.ts:
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormBuilder, Validators,FormControl} from '#angular/forms'
import { Observable } from 'rxjs';
import { startWith, map } from 'rxjs/operators';
export class AjouterMarcheComponent implements OnInit {
createForm: FormGroup;
myControl : FormControl;
directions: string[] = ['DGI','SSI','TTU','BLI'];
filteredDirections: Observable<string[]>;
constructor(private fb: FormBuilder) {
this.createForm = this.fb.group({
NomDirection: ['', Validators.required]})
this.myControl= new FormControl();
});
}
ngOnInit() {
this.filteredDirections = this.myControl.valueChanges.pipe(
startWith(''),
map(value => this._filter(value))
);
}
private _filter(value: string): string[] {
const filterValue = value.toLowerCase(); //miniscule
return this.directions.filter(direction => `direction.toLowerCase().indexOf(filterValue) === 0);
}
}
Thanks for your help
Compoment.html
compoment.html
<div>
<br>
<mat-card>
<br>
<form [formGroup]="createForm" class="create-form" >
<mat-form-field class="field-full-width">
<input placeholder="Choisir ou ajouter une direction" type="text" aria-label="Number" [formControl]="myControl" matInput [matAutocomplete]="auto" #NomDirection>
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of options" [value]="option">
{{ option }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</form>
</mat-card>
</div>

Angular form input value undefined

I'm trying to get the value of an input field in my first ever Angular form, but it is always undefined, and I can't figure out why. I'm importing FormsModule correctly, and I can reference the form object fine, so I must be missing something obvious.
My components HTML
<form #searchValue='ngForm' class="" (ngSubmit)='submitSearch(searchValue)'>
<div>
<input type="text" name="q" placeholder="search">
</div>
</form>
And my components ts method (Shortened)
import { Component, OnInit } from '#angular/core';
import { FormsModule } from '#angular/forms';
#Component({
selector: 'google-search',
templateUrl: './google.component.html',
styleUrls: ['./google.component.css']
})
export class GoogleComponent implements OnInit {
constructor() { }
ngOnInit() {
}
submitSearch(formData) {
console.log(this.searching);
console.log(formData.value.q);
}
}
Any ideas to why this is?
You need to mark the input with ngModel so angular will know that this is one of form's controls:
<input type="text" ngModel name="q" placeholder="search">
Or you can define the variable first in your component, and then bind the input to it via [(ngModel)] directive:
export class GoogleComponent implements OnInit {
q: string;
submitSearch() {
console.log(this.q);
}
}
<form class="" (ngSubmit)='submitSearch()'>
<div>
<input type="text" name="q" [(ngModel)]="q" placeholder="search">
</div>
</form>
One way binding (just [ngModel]="q") could be enough if you just want to read the value from input.
Some like this should work..
Maybe you want to read about model binding and forms.
html component
<form #searchValue='ngForm' class="some-css-class" (ngSubmit)='submitSearch()'>
<div>
<input type="text" name="q" [(ngModel)]="searchValue" placeholder="search">
<input type="submit" name="btn" placeholder="Submit">
</div>
</form>
In component.ts
import { Component, OnInit } from '#angular/core';
import { FormsModule } from '#angular/forms';
#Component({
selector: 'google-search',
templateUrl: './google.component.html',
styleUrls: ['./google.component.css']
})
export class GoogleComponent implements OnInit {
searchValue: string = '';
constructor() { }
ngOnInit() { }
submitSearch() {
console.log(this.searchValue);
}
}

Angular2: issues converting a form's value to a Model with default fields

I am busy with a Stock take form that needs to be posted to a REST API. I have a stockTake model which has three fields that get initialized with default values of 0. the three fields "StockTakeID, IDKey and PackSize" are not part of the angular form where the data needs to be entered. My issue is submitting the model with those three fields with default values to my RestService. When I submit the stockTakeForm.value I get an error as those three fields are not part of the data that's being submitted... Any idea how I can go about getting this to work?
my stock-take.model.ts:
export class StockTakeModel {
constructor(
StockTakeID: number = 0,
IDKey: number = 0,
BarCode: number,
ProductCode: string,
SheetNo: string,
BinNo: string,
Quantity: number,
PackSize: number = 0) { }
}
my stock-take.component.ts:
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { RestService } from '../../services/rest.service';
import { StockTakeModel } from '../../models/stock-take.model';
#Component({
selector: 'stock-take',
templateUrl: './stock-take.component.html',
styleUrls: ['./stock-take.component.css']
})
export class StockTakeComponent implements OnInit {
stockTakeForm: FormGroup;
constructor(private fb: FormBuilder, private restService: RestService) {
this.stockTakeForm = fb.group({
'sheetNo':['', Validators.required],
'binNo':['', Validators.required],
'barcode':['', Validators.required],
'Qty':['', Validators.required]
});
}
submitStockTake(stockTakeModel: StockTakeModel) {
//console.log(stockTakeModel);
this.restService.postStockTake(stockTakeModel)
.subscribe(
(res) => {
console.log(res);
},
(res) => {
console.log("failure: " + res);
}
);
this.stockTakeForm.reset();
}
ngOnInit() {
}
}
my stock-take.component.html:
<div class="row">
<div class="col-md-6 col-md-push-3">
<h1>Stock Take</h1>
<br /><br />
<form [formGroup]="stockTakeForm" role="form" (ngSubmit)="submitStockTake(stockTakeForm.value)">
<input type="text" placeholder="Enter Sheet Number" class="form-control" id="sheetNo" [formControl]="stockTakeForm.controls['sheetNo']" name="sheetNo">
<input type="text" placeholder="Enter Bin Number" class="form-control" id="binNo" [formControl]="stockTakeForm.controls['binNo']" name="binNo">
<input type="number" placeholder="Enter Barcode" class="form-control" id="bCode" [formControl]="stockTakeForm.controls['barcode']" name="barcode">
<input type="number" placeholder="Enter Quantity" clas="form-control" id="Qty" [formControl]="stockTakeForm.controls['Qty']" name="quantity">
<button class="btn btn-block" [disabled]="!stockTakeForm.valid">Submit</button>
</form>
</div>
</div>
updated stock-take.component:
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { RestService } from '../../services/rest.service';
import { StockTakeModel } from '../../models/stock-take.model';
#Component({
selector: 'stock-take',
templateUrl: './stock-take.component.html',
styleUrls: ['./stock-take.component.css']
})
export class StockTakeComponent implements OnInit {
stockTakeForm: FormGroup;
stockTakeModel: StockTakeModel;
constructor(private fb: FormBuilder, private restService: RestService) {
this.stockTakeForm = fb.group({
'sheetNo':['', Validators.required],
'binNo':['', Validators.required],
'barcode':['', Validators.required],
'Qty':['', Validators.required]
});
}
doStockTake(val: any) {
//console.log("val:" + JSON.stringify(val));
this.stockTakeModel = new StockTakeModel(0, 0, val[Object.keys(val)[2]], '', val[Object.keys(val)[0]], val[Object.keys(val)[1]], val[Object.keys(val)[3]], 0);
// console.log(this.stockTakeModel);
console.log(JSON.stringify(this.stockTakeModel));
}
submitStockTake(stockTakeModel: StockTakeModel) {
//console.log(stockTakeModel);
this.restService.postStockTake(stockTakeModel)
.subscribe(
(res) => {
console.log(res);
},
(res) => {
console.log("failure: " + res);
}
);
this.stockTakeForm.reset();
}
ngOnInit() {
}
}
One workaround would to create a new variable, e.g:
stockTakeModel: StockTakeModel;
if that doesn't work (as you said it complained about undefined), you can also instantiate it as an empty Object, like so:
stockTakeModel = {};
EDIT, so you changed your code to something like this: I also added some console.logs to show where it is undefined, as there was a little question about that.
stockTakeModel: StockTakeModel; // undefined at this point (or empty if you have defined it as empty above)!
// use Object keys to access the keys in the val object, a bit messy, but will work!
submitStockTake(val: any) {
this.stockTakeModel =
new StockTakeModel(0, 0, val[Object.keys(val)[2]], '', val[Object.keys(val)[0]],
val[Object.keys(val)[1]], val[Object.keys(val)[3]]);
// console.log(this.StockTakeModel); // now it should have all values!
this.restService.postStockTake(this.stockTakeModel)
.subscribe(d => { console.log(d)} ) // just shortened your code a bit
this.stockTakeForm.reset();
}
EDIT: So finally the last issue is solved, besides the solution above. It's because your StockTakeModel-class wasn't properly declared the value was undefined, or actually empty. So fix your class to:
export class StockTakeModel {
constructor(
public StockTakeID: number = 0, // you were missin 'public' on everyone
public IDKey: number = 0,
public BarCode: number,
public ProductCode: string,
public SheetNo: string,
public BinNo: string,
public Quantity: number,
public PackSize: number = 0) { }
}
Note also that in your 'updated' code you still have (ngSubmit)="submitStockTake(stockTakeForm.value) in your form, so your created doStockTake-method is not called.
I don't understand the construction [formControl] in your template.
There should be at least [ngModel].
Usualy I use, for two-way communication, the [(ngModel)]. Not the [formControl].
So your template code should look like this:
<div class="row">
<div class="col-md-6 col-md-push-3">
<h1>Stock Take</h1>
<br /><br />
<form [formGroup]="stockTakeForm" role="form" (ngSubmit)="submitStockTake(stockTakeForm.value)">
<input type="text" placeholder="Enter Sheet Number" class="form-control" id="sheetNo" [(ngModel)]="stockTakeForm.controls['sheetNo']" name="sheetNo">
<input type="text" placeholder="Enter Bin Number" class="form-control" id="binNo" [(ngModel)]="stockTakeForm.controls['binNo']" name="binNo">
<input type="number" placeholder="Enter Barcode" class="form-control" id="bCode" [(ngModel)]="stockTakeForm.controls['barcode']" name="barcode">
<input type="number" placeholder="Enter Quantity" clas="form-control" id="Qty" [(ngModel)]="stockTakeForm.controls['Qty']" name="quantity">
<button class="btn btn-block" [disabled]="!stockTakeForm.valid">Submit</button>
</form>
</div>
</div>

Latest angular2 form

I have a angular2 version debounce input control, template like below.
<input type="text" [ngFormControl]="compInput" placeholder="demo input" />
In my component.ts
import {Component} from "angular2/core";
import {Control} from "angular2/common";
#Component({
...
)
export class Demo{
private compInput = new Control();
constructor(){
this.compInput.valueChanges.subscribe(() => {});
}
}
these code works until I upgrade my angular2 version to latest.
It seems form usage has changed.
I changed [ngFormControl] to ngControl and Control to FormControl from "#angular/forms", but doesn't work.
Does anyone know where I am wrong about the new usage and how to fix?
Here's a simple example using ngModel
import {Component, Input, Output, HostListener, EventEmitter, ChangeDetectionStrategy} from '#angular/core';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
#Component({
moduleId: module.id,
selector: 'user-search',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<form role="search">
<div class="form-group">
<input
class="form-control"
name="input"
type="text"
placeholder="Search user"
[(ngModel)]="input"
(keyup)="keyup$.next($event.target.value)"
/>
</div>
</form>
`
})
export class UserSearchComponent {
input: string;
keyup$ = new Subject<string>();
#HostListener('window:keyup', ['$event'])
cancelSearch(event) {
if (event.code === 'Escape') {
this.input = undefined;
this.keyup$.next(undefined);
}
}
#Output() search: Observable<string> = this.keyup$
.debounceTime(700)
.distinctUntilChanged();
}
Thanks for all your help. I have find the answer for my question with help of my colleague. Here it is.
template.html
<form #form="ngForm">
<input name="nameInput" [(ngModel)]="componentName" #nameInput="ngModel">
</form>
component.ts
import {Component, ViewChild} from "#angular/core";
import {NgModel} from "#angular/common";
#Component({...})
export class Demo{
#ViewChild('nameInput') nameInput:NgModel;
componentName:string;
ngAfterViewInit(){
this.nameInput.update //or this.nameInput.control.valueChanges
.subscribe((val:any) => {console.log("value update", val);})
}
}