I have two inputs, each populated by their own ng-bootstrap datepicker that pops up when I click a button. The input populates properly when you choose a new date, but my problem is if I want to initialize the inputs to null or simply another value. I know my getDate function gets in the way of that, I'm just not sure how to change it to allow that.
Screenshot (datepicker opens with click of the button to the right of each input, one per input):
HTML:
<form class="form-inline">
<div>
<div class="form-group" [ngClass]="{'has-error':!secondForm.controls['startDate'].valid && secondForm.controls['startDate'].touched}">
<label>Start Date:</label>
<input style="width:250px" [value]="getDate('start')" class="form-control" type="text" [formControl]="secondForm.controls['startDate']">
</div>
<div style="display:inline-block">
<ngb-datepicker id="special" *ngIf="startCheck;" [(ngModel)]="startDate" (ngModelChange)="showDatePick(0)" [ngModelOptions]="{standalone: true}"></ngb-datepicker>
</div>
<button type="button" class="btn icon-calendar" (click)="showDatePick(0)"></button>
<div class="form-group" [ngClass]="{'has-error':!secondForm.controls['endDate'].valid && secondForm.controls['endDate'].touched}">
<label>End Date:</label>
<input style="width:250px" [value]="getDate('end')" class="form-control" type="text" [formControl]="secondForm.controls['endDate']">
</div>
<div style="display:inline-block">
<ngb-datepicker id="special" *ngIf="endCheck;" [(ngModel)]="endDate" (ngModelChange)="showDatePick(1)" [ngModelOptions]="{standalone: true}"></ngb-datepicker>
</div>
<button type="button" class="btn icon-calendar" (click)="showDatePick(1)"></button>
<button type="submit" class="btn icon-search" [disabled]="!secondForm.valid"></button>
<span [hidden]="!secondForm.hasError('endDateLessThanStartDate')" class="alert alert-danger first">End Date must be equal to or after Start Date</span>
</div>
</form>
Typescript:
import { Component } from '#angular/core';
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
import {NgbDateStruct} from '#ng-bootstrap/ng-bootstrap';
import {DatePipe} from "#angular/common";
#Component({
selector: 'calendar-pick',
styleUrls: ['../app.component.css'],
templateUrl: './calendarpick.component.html',
providers: [DatePipe]
})
export class CalendarPickComponent {
public dt: NgbDateStruct;
public dt2: NgbDateStruct;
public startCheck: boolean = false;
public endCheck: boolean = false;
secondForm : FormGroup;
public constructor(fb: FormBuilder, private datePipe: DatePipe) {
this.secondForm = fb.group({
'startDate' : [null, Validators.required],
'endDate' : [null, Validators.required]
}, {validator: this.endDateAfterOrEqualValidator})
}
public getDate(dateName: string) {
let workingDateName = dateName + 'Date';
let timestamp = this[workingDateName] != null ? new Date(this[workingDateName].year, this[workingDateName].month-1, this[workingDateName].day).getTime() : new Date().getTime();
this.secondForm.controls[dateName + 'Date'].setValue(this.datePipe.transform(timestamp, 'MM/dd/yyyy'));
}
public showDatePick(selector):void {
if(selector === 0) {
this.startCheck = !this.startCheck;
} else {
this.endCheck = !this.endCheck;
}
}
endDateAfterOrEqualValidator(formGroup): any {
var startDateTimestamp, endDateTimestamp;
for(var controlName in formGroup.controls) {
if (controlName.indexOf("startDate") !== -1) {
startDateTimestamp = Date.parse(formGroup.controls[controlName].value);
}
if (controlName.indexOf("endDate") !== -1) {
endDateTimestamp = Date.parse(formGroup.controls[controlName].value);
}
}
return (endDateTimestamp < startDateTimestamp) ? { endDateLessThanStartDate: true } : null;
}
}
Changed second line in getDate function to let timestamp = this[workingDateName] != null ? new Date(this[workingDateName].year, this[workingDateName].month - 1, this[workingDateName].day).getTime() : null;
Related
I'm working on an update task, I have a list of objects displayed in datatable and I want to execute the update process with a modal contain a form with an input and select options, when click the button to display the form, the input take the first attribute of my objectn but the problem is that the select option does not take a default value which is should be the second attribute of object !
<a data-toggle="modal" [attr.data-target]="'#modal-centered' + index"><i class="la la-pencil edit"
(click)="patchValue(rowData)"></i></a>
<div [attr.id]="'modal-centered' + index" class="modal fade">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{rowData.name}}</h4>
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">×</span>
<span class="sr-only">Fermer</span>
</button>
</div>
<div class="modal-body">
<form [formGroup]="updateForm">
<div class="form-group">
<input type="text" formControlName="name" placeholder="Nom de la commission"
class="form-control">
</div>
<div class="form-group">
<select name="president" class="col-lg-6 custom-select form-control rounded"
formControlName="president">
<option [ngValue]="null" disabled>Choisir un Président</option>
<option [value]="president._id" *ngFor="let president of presidents">
{{president.firstName}}
{{president.lastName}}</option>
</select>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal"
(click)="updateCommission(rowData._id)">Sauvegarder</button>
</div>
</div>
</div>
</div>
import { Component, OnInit } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { FormGroup, FormControl } from '#angular/forms';
#Component({
selector: 'app-commission',
templateUrl: './commission.component.html',
styleUrls: ['./commission.component.css']
})
export class CommissionComponent implements OnInit {
commissions: any[];
updateForm: FormGroup;
presidents: any[];
cols: any[];
loading: boolean = true;
constructor(
private http: HttpClient
) {
this.updateForm = new FormGroup({
name: new FormControl(''),
president: new FormControl('')
});
}
ngOnInit() {
this.http.get('/api/commissions').subscribe((commissions: any[]) => {
this.loading = false
this.commissions = commissions;
})
this.http.get('/api/users/byType/conseillerMunicipal').subscribe((presidents: any[]) => {
this.presidents = presidents;
})
this.cols = [
{ field: 'name', header: 'Nom' },
{ field: 'presidentFullName', header: 'President' },
];
}
updateCommission(commissionID) {
this.loading = true;
this.http.put('/api/commissions/' + commissionID, this.updateForm.value).subscribe(updatedCommission => {
this.loading = false;
this.commissions.filter(commission => commission._id === commissionID)[0] = updatedCommission;
})
}
patchValue(commission) {
debugger
this.updateForm.setValue({
name: commission.name,
president: commission.presidentFullName
})
}
}
This does the job and displays the presidents in a drop down. However, I also need to select the president of the object to updated by default .
Try this one:
this.updateForm = new FormGroup({
name: new FormControl(''),
president: new FormControl(null)
});
Accoring to your condition inner template, you compare option with null. But your default value for formControl is ''.
I have few checkboxes whose values are coming from loop,Here I am validating those checkboxes using reactive form.My validation is atleast one checkboxes should be selected.when I check and uncheck the checkbox validation is working fine,but when my all checkboxes are already preselected and click submit,even though its showing empty message.Is there any solution for it.Here is the code below.
home.component.html
<div>
<p>Form 1</p>
<form [formGroup]="registerForm">
<div *ngFor="let grpdata of statusdata">
<input type="checkbox" formControlName="title" value="{{grpdata.groupid}}" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.title.errors }">{{grpdata.groupname}}<br>
</div>
<div *ngIf="submitted && f.title.errors" class="invalid-feedback">
<div *ngIf="f.title.errors.required">Title is required</div>
</div>
<button type="submit" (click)="getSelectedVal()">Click here</button>
</form>
</div>
<div>
<p>Form 2</p>
<form [formGroup]="editForm">
<input type="textbox" disabled formControlName="edithidden" [(ngModel)]="hello" class="form-control"><br>
<div *ngFor="let grpdata of statusdata">
<input type="checkbox" formControlName="edittitle" [checked]=true value="{{grpdata.groupid}}" class="form-control" [ngClass]="{ 'is-invalid': submitted1 && g.edittitle.errors }">{{grpdata.groupname}}<br>
</div>
<div *ngIf="submitted1 && g.edittitle.errors" class="invalid-feedback">
<div *ngIf="g.edittitle.errors.required">Title is required</div>
</div>
<button type="submit" (click)="editSelectedVal()">Click here</button>
</form>
</div>
home.component.ts
import { Component, OnInit } from '#angular/core';
import { CommonserviceService } from './../utilities/services/commonservice.service';
import { FormBuilder, FormControl, FormGroup, Validators } from '#angular/forms';
declare var $: any;
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
submitted = false;
submitted1 = false;
getListData: any;
registerForm: FormGroup;
editForm: FormGroup;
statusdata: any;
constructor(private commonserviceService: CommonserviceService,private formBuilder: FormBuilder)
{
this.registerForm = this.formBuilder.group({
title: [false, Validators.requiredTrue],
});
this.editForm = this.formBuilder.group({
edittitle: [false, Validators.requiredTrue],
edithidden: new FormControl()
});
}
ngOnInit() {
this.statusdata = [{"groupid":1,"groupname":"project1"},{"groupid":2,"groupname":"project2"},{"groupid":3,"groupname":"project3"}];
}
get f() { return this.registerForm.controls; }
get g() { return this.editForm.controls; }
getSelectedVal(){
this.submitted = true;
// stop here if form is invalid
if (this.registerForm.invalid) {
return;
}
console.log('submitted');
}
editSelectedVal(){
this.submitted1 = true;
// stop here if form is invalid
if (this.editForm.invalid) {
return;
}
console.log('submitted edit');
}
}
<input type="checkbox" formControlName="edittitle" [checked]=true...
You shouldn't try to set the value from outside of the form. You never know when it is actually attached. When you want to have the checkbox to be preselected use the form value instead.
this.editForm = this.formBuilder.group({
edittitle: [true, Validators.requiredTrue], // true here, you had false here
edithidden: new FormControl()
});
I am following the tutorial:
https://jasonwatmore.com/post/2018/10/29/angular-7-user-registration-and-login-example-tutorial
and write a login form according to this by using ReactiveFormsModule in Angular 7
However, after I click on submit with wrong information I just see alert at top for a second and page refreshes itself. I searched a lot about this topic and changing button type does not help.
Here is my HTML code:
<div>
<h2 align="center">Login</h2>
<form [formGroup]="loginForm">
<div class="form-group">
<label for="email">E-mail</label>
<input type="text" formControlName="email" class="form-control" placeholder="your_email#example.com" [ngClass]="{ 'is-invalid': submitted && f.email.errors }" />
<div *ngIf="submitted && f.email.errors" class="invalid-feedback">
<div *ngIf="f.email.errors">Invalid e-mail</div>
</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" formControlName="password" placeholder="******" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.password.errors }" />
<div *ngIf="submitted && f.password.errors" class="invalid-feedback">
<div *ngIf="f.password.errors" >Invalid password</div>
</div>
</div>
<div class="form-group">
<button (click)="onSubmit()" [disabled]="loading || !loginForm.controls.email.value || !loginForm.controls.password.value" class="btn btn-primary">Login</button>
<img *ngIf="loading" class="pl-2" src="data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==" />
</div>
<a routerLink="/register" class="go-register">Do you need an account?</a>
</form>
</div>
and TypeScript file:
import { Component, OnInit , OnDestroy} from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { FormBuilder, FormGroup, Validators} from '#angular/forms';
import { first } from 'rxjs/operators';
import { AuthenticationService } from '../services/auth.service';
import { AlertService } from '../services/alert.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
loginForm: FormGroup;
loading = false;
submitted = false;
returnUrl: string;
error = '';
constructor(
private formBuilder: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private authenticationService: AuthenticationService,
private alertService: AlertService
) { }
ngOnInit() {
this.loginForm = this.formBuilder.group({
email: ['', [Validators.email, Validators.required]],
password: ['', [Validators.pattern('^(?=[^A-Z]*[A-Z])(?=[^a-z]*[a-z])(?=[^0-9]*[0-9]).{6,}$'),
Validators.minLength(6),
Validators.required]]
});
// reset login status
this.authenticationService.logout();
// get return url from route parameters or default to '/'
this.returnUrl = this.route.snapshot.queryParams.returnUrl || '/';
}
// convenience getter for easy access to form fields
get f() { return this.loginForm.controls; }
onSubmit() {
this.submitted = true;
// stop here if form is invalid
if (this.loginForm.invalid) {
return;
}
this.loading = true;
this.authenticationService.login(this.f.email.value, this.f.password.value)
.subscribe(
data => {
this.router.navigate([this.returnUrl]);
},
error => {
this.error = error;
// alert(error);
this.alertService.error(error);
this.loading = false;
});
}
}
I debug my code and I guess problem is about HTML but I cannot resolve.
Also, I tried add event.preventDefault() and (ngSubmit), none of them helped me.
I am open for any ideas...
https://angular.io/guide/reactive-forms#saving-form-data
You have to use ngSubmit...
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
Then the default behaviour is disabled and angular handles it.
Can someone suggest how to display a pop-up menu suggestions with past queries only after the input field search-form__input has been selected?
search.component.html
<div class="search-form mt-2 input-group mb-2">
<input appShowSuggestions type="search" class="form-control search-form__input"
placeholder="Поиск (макс. количество найденных статей - 30)" maxlength="60"
[(ngModel)]="searchQuery"
(keyup)="showArticlesKeyup($event)"
[formControl]="typeahead"
(input)="suggest()" >
<div class="input- group-append">
<button class="btn btn-outline-secondary search-form__btn" type="submit"
(click)="showArticles()" [disabled]="searchQuery.trim() === ''">Искать</button>
</div>
</div>
<div class="suggestions" *ngIf="suggestions.length">
<small class="text-muted">Ваши прошлые запросы:</small>
<p class="suggestions__link"
*ngFor="let suggest of suggestions"
(click)="searchQuery = suggest; showArticles()"> {{ suggest }}</p>
</div>
search.component.ts
export class SearchComponent implements OnInit {
constructor(private articleService: ArticleService) { }
searchQuery = '';
articles: any[] = [];
searchQueryCollection: string[] = [];
suggestions: string[] = [];
typeahead: FormControl = new FormControl();
static getUrl(searchQuery: string) {
return 'https://ru.wikipedia.org/w/api.php?action=opensearch&profile=normal&search='
+ searchQuery + '&limit=30&namespace=0&format=json&origin=*';
}
suggest() {
this.suggestions = Array.from(new Set(this.searchQueryCollection))
.filter(c => c.startsWith(this.typeahead.value))
.slice(0, 5);
}
ngOnInit() {
}
showArticles() {
this.articleService.getArticles(SearchComponent.getUrl(this.searchQuery))
.subscribe(
(data: IArticle) => {
this.articles = Object.values({ ...data });
this.searchQueryCollection.push(this.searchQuery);
}
);
}
showArticlesKeyup(event) {
if (event.key === 'Enter' && this.searchQuery.trim() !== '') {
this.showArticles();
}
}
}
<input appShowSuggestions type="search" class="form-control search-form__input"
placeholder="Поиск (макс. количество найденных статей - 30)" maxlength="60"
[(ngModel)]="searchQuery"
(keyup)="showArticlesKeyup($event)"
[formControl]="typeahead"
(input)="suggest()" >
One suggestion would be to add (focus)="toggleSuggestions()" and then toggle suggestions would look like this:
toggleSugestions = () => this.showSuggestions = !this.showSuggestions;
You would then change your ng if to something like:
<div class="suggestions" *ngIf="showSuggestions && suggestions.length">
I followed Angular Reative Form guide that explains how to add a FormArray of Adrresses to a FormGroup.
Now I want to have a hero that can have different powers, selecting them from a select, or better from a dynamic array of select.
Passing from the example of Angular Docs to my desired functionality I can't make it to run.
This is my hero-form.ts
#Component({
selector: 'app-hero-form',
templateUrl: './hero-form.component.html',
styleUrls: ['./hero-form.component.css']
})
export class HeroFormComponent implements OnInit, OnChanges {
heroForm: FormGroup;
nameChangeLog: string[] = [];
hero: Hero = new Hero();
allPowers: Power[] = [];
constructor(private fb: FormBuilder, private powerService: PowerService) {
this.createForm();
this.logNameChange();
}
ngOnInit() {
this.powerService.getAll().subscribe(powers => this.allPowers = powers);
}
createForm() {
this.heroForm = this.fb.group({
name: ['', Validators.required],
powers: this.fb.array([]),
});
}
ngOnChanges() {
this.rebuildForm();
}
rebuildForm() {
this.heroForm.reset({
name: this.hero.name
});
this.setPowersControl(this.hero.powers);
}
setPowersControl(powers: Power[]) {
const powersFGs = powers.map(pow => this.fb.group(pow));
const powersFormArray = this.fb.array(powersFGs);
this.heroForm.setControl('powers', powersFormArray);
}
get powers(): FormArray {
const pows = this.heroForm.get('powers') as FormArray;
return pows;
}
addPowerChoice() {
this.powers.push(this.fb.control(new Power()));
// this.powers.push(this.fb.group(new Power(), Validators.required));
}
onSubmit() {
this.hero = this.prepareSaveHero();
console.log('SAVING HERO', this.hero);
// this.heroService.updateHero(this.hero).subscribe(/* error handling */);
this.rebuildForm();
}
prepareSaveHero(): Hero {
const formModel = this.heroForm.value;
// deep copy of form model lairs
const powersDeepCopy: Power[] = formModel.powers.map(
(pow: Power) => Object.assign({}, pow)
);
// return new `Hero` object containing a combination of original hero value(s)
// and deep copies of changed form model values
const saveHero: Hero = {
id: this.hero.id,
name: formModel.name as string,
// addresses: formModel.secretLairs // <-- bad!
powers: powersDeepCopy
};
return saveHero;
}
revert() { this.rebuildForm(); }
logNameChange() {
const nameControl = this.heroForm.get('name');
nameControl.valueChanges.forEach(
(value: string) => this.nameChangeLog.push(value)
);
}
}
This is my hero-form.html
<form [formGroup]="heroForm" (ngSubmit)="onSubmit()">
<div style="margin-bottom: 1em">
<button type="submit" [disabled]="heroForm.pristine" class="btn btn-success">Save
</button>
<button type="button" (click)="revert()" [disabled]="heroForm.pristine" class="btn btn-danger">Revert</button>
</div>
<!-- Hero Detail Controls -->
<div class="form-group">
<label class="center-block">Name:
<input class="form-control" formControlName="name">
</label>
</div>
<div formArrayName="powers" class="well well-lg">
<div *ngFor="let pow of powers.controls; let i=index" [formControlName]="i">
<!-- The repeated power template -->
<h4>Potere #{{i + 1}}</h4>
<div style="margin-left: 1em;">
<div class="form-group">
<label class="center-block">Power:
<select class="form-control">
<option *ngFor="let pow of allPowers" [value]="pow">{{pow.name}}</option>
</select>
</label>
</div>
</div>
<br>
<!-- End of the repeated address template -->
</div>
<button (click)="addPowerChoice()" type="button">Add a Power</button>
</div>
</form>
<p>heroForm value: {{ heroForm.value | json}}</p>
<h4>Name change log</h4>
<div *ngFor="let name of nameChangeLog">{{name}}</div>
This is power-service that is only returning stubbed data
#Injectable({
providedIn: 'root'
})
export class PowerService {
constructor() {
}
getAll(): Observable<Power[]> {
return of(this.getProds());
}
getProds(): Power[] {
const powers = [];
for (let i = 0; i < 10; i++) {
const pow = new Power();
pow.id = i+'';
pow.name = 'Power ' + i;
powers.push(pow);
}
return powers;
}
}
And these are my data models
export class Hero {
id: number;
name: string;
powers: Power[];
}
export class Power {
id: string = '';
name: string = '';
}
Here I have make a stackblitz example but it's not working
I've solved
I have moved formControlName from div onto select as suggested by Lucas Klaassen, and changed [value] to [ngValue] onto option
<form [formGroup]="heroForm" (ngSubmit)="onSubmit()">
<div style="margin-bottom: 1em">
<button type="submit"
[disabled]="heroForm.pristine" class="btn btn-success">Save
</button>
<button type="button" (click)="revert()"
[disabled]="heroForm.pristine" class="btn btn-danger">Revert
</button>
</div>
<!-- Hero Detail Controls -->
<div class="form-group">
<label class="center-block">Name:
<input class="form-control" formControlName="name">
</label>
</div>
<div formArrayName="powers" class="well well-lg">
<div *ngFor="let pow of powers.controls; let i=index">
<!-- The repeated power template -->
<h4>Potere #{{i + 1}}</h4>
<div style="margin-left: 1em;">
<div class="form-group">
<label class="center-block">Power:
<select class="form-control" [formControlName]="i">
<option *ngFor="let pow of allPowers" [ngValue]="pow">{{pow.name}}</option>
</select>
</label>
</div>
</div>
<br>
<!-- End of the repeated address template -->
</div>
<button (click)="addPowerChoice()" type="button">Add a Power</button>
</div>
</form>
<p>heroForm value: {{ heroForm.value | json}}</p>
<h4>Name change log</h4>
<div *ngFor="let name of nameChangeLog">{{name}}</div>
Then I have changed onSubmit() adding a Hero's constructor call as follow
onSubmit() {
this.hero = this.prepareSaveHero();
console.log('SAVING HERO', this.hero);
// this.heroService.updateHero(this.hero).subscribe(/* error handling */);
this.hero = new Hero();
this.rebuildForm();
}
Then I have added a custom constructor to Hero class
export class Hero {
id: number;
name: string;
powers: Power[];
constructor() {
this.id = 0;
this.name = '';
this.powers = [];
}
}
Now it's working and correctly rebuilding form after submit