Can someone help me set the values of a model driven form for data coming through http.
Here is the excerpt from service.ts
getExternalConnection(row_id){
return this._http.get(this._url + '/' + row_id)
.map(res=> res.json());
}
Here is the excerpt from component.ts
constructor(private _externalConnectionListService: ExternalConnectionListService,
private _route: ActivatedRoute, fb: FormBuilder
) {
this.externalConnectionForm = fb.group({
row_id : [''],
name: ['', Validators.required]
});
}
ngOnInit(): void {
this.id = this._route.snapshot.params["row_id"];
this._externalConnectionListService.getExternalConnection(this.id)
.subscribe( res=> {this.data = res;
this.externalConnectionForm.patchValue(this.data);
});
With this code I dont see any initial values in the form. However if I replace this.data in patchValue call manually with the entire string returned by the observable after removing the square brackets [], the form is populated fine.
How do I deal with this? This means patchValue is working fine, it just does not like the format of the input from this.data.
Related
I am new to Ionic and trying to pass api from providers to application's ts page but I am getting an error, maybe I am passing the wrong id.
TS part:
export class CardsPage {
currentItems: Item[];
id: any;
getData: Object;
categories;
constructor(public navCtrl: NavController, public api:Api, navParams:
NavParams, items: Items, public http: HttpClient) {
this.id = navParams.get('idName') ||'';
console.log(this.id);
this.api.getCategoryPosts(this.id).subscribe(data=>{
console.log(data)
this.getData = data
},err=>{
console.log(err)
})
}
openItem(item){
this.navCtrl.push('ItemDetailPage', {
itemName: item
});
}
}
Api:
getCategoryPosts(category: any) {
return this.http.get(`${this.api_url}/posts?categories=${category.id}`);
}
I have posted the code part about my API and ts file now I want to pass data to next page using parameters. I wanted to know what should I pass in parameter to get data displayed in next page
According to your code, You are passing id directly to the Service's getCategoryPosts method. Therefore use category instead of category.id as below.
getCategoryPosts(category: any) {
return this.http.get(`${this.api_url}/posts?categories=${category}`);
}
I am trying to implement async validator in my Reactive Form in angular 4.3.4 which will check that entered email exists or not in your system.
but this does not work properly, earlier it was invoking on every key up so I made some changes and make it Observable now only Checking after a given debounce time. checking...' text is displaying but the response comes but no error is being displayed on the page.
what can be the issue? I have very base knowledge of Observable and angular 4. please help me what is the issue. I have checked in the console and it is going and print the value in the asyncvalidator function.
here is the relevant code.
signup.component.html
<form [formGroup]="myForm" novalidate #formDir="ngForm" (ngSubmit)="doSignup()">
<input type="email" formControlName="email" pattern="{{email_pattern}}"/>
<div [hidden]="myForm.controls.email.valid || myForm.controls.email.pristine" class="text-danger">
<div *ngIf="myForm.controls.email.required">Please enter Email</div>
<div *ngIf="myForm.controls.email.pattern">Invalid Email</div>
<div *ngIf="myForm.controls.email.status === 'PENDING'">
<span>Checking...</span>
</div>
<div *ngIf="myForm.controls.email.errors && myForm.controls.email.errors.emailTaken">
Invitation already been sent to this email address.
</div>
</div>
<button type="submit" [disabled]="!myForm.valid">Invite</button>
</form>
signup.component.ts
import { FormBuilder, FormGroup, Validators, FormControl } from '#angular/forms';
import { ValidateEmailNotTaken } from './async-validator';
export class SignupComponent implements OnInit {
public myForm: FormGroup;
constructor(
private httpClient: HttpClient,
private fb: FormBuilder
) {
}
ngOnInit(): void {
this.buildForm();
}
private buildForm() {
this.inviteForm = this.fb.group({
firstname: [''],
lastname: [''],
email: [
'',
[<any>Validators.required, <any>Validators.email],
ValidateEmailNotTaken.createValidator(this.settingsService)
]
});
}
asyn-validator.ts
import { Observable } from 'rxjs/Observable';
import { AbstractControl } from '#angular/forms';
import { UserService } from './user.service';
export class ValidateEmailNotTaken {
static createValidator(service: UserService) {
return (control: AbstractControl): { [key: string]: any } => {
return Observable.timer(500).switchMapTo(service.checkEmailNotTaken(control.value))
.map((res: any) => {
const exist = res.item.exist ? { emailTaken: true } : { emailTaken: false };
console.log('exist: ', exist);
return Observable.of(exist);
})
.take(1);
};
}
}
user.service.ts
checkEmailNotTaken(email) {
const params = new HttpParams().set('email', email);
return this.httpClient.get(`API_END_POINT`, {
headers: new HttpHeaders({
'Content-type': 'application/json'
}),
params: params
});
}
You use Observable.timer(500) without a second argument, so after 500 milliseconds, it completes and never runs again. So first thing to do is to pass that argument - Observable.timer(0, 500).
switchMapTo cancels its previous inner Observable (service.checkEmailNotTaken(control.value) in your case) every time source Observable emits new value (so every 500 milliseconds). So if your http request lasts longer, you wont get its response. Thats why usually switchMap and switchMapTo are not suitable for http requests.
Here is an illustration:
const source = Rx.Observable.timer(0, 500);
const fail = source.switchMapTo(Rx.Observable.of('fail').delay(600))
const success = source.switchMapTo(Rx.Observable.of('success').delay(400))
const subscribe = fail.subscribe(val => console.log(val));
const subscribe2 = success.subscribe(val => console.log(val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"></script>
So you should pick another flattening operator, like flatMap:
const source = Rx.Observable.timer(0, 500);
const success = source.flatMap(()=>Rx.Observable.of('success').delay(600))
const subscribe = success.subscribe(val => console.log(val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"></script>
I know its too late for the answer but anyone facing same issue might find it useful:
apart from above answer the AsyncValidatorFn should return Promise<ValidationErrors | null> | Observable<ValidationErrors | null>.
Return value of ValidationErrors | null isn't correct.
Check out official docs
In an Angular 4 application, how can I validate two fields of a form doing a comparison?
For example, let's suppose that my form has a startDate and an endDate date fields and I want to make sure that the endDate must be bigger than the startDate.
When you want to implement validations containing one or more sibling (form)controls, you have to define the validator function on a level up/above that of the sibling controls. For ex:
ngOnInit() {
this.form = this.formbuilder.group({
'startDate': ['', [<control-specific - validations >]],
'endDate': ['', [<control-specific - validations >]]
}, { validator: checkIfEndDateAfterStartDate });
}
Then outside the component class's definition (in the same file), define the function checkIfEndDateAfterStartDate as well.
export function checkIfEndDateAfterStartDate (c: AbstractControl) {
//safety check
if (!c.get('startDate').value || !c.get('endDate').value) { return null }
// carry out the actual date checks here for is-endDate-after-startDate
// if valid, return null,
// if invalid, return an error object (any arbitrary name), like, return { invalidEndDate: true }
// make sure it always returns a 'null' for valid or non-relevant cases, and a 'non-null' object for when an error should be raised on the formGroup
}
This validation will make the FormGroup invalid by adding the error-flag (here invalidEndDate) to true to the errors object for that FormGroup. If you want to have specific errors to be set on any of the sibling controls instead, then you can manually set the error flags on that formControl by using something like, c.get('endDate').setErrors({ invalidEndDate: true }). If you do this, then make sure you clear them for a valid case by setting the errors to null like this, c.get('endDate').setErrors(null).
A live demo of a similar validation can be seen here.
try this
export class validationComponent implements OnInit {
private testForm:FormGroup;
constructor(private fb: FormBuilder) {
}
ngOnInit() {
this.testForm = this.fb.group({
'startDate': ['', [Validators.required]],
'endDate': ['', [Validators.required]]
});
this.subscribeDateChanges();
}
subscribeDateChanges() {
const startDateChanges = (<any>this.testForm).controls.startDate.valueChanges;
const endDateChanges = (<any>this.testForm).controls.endDate.valueChanges;
startDateChanges.subscribe(start => {
this.testForm.controls['endDate'].
setValidators(
[Validators.required,
CustomValidators.minDate(this.toYYYYMMDD(start))]);
this.validateDates();
});
endDateChanges.subscribe(end => {
this.validateDates();
});
}
dateError: boolean = false;
validateDates(): void{
let startDate = this.testForm.controls['startDate'].value;
let endDate = this.testForm.controls['endDate'].value;
if(endDate && startDate){
this.dateError = endDate <= startDate;
}
}
toYYYYMMDD(d:Date): string {
d = new Date(d)
var yyyy = d.getFullYear().toString();
var mm = (d.getMonth() + 101).toString().slice(-2);
var dd = (d.getDate() + 100).toString().slice(-2);
return yyyy + '-' + mm + '-' + dd;
}
based on the dateError boolean value you show error msg
Trying to create a new form group inside of the existing form group in Angular 2. I define the main form in one of the component with the empty formGroup "type". I pass that formGroup "type" to another component. In that component I want to create a new formGroup with name "time", but it doesn't work. I didn't find any method in the FormGroup class for creating a new group. There is just methods for Controls for adding them to the FormGroup.
addControl method works but the next one not.
this.typeForm.addControl("gender", new FormControl('', Validators.required));
this.typeForm['time'] = this.fb.group({});
The FormBuilder should create something like:
{
"name": "",
"type": {
"gender": "",
"time": {
}
}
}
Any idea how can I do that?
I think you can try to do something like this:
ngOnInit() {
this.form = this.fb.group({
name: [''],
type: this.typeOptions()
});
}
typeOptions(): FormGroup {
return this.someService.getTypes().subscribe(
res => {
this.options = res.body;
let obj: any = {};
for (let option of options) {
let control: FormControl = new FormControl(option.value || '');
obj[option.title] = control;
}
return new FormGroup(obj);
}
);
}
Let's say I have this model:
export class MyModel {
constructor(
public id: number,
public name: string
) {}
}
and this ControlGroup:
export class MyComponent {
form: ControlGroup;
model: MyModel;
constructor(builder: FormBuilder) {
this.form = this.builder({
'id' : [''],
'name' : ['']
})
}
}
To get form's data I can simply do that (if field names match):
this.model = this.form.value;
But how can I set form's value in the same manner?
something like: this.form.value = model;
Getting the following error:
Cannot set property value of #<AbstractControl> which has only a getter
Thank you!
UPD: Based on Günter Zöchbauer's suggestion below I ended up with that helper method:
setFormValues(form: ControlGroup, model: any) {
for(var key in model) {
var ctrl = (<Control>form.controls[key]);
if ( ctrl != undefined )
ctrl.updateValue(model[key]);
}
}
The ControlGroup returned from this.builder.group(...) doesn't support to set the value. To set the value you have to set it on each control individually like:
setValue() {
let value = {id: 'xxx', name: 'yyy'};
Object.keys(value).forEach((k) => {
this.form.controls[k].updateValue(value[k]);
});
}
Plunker example