Angular form input value undefined - forms

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);
}
}

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/material autocomplete: options do not render

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>

How can I create my own component for FormControls?

I would like to create a form and use a new, custom component for its controls. So I created a new component and included it into the parent form. But although the parent form has a formGroup, Angular complains that it does not.
The error:
Error: formControlName must be used with a parent formGroup directive.
You'll want to add a formGroup
directive and pass it an existing FormGroup instance (you can create one in your class).
The parent form has:
<form [formGroup]="learningObjectForm" (ngSubmit)="onSubmit()" novalidate>
<div>
<button type="submit"
[disabled]="learningObjectForm.pristine">Save</button>
</div>
<ava-form-control [label]="'Resource'"></ava-form-control>
</form>
And in the .ts:
constructor(private fb: FormBuilder) {
this.createForm();
}
createForm() {
this.learningObjectForm = this.fb.group({
text: '',
});
}
In the custom component I have
import { Component, Input, OnInit } from '#angular/core';
#Component({
selector: 'ava-form-control',
template: ` <div>
<label>{{label}} :</label>
<input formControlName="{{name}}">
</div>
`
})
export class FormControlComponent implements OnInit {
#Input() label: string;
#Input() name: string;
constructor() {}
ngOnInit() {
if (this.name === undefined) {
// turns 'The Label' into 'theLabel'
this.name = this.label[0].toLowerCase().concat(this.label.slice(1));
this.name = this.name.split(' ').join('');
console.log(this.label, this.name);
}
}
}
You should also be passing the formGroup instance along with control name to your custom component. And then create a form control under that formGroup in custom component. Your custom component will create the control virtually under the same formGroup that you have provided.
<form [formGroup]="learningObjectForm" (ngSubmit)="onSubmit()" novalidate>
<div>
<button type="submit"
[disabled]="learningObjectForm.pristine">Save</button>
</div>
<ava-form-control [label]="'Resource'" [formGroup]="learningObjectForm" [controlName]="'mycontrol'"></ava-form-control>
</form>
custom.component.ts
import { Component, Input, OnInit } from '#angular/core';
#Component({
selector: 'ava-form-control',
template: ` <div>
<label>{{label}} :</label>
<input [formControl]="formGroup.controls[controlName]>
</div>
`
})
export class FormControlComponent implements OnInit {
#Input() label: string;
#Input() formGroup: FormGroup;
#Input() controlName: string;
constructor() {}
ngOnInit() {
let control: FormControl = new FormControl('', Validators.required);
this.formGroup.addControl(this.controlName, control);
}
}
With this your parent component can access all the form controls defined within their respective custom components.
I played around with the accepted answer for a long time, and never had any luck.
I had much better results implementing the ControlValueAccessor interface as shown here:
https://alligator.io/angular/custom-form-control/
It's actually pretty simple, I also rigged up an Example StackBlitz

Angular 2 form, get method istead of post

While working on Angular 2 form submit I run into a problem. When I create an object inside a component everything works well and my form gets submit via post method. But when I am using an object from a class outside the component my form sends a get request with url http://localhost:4200/blog?title=sss&content=ssssss
Does anyone know why this is happening?
Template:
<form (ngSubmit)="onSubmit()" #f="ngForm">
<!-- <form #f="ngForm" (ngSubmit)="onSubmit(f)">-->
<div class="form-group">
<label for="title">Tytuł</label>
<textarea class="form-control" id="title" rows="1"
ngModel name = "title" required minlength="3" #title="ngModel"></textarea>
<span class="help-block" *ngIf="!title.valid && title.touched">Wprowadzono za krótki tekst (minum to 3 znaki).</span>
<label for="content">Zawartość:</label>
<textarea class="form-control" id="content" rows="3"
ngModel name = "content" required minlength="3" #content="ngModel"></textarea>
<span class="help-block" *ngIf="!content.valid && content.touched">Wprowadzono za krótki tekst (minum to 3 znaki).</span>
</div>
<button type="submit" class="btn btn-primary"
[disabled] ="!f.valid"
>Wyślij</button>
</form>
Component:
import {Component, OnInit, ViewChild} from '#angular/core';
import {NgForm} from "#angular/forms";
import {Blog} from "../../shared/blog";
import {BlogService} from "../../services/blog.service";
#Component({
selector: 'app-blog-form',
templateUrl: './blog-form.component.html',
styleUrls: ['./blog-form.component.css']
})
export class BlogFormComponent implements OnInit {
#ViewChild('f') form: NgForm;
errorMessage: string;
/* this works well
blog = {
title: '',
content: '',
dateCreated: ''
}*/
//this doesn't work
blog: Blog;
ngOnInit(){}
onSubmit(){
this.blog.title = this.form.value.title;
this.blog.content = this.form.value.content;
}
}
The Blog class. I tried both this:
export class Blog {
constructor(public title = '', public content = '', public dateCreated = ''){}}
And this:
export class Blog {
constructor(public title : string, public content : string, public dateCreated : string){}}
Thanks for any help :)
I am not sure why this is happening but try not using this.form.value.
onSubmit(){
this.blog.title = this.form.title;
this.blog.content = this.form.content;
console.log(this.blog);
}
Use of value posts back your page. Now this code should work.

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);})
}
}