I don't know why the form is not working!
the input does not change the values in the form.
and the valueChanges is not detecting anything. any help?
applied on stackblitz:
https://stackblitz.com/edit/angular-ivy-w68v69?file=src/app/app.component.ts
import { Component, VERSION } from "#angular/core";
import { FormControl, FormGroup, Validators } from "#angular/forms";
#Component({
selector: "my-app",
template: `
<form [formGroup]="form">
<input
FormControlName="patientName"
placeholder="Enter Patient Name ..."
/>
</form>
`,
styleUrls: ["./app.component.css"]
})
export class AppComponent {
form: FormGroup;
ngOnInit() {
this.form = new FormGroup({
patientName: new FormControl("")
});
this.form.get("patientName").valueChanges.subscribe(x => console.log(x));
this.form.valueChanges.subscribe(next => {
console.log("valueChanges didtted");
console.log(next);
});
console.log("form created");
}
}
The property FormControlName in the HTML part should have the 'F' in lowercase : formControlName="patientName".
It you correct this, it works : https://stackblitz.com/edit/angular-ivy-cjntrr?file=src/app/app.component.ts
Related
I have the following code that should set the checked value to false on click:
#Component({
template: `
<input type="checkbox" [checked]="checked" (change)="onChange()">
`
})
export class AppComponent {
checked = false;
onChange() {
setTimeout(() => {
this.checked = false;
}, 1000)
}
}
The problem is that if we click on the input and we wait for a second, it'll stay checked. Why is this happening? Why Angular doesn't change it to false again?
Long story short Angular checkboxes are just broken : Issue
If you however want to achive this effect i will recommend you to create your own custom component that will act the same way as a checkbox.
Here is one more fun example of Checkbox madnes try to interact with both "With track by" and "No track by" blocks and see what happens.
I think you can do that easily by using [(ngModel)]="checked". Your code is working in the stackblitz link. Please check that. Here is my code given below.
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
template: `
<input type="checkbox" [checked]="checked" [(ngModel)]="checked" (change)="onChange()" name="horns">
<label for="horns">Horns</label>
`,
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
checked = false;
onChange() {
setTimeout(() => {
this.checked = false;
}, 2000)
}
}
in angular you can manipulate dom using view child and element ref
first of all you need to import viewchild and elementRef in your component
app.component.ts
import { Component, VERSION } from "#angular/core";
import { ViewChild, ElementRef } from "#angular/core";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
#ViewChild("checkBox") el: ElementRef;
onChange() {
setTimeout(() => {
this.el.nativeElement.checked = false;
}, 100);
}
}
app.component.html
<input type="checkbox" #checkBox (click)="onChange()">
stackblitz
If I have a component with a form input and I want to detect the two statements in the OnInit block as events inside my directive, what is the correct way to do this? I have had luck with 'input' and 'ngModelChange', but none of the events I try to listen to catch the patchValue() method for model driven forms or the straight assignment for template driven forms (even though it reflects in the DOM).
Here is my Component:
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormControl } from '#angular/forms'
#Component({
selector: 'my-app',
template:
`
<h5>Model form input</h5>
<form [formGroup]="inputForm">
<input patchable-input formControlName="input" />
</form>
<h5>Template form input</h5>
<input patchable-input [(ngModel)]="input" />
`
})
export class AppComponent implements OnInit {
inputForm = new FormGroup({
input: new FormControl('')
})
input = '';
ngOnInit() {
this.inputForm.patchValue({
input: 'testing patch'
})
this.input = 'testing override'
}
}
Here is my Directive:
import { Directive, HostListener } from '#angular/core';
#Directive({
selector: '[patchable-input]'
})
export class PatchableInputDirective {
#HostListener('ngModelChange', ['$event']) ngOnChanges($event) {
console.log($event);
}
}
and a minimal reproduction in StackBlitz (watch the console)
You have to implement AfterViewInit instead of OnInit. The reason for that is at this point in the life cycle your directive has been initialized and has subscribed to the ngModelChange event via the #HostListener decorator.
See also the Angular Life Cycle Hooks documentation.
stackblitz
import { Component, AfterViewInit } from '#angular/core';
import { FormGroup, FormControl } from '#angular/forms'
#Component({
selector: 'my-app',
template:
`
<h5>Model form input</h5>
<form [formGroup]="inputForm">
<input patchable-input formControlName="input" />
</form>
<h5>Template form input</h5>
<input patchable-input [(ngModel)]="input" />
`
})
export class AppComponent implements AfterViewInit {
inputForm = new FormGroup({
input: new FormControl('')
})
input = '';
ngAfterViewInit() {
this.inputForm.patchValue({
input: 'testing patch'
})
}
}
I have created a simple custom component in Angular 2 by implementing the CustomValueAccessor interface and it works fine. This component has just 1 input field in it. e.g. Postcode component
<postcode label="Post Code" cssClass="form-control" formControlName="postcode"> </postcode>
Now, I want to expand on this example and create an address component that has multiple input fields, line1, line 2, line3, postcode and country.
I have expanded the postcode example to include multiple fields and I can see the input component coming up on screen. However, the address component values are not being reflected in the host form.
Appreciate any pointer in this direction.
Example :
import { Component, OnInit, Input } from '#angular/core';
import { FormControl, FormGroup, ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR, Validators } from '#angular/forms';
import { Subscription } from 'rxjs/Subscription';
#Component({
selector: 'address',
templateUrl: './address.component.html',
styleUrls: ['./address.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: CommonAddressComponent,
multi: true
}
]
})
export class CommonAddressComponent implements OnInit , ControlValueAccessor {
addressForm : FormGroup
ngOnInit() {
this.addressForm = this.formBuilder.group({
line_1: '',
line_2: '',
});
}
/*private addressForm = new FormControl()*/
private subscription: Subscription;
public disabled: any = false;
constructor(private formBuilder: FormBuilder) { }
//model to view
writeValue(value: any) {
console.log("value = " + value)
this.addressForm.setValue(value);
}
registerOnChange(fn: (value: any) => void) {
console.log("registerOnChange = " + fn)
this.addressForm.valueChanges.subscribe(fn);
}
registerOnTouched() {}
}
Template file :
<div class="form-group" [formGroup]="addressForm">
<input class="form-control" type="text" formControlName="line_1" />
<input class="form-control" type="text" formControlName="line_2" />
</div>
Host Form component file:
import { Component, OnInit } from '#angular/core';
import { FormControl, FormGroup, Validators, FormBuilder} from '#angular/forms';
#Component({
selector: 'app-contacts',
templateUrl: './contacts.component.html',
styleUrls: ['./contacts.component.scss']
})
export class ContactsComponent implements OnInit {
contactsForm: FormGroup;
constructor(private fb: FormBuilder) {
this.createForm();
}
createForm() {
this.contactsForm = this.fb.group({
name: 'test', // <--- the FormControl called "name"
postcode: 'tester111', // <--- the FormControl called "name"
line_3: '111', // <--- the FormControl called "name"*/
addressForm: new FormGroup({
line_1: new FormControl('I am line 1', Validators.minLength(2)),
line_2: new FormControl('I am line 2')
}),
});
}
ngOnInit() {
}
}
Host Form Component Template file:
<form [formGroup]="contactsForm">
<p>Form value: {{ contactsForm.value | json }}</p>
<p>Form status: {{ contactsForm.status | json }}</p>
<div class="form-group">
<label class="center-block">Name:
<input class="form-control" formControlName="name">
</label>
</div>
<div>
<postcode label="Post Code" cssClass="form-control" formControlName="postcode"> </postcode>
</div>
<div class="form-group">
<address-line cssClass="form-control" name="line3" label="line 3" elementName="line3"
elementID="line3" formControlName="line_3"> </address-line>
</div>
<!--<div [formGroup]="contactsForm.addressForm"> -->
<div >
<address formGroupName="addressForm"> </address>
</div>
</form>
After multiple attempts, I was able to get a custom control with multiple input fields in Angular working. Code for the same goes as follows:
Custom component with multiple input fields
import { Component, OnInit, Input, ViewChild } from '#angular/core';
import { FormControl,NgForm, FormGroup, ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR, Validators, NgModel } from '#angular/forms';
import { Subscription } from 'rxjs/Subscription';
#Component({
selector: 'address',
templateUrl: './address.component.html',
styleUrls: ['./address.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: AddressComponent,
multi: true
}
]
})
export class AddressComponent implements OnInit , ControlValueAccessor {
addressForm : FormGroup
#Input() label: string;
constructor(private formBuilder: FormBuilder) { }
ngOnInit() {
this.addressForm = this.formBuilder.group({
line1: '',
line2: '',
line3: '',
line4: '',
});
}
writeValue(value: any) {
if (value) {
this.addressForm.setValue(value);
}
}
registerOnChange(fn: (value: any) => void) {
console.log("registerOnChange = " + fn)
this.addressForm.valueChanges.subscribe(fn);
}
registerOnTouched() {}
}
Template of Custom Component
Template Code
<div class="form-group" [formGroup]="addressForm">
<input type="text" name="line1" class="form-control" type="text" formControlName="line1" />
<input type="text" name="line2" class="form-control" type="text" formControlName="line2" />
<input type="text" name="line3" class="form-control" type="text" formControlName="line3" />
<input type="text" name="line4" class="form-control" type="text" formControlName="line4" />
</div>
Host or Parent Component class
import { Component } from '#angular/core';
import { NgForm, Validators, FormControl, FormGroup } from '#angular/forms';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
pageForm: FormGroup = new FormGroup({
address: new FormGroup({
line1: new FormControl('',Validators.required),
line2: new FormControl('',Validators.required),
line3: new FormControl('',Validators.required),
line4: new FormControl('')
}),
})
}
4.
<div class="container">
<form [formGroup]="pageForm">
<p>Form value: {{ pageForm.value | json }}</p>
<p>Form status: {{ pageForm.status | json }}</p>
<address label="Line 1" formControlName="address" > </address>
</form>
</div>
Note that Host or Parent Component class must declare the "address" field as a FormControl, not a FormGroup:
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
pageForm: FormGroup = new FormGroup({
address: new FormControl({
line1: new FormControl('',Validators.required),
line2: new FormControl('',Validators.required),
line3: new FormControl('',Validators.required),
line4: new FormControl('')
}),
})
}
I have a custom Validator to check for spaces in input field, but I cant understand why input field is being undefined to the constructor.
CustomValidationComponent:
import {Component} from '#angular/core';
import {FormControl, FormGroup, FormBuilder} from '#angular/forms';
import {UsernameValidators} from './usernameValidators';
#Component({
selector: 'custom-validation',
template: `
<div [formGroup]="usernameGroup">
<input type="text" placeholder="Name" formControlName="username">
<div *ngIf="username.errors.cannotContainSpace"> Without spaces! </div>
</div>
`
})
export class CustomValidationFormComponent {
usernameGroup: FormGroup;
constructor(fb: FormBuilder){
this.usernameGroup = fb.group({
username: ['', UsernameValidators.cannotContainSpace],
});
}
UsernameValidator:
import {FormControl} from '#angular/forms';
export class UsernameValidators {
static cannotContainSpace(control: FormControl){
if (control.value.indexOf(' ') >= 0)
return { cannotContainSpace: true };
return null;
}
}
PLUNKER
Your username formControl is not a class variable to access directly. You can access it through the FormGroup which is userNameGroup.
<div *ngIf="usernameGroup.controls['username'].errors?.cannotContainSpace">
Or as Manuel Zametter mentioned:
<div *ngIf="usernameGroup.controls.username.errors?.cannotContainSpace">
The ?. is safe navigation operator which does a check on errors if it is undefined or null.
Plunker
I'm building a standard angular2 app(more specific a todo app). I also added #Ngrx/store.
The first time I load the page, the button is disabled, but when I enter some value in the input box, the button needs to be enabled, but it stays disabled...
app/app.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
template: `
<h1>Hello {{name}}</h1>
<div>
<add-todo></add-todo>
</div>`,
})
export class AppComponent { name = 'Angular'; }
app/components/add-todo/add-todo.component.ts
import { Component, ViewChild, ElementRef } from '#angular/core';
#Component({
moduleId: module.id,
selector: 'add-todo',
template: `
Create new todo
<input #myInput />
<button (click)="addTodo()" [disabled]="!myInput.value">Add</button>`
})
export class AddTodoComponent {
#ViewChild('myInput') input: ElementRef;
constructor() {}
addTodo(): void {
alert(this.input.nativeElement.value);
}
}
From the documentation https://angular.io/docs/ts/latest/guide/forms.html Track the change state and validity of form controls using ngModel...
Update your code to use ngModel and it should work as expected
import { Component, ViewChild, ElementRef } from '#angular/core';
#Component({
moduleId: module.id,
selector: 'add-todo',
template: `
Create new todo
<input #myInput [(ngModel)]="inputFieldValue" />
<button (click)="addTodo()" [disabled]="!myInput.value">Add</button>`
})
export class AddTodoComponent {
#ViewChild('myInput') input: ElementRef;
public inputFieldValue:string = '';
constructor() {}
addTodo(): void {
alert(this.input.nativeElement.value);
}
}
What if you do this way:
import {Component} from '#angular/core';
#Component({
selector: 'add-todo',
template: `
Create new todo
<input #todoTitleInput [(ngModel)]="todoTitle"/>
<button (click)="addTodo(todoTitleInput.value)" [disabled]="!todoTitle">Add</button>
`,
})
export class AddTodoComponent {
addTodo(title: string): void {
alert(title);
}
}