I have a form html and the button submit becomes enable only when the form is valid. That is, when a particular input contains the recommended pattern. I need to use this pattern in an inputlist. It works with a simple input but with the input list, the list disappears :
<input list="refNumbers" pattern="[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]" formControlName="refNb" type="text" name="article" maxlength="8" size="15" required title="8 characters" />
<datalist id="refNumbers">
<option *ngFor="let ref of listOfArticles">{{ref.refNumber.input}}</option>
</datalist>
Otherwise, how can i disable button submit if the ref number selected is not in the datalist ? Because it seems that the valid condition on the form is not enough :
<button type="submit" [disabled]="!myFormGroup.valid" >Valider</button>
component.ts :
import { Component, OnInit } from '#angular/core';
import 'rxjs/add/operator/switchMap';
import { ManagementArbologistiqueService } from "../management-arbologistique.service";
import { ActivatedRoute, Params } from '#angular/router';
import { FormGroup, FormControl, FormBuilder, FormArray, Validators } from '#angular/forms';
#Component({
selector: 'app-arbologistique',
templateUrl: './arbologistique.component.html',
styleUrls: ['./arbologistique.component.css']
})
export class ArbologistiqueComponent implements OnInit {
private reponseTest: String;
private listOfArticles :Array<Object>
private pathDownload: any;
private myFormGroup: FormGroup;
fileToUpload: File = null;
private buttonSubmitEnabled: boolean = false;
constructor(public fb: FormBuilder, private managementArbo: ManagementArbologistiqueService, private route: ActivatedRoute) { }
ngOnInit() {
this.myFormGroup = this.fb.group({
itemRows: this.fb.array([this.initItemRows()])
})
this.myFormGroup.valueChanges.subscribe(x => this.buttonSubmitEnabled = false);
this.getListBdd();
}
initItemRows() {
return this.fb.group({
... //other fields
refNb: ['',Validators.required],
... //other fields
})
}
addRow(index: number) {
console.log("functionAddRow called");
const control = <FormArray>this.myFormGroup.controls['itemRows'];
control.insert(index, this.initItemRows());
}
deleteRow(index: number) {
console.log("functionDeleteRow called");
const control = <FormArray>this.myFormGroup.controls['itemRows'];
control.removeAt(index);
}
sendForm() {
this.buttonSubmitEnabled=true;
console.log("functionExportCalled");
this.route.params.subscribe((params: Params) => {
let subroute = "exportation";
this.managementArbo.postProducts(subroute, JSON.stringify(this.myFormGroup.value))
.subscribe(
res => { this.reponseTest = res; console.log('reponse:' + res); }
,
err => console.log(err),
() => console.log('getProducts done'));
});
}
getListBdd() {
this.route.params.subscribe((params: Params) => {
let subroute = "getRefNumber";
this.managementArbo.getProducts(subroute)
.subscribe(
res => { this.listOfArticles = res; console.log('reponse:' + res); }
,
err => console.log(err),
() => console.log('getProducts done'));
});
}
get refNb() {
return this.myFormGroup.get('itemRows.refNb');
}
}
If you want to add Validation in reactive form you can use build in Validators
initItemRows() {
const regEx="[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]";
return this.fb.group({
... //other fields
refNb: ['',Validators.required,Validators.pattern(regEx)],
... //other fields
})
}
<button type="submit" [disabled]="!myFormGroup.valid" >Valider</button>
Related
I have a following angular component ts with an ability to add dynamic fields to a form:
import {Component, ElementRef, OnInit, ViewChild} from '#angular/core';
import {Buildcompany} from "../../models/buildcompany.model";
import {BuildcompanyService} from "../../services/buildcompany.service";
import { FormGroup, FormControl, FormArray, FormBuilder } from '#angular/forms'
#Component({
selector: 'app-add-buildcompany',
templateUrl: './add-buildcompany.component.html',
styleUrls: ['./add-buildcompany.component.css'],
})
export class AddBuildcompanyComponent implements OnInit {
buildcompany: Buildcompany={
shortname:'',
fullname:'',
address:'',
telephone:'',
website:'',
sociallinks:'',
foundationyear:''
}
submitted=false;
#ViewChild('network', {static: true}) networkElement: ElementRef;
netw?: [];
constructor(private buildcompanyService:BuildcompanyService,networkElement: ElementRef) {
this.networkElement=networkElement;
}
ngOnInit(): void {
}
add(){
let row = document.createElement('div');
row.className = 'row';
row.innerHTML = `
<br>
<input type="text" id="netw" name="network[]" #network class="form-control netw">"`;
let row2 = document.createElement('div');
row2.innerHTML = `
<br>
<input type="text" name="links[]" class="form-control">"`;
// #ts-ignore
document.querySelector('.showInputField').appendChild(row);
// #ts-ignore
document.querySelector('.showInputField').appendChild(row2);
}
saveBuildcompany(): void {
// #ts-ignore
this.netw = (<HTMLInputElement>document.getElementsByClassName("netw")).value;
// #ts-ignore
console.log(this.netw[0].value);
const data = {
shortname: this.buildcompany.shortname,
fullname: this.buildcompany.fullname,
address: this.buildcompany.address,
telephone: this.buildcompany.telephone,
website: this.buildcompany.website,
sociallinks: this.buildcompany.sociallinks,
foundationyear: this.buildcompany.foundationyear
};
this.buildcompanyService.create(data)
.subscribe({
next: (res) => {
console.log(res);
this.submitted = true;
},
error: (e) => console.error(e)
});
}
newBuildcompany(): void {
this.submitted = false;
this.buildcompany = {
shortname: '',
fullname:'',
address:'',
telephone:'',
website:'',
foundationyear:''
};
}
}
Is that posible at all to get values of those dynamicly added fields into an array on save() function? I can see in a console that they are actually shown. The class is called netw. Or what is the best approche to this task?
UPDATE
I tryed to add a formgroup and got the following error
Error:
ngModel cannot be used to register form controls with a parent formGroup directive. Try using
formGroup's partner directive "formControlName" instead. Example:
I'm working on Angular 9 and want to access an input field after clicking on a button. right now it gives me undefined. I have tried #ViewChild and #viewChildern because I'm using ngIf.
Template.html file
<div class="search-input" #searchDiv *ngIf="serachActive">
<input
#searched
autofocus
type="text"
class="serach-term"
placeholder="Search"
[(ngModel)]="searchTerms"
(ngModelChange)="applySearch()"
/>
<button (click)="toggleSearch(!serachActive)">
<span class="material-icons"> search </span>
</button>
<ul class="search-list">
<li *ngFor="let result of results">
<a [routerLink]="['/', 'video', 'details', result._id]">{{
result.title ? result.title : ''
}}</a>
</li>
</ul>
</div>
Template.ts file
import { Component, OnInit,AfterViewInit,ElementRef,ViewChild,ViewChildren } from '#angular/core';
import { UserService } from '../../../user.service';
import { VideoService } from '../../../services/video.service';
import { Subject } from 'rxjs';
import { distinctUntilChanged, debounceTime } from 'rxjs/operators';
import { Router } from '#angular/router';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css'],
})
export class HeaderComponent implements OnInit,AfterViewInit{
serachActive: boolean = false;
#ViewChildren('searched') searchElement: ElementRef;
#ViewChildren("searched") input: ElementRef;
user;
subject = new Subject<string>();
results = [];
searchTerms;
loggedIn: Boolean = false;
constructor(
private userService: UserService,
private videoService: VideoService,
private router: Router
) {
this.user = this.userService.getUser();
this.loggedIn = this.userService.isAuthenticated();
}
ngOnInit() {
console.log('on init', this.input); //undefined
this.subject
.pipe(debounceTime(400), distinctUntilChanged())
.subscribe((value) => {
this.router.navigate(['search'], { queryParams: { term: value } });
});
}
ngAfterViewInit() {
console.log('on after', this.input); //undefined
}
toggleSearch(toggledata) {
this.serachActive = toggledata;
this.results = [];
this.searchTerms = '';
console.log(this.input) //undefined
console.log(this.searchElement.nativeElement) //undefined
}
applySearch() {
const searchText = this.searchTerms;
this.subject.next(searchText);
this.searchElement.nativeElement.focus(); //undefined
}
menuButtonClick(button){
if(button === "history"){
this.router.navigate(['history'])
}
}
}
Use ViewChild since you're only searching for 1 element ID.
If adding { static: true } or { static: false } in your ViewChild options doesn't work as what is stipulated on Angular Static Query Migration Documentation
Use ChangeDetectorRef instead:
#Component({...})
export class AppComponent {
#ViewChild('searchInput') input: ElementRef;
isShow: boolean = false;
constructor(private cdr: ChangeDetectorRef) {}
toggle(): void {
this.isShow = !this.isShow;
this.cdr.detectChanges(); // Detects changes which this.isShow is responsible on showing / hiding
// the element you're referencing to in ViewChild
if (this.isShow) // If element is shown, console the referenced element
console.log(this.input);
}
}
Have created a Stackblitz Demo for your reference
I want to display a list as the options in select when in each round in the loop the list will be updated according to the current div
It works in terms of concept, but the html is updated only once according to the last entry in the list and does not display a different list for each loop rotation
my html
<div *ngFor="let item of listConstraint" [value]="item.id">
<p>{{item.name_constraint}}</p>
<select>
<option *ngFor="let item1 of listConstraintDetails" [value]="item1.id">
{{item1.name_constraint}}</option>
</select>
</div>
my ts
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormControl, Validators } from '#angular/forms';
import { Router } from '#angular/router';
import { HttpClient } from '#angular/common/http';
import { AjaxRequestService } from 'src/app/services/Request/ajax-request.service';
import { ConstraintKind } from 'src/app/class/constraintKind';
import { ConstraintDetails } from 'src/app/class/constraint-details';
#Component({
selector: 'app-constraints',
templateUrl: './constraints.component.html',
styleUrls: ['./constraints.component.css']
})
export class ConstraintsComponent implements OnInit {
constraintForm: FormGroup;
listConstraint: ConstraintKind[] = [];
listConstraintDetails: ConstraintDetails[] = [];
constructor(private http: AjaxRequestService, private httpClient: HttpClient, private route: Router) {
}
ngOnInit(): void {
this.GetConstraintsKind();
}
GetConstraintsKind() {
return this.http.GetConstraintsKind().subscribe(data => {
this.listConstraint = data;
data.forEach(element => {
this.GetConstraintsDetails(element.id);
})
console.log(data);
})
}
GetConstraintsDetails(constraintId) {
return this.http.GetConstraintsDetails(constraintId).subscribe(data => {
this.listConstraintDetails = data;
console.log(data);
})
}
}
my functions ajax service
GetConstraintsKind() {
return this.http.get<any>('http://localhost:11818/Api/Constraint/getConstraintKind', { headers: this.header });
}
GetConstraintsDetails(constraintId: number) {
return this.http.get<ConstraintDetails[]>('http://localhost:11818/Api/Constraint/GetConstraintsDetails/' + constraintId);
}
the server works well, and send the correct data, but the html display the same list the whole time
Thanks so much for any help
You are performing inner loop operation all at ngOnInit inside a single array, thats overwriting previously fetched data in the listConstraintDetails array.
What you want can be achieved, if you modify your code a little, like this
ngOnInit(): void {
this.GetConstraintsKind();
}
GetConstraintsKind() {
return this.http.GetConstraintsKind().subscribe(data => {
this.listConstraint = data;
})
}
GetConstraintsDetails(constraintId):ConstraintDetails[]{
let itemsarr: ConstraintDetails[] = [];
if(constraintId)
{
this.http.GetConstraintsDetails(constraintId).subscribe(data => {
itemsarr = data;
})
}
return itemsarr;
}
And your html would get modified like
<div *ngFor="let item of listConstraint" [value]="item.id">
<p>{{item.name_constraint}}</p>
<select>
<option *ngFor="let item1 of GetConstraintsDetails(item.id)" [value]="item1.id">
{{item1.name_constraint}}</option>
</select>
</div>
Thanks.
my html
<div *ngFor="let item of listConstraint" [attr.data-value]="item.id">
<p>{{item.name_constraint}}</p>
<select *ngIf="constraintDetails[item.id]">
<option *ngFor="let item1 of constraintDetails[item.id]" [value]="item1.id">
{{item1.name_constraint}}</option>
</select>
</div>
my ts
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormControl, Validators } from '#angular/forms';
import { Router } from '#angular/router';
import { HttpClient } from '#angular/common/http';
import { AjaxRequestService } from 'src/app/services/Request/ajax-request.service';
import { ConstraintKind } from 'src/app/class/constraintKind';
import { ConstraintDetails } from 'src/app/class/constraint-details';
import { Observable } from 'rxjs';
#Component({
selector: 'app-constraints',
templateUrl: './constraints.component.html',
styleUrls: ['./constraints.component.css']
})
export class ConstraintsComponent implements OnInit {
constraintForm: FormGroup;
listConstraint: ConstraintKind[] = [];
listConstraintDetails: ConstraintDetails[] = [];
constraintDetails = {};
constructor(private http: AjaxRequestService, private httpClient: HttpClient, private route: Router) {
}
ngOnInit(): void {
this.GetConstraintsKind();
}
GetConstraintsKind() {
return this.http.GetConstraintsKind().subscribe(data => {
this.listConstraint = data;
for (let i = 0; i < this.listConstraint.length; i++) {
const element = this.listConstraint[i].id;
this.http.GetConstraintsDetails(element)
.subscribe(cd => {
this.constraintDetails[element] = cd;
console.log(this.constraintDetails)
});
}
})
}
GetConstraintsDetails(constraintId): ConstraintDetails[] {
console.log(constraintId);
let itemsarr: ConstraintDetails[] = [];
if (!itemsarr) {
this.http.GetConstraintsDetails(constraintId).subscribe(data => {
itemsarr = data;
})
}
return itemsarr;
}
}
Having some problems with filling my drop down menu with data from my API. I need help figuring out what I am doing wrong because I am getting no errors in the console.
Service.ts method call:
#Injectable({
providedIn: 'root'
})
export class OrderExceptionReportService extends ApiServiceBase {
private apiRoute: string = 'exception-report';
public sessionData: ExceptionReportSessionData[];
constructor(http: HttpClient, configService: ConfigService) {
super(http, configService);
}
public async GetExceptionReportSessionData(): Promise<ExceptionReportSessionData[]> {
if (!this.appSettings) {
this.appSettings = await this.configService.loadConfig().toPromise();
}
return await this.http.get<ExceptionReportSessionData[]>(this.appSettings.apiSetting.apiAddress + this.apiRoute + '/session-data')
.pipe(
retry(1),
catchError(this.handleError)
)
.toPromise()
}
}
component.ts file:
#Component({
selector: 'app-order-exception-report',
templateUrl: './order-exception-report.component.html',
styleUrls: ['./order-exception-report.component.scss']
})
export class OrderExceptionReportComponent implements OnInit {
public sessionData: ExceptionReportSessionData[];
constructor(private orderExceptionReportService: OrderExceptionReportService) {
}
getExceptionReportSessionData() {
this.orderExceptionReportService.GetExceptionReportSessionData()
.then(
data => {
this.sessionData = data;
});
}
ngOnInit() {
}
}
html where I need the data:
<div class="input-group">
<select class="custom-select" id="inputGroupSelect01">
<option value="" *ngFor="let session of sessionData">{{session.SessionName}}</option>
</select>
</div>
Interface.ts file:
export interface ExceptionReportSessionData {
SessionName: string,
ReportFiles: Array<string>
}
From your comments, it appears the this.configService.loadConfig() returns an HTTP observable as well.
In that case, you could avoid using promises and streamline the process using RxJS method iff to check if the this.appSettings variable is defined and switchMap operator to switch to the target request once appSettings is retrieved.
Try the following
public GetExceptionReportSessionData(): Observable<ExceptionReportSessionData[]> {
return iff(() =>
this.appSettings,
of(this.appSettings),
this.configService.loadConfig().pipe(tap(appSettings => this.appSettings = appSettings))).pipe(
switchMap(appSettings => this.http.get<ExceptionReportSessionData[]>(appSettings.apiSetting.apiAddress + this.apiRoute + '/session-data')),
retry(1),
catchError(this.handleError)
);
}
tap operator is used to tap into the result and store it into this.appSettings variable. of method is used to send an observable of this.appSettings since switchMap operator expects an observable.
You could then subscribe to the function in the component
export class OrderExceptionReportComponent implements OnInit {
public sessionData: ExceptionReportSessionData[];
constructor(private orderExceptionReportService: OrderExceptionReportService) {}
getExceptionReportSessionData() {
this.orderExceptionReportService.GetExceptionReportSessionData().subscribe(
data => { this.sessionData = data; },
error => { }
);
}
ngOnInit() {
this.getExceptionReportSessionData();
}
}
Trying to make Play Framework (REST) and ng-bootstrap - Typeahead work together. But I'm facing a problem with extracting data from json response. For example I write "test" (searches in database by name), server returns json array(everything is right):
[{
"id": 1,
"name": "test",
"annotation": "test annotation",
"kiMin": 1,
"kiMax": 2,
"cosFiMin": 3,
"cosFiMax": 4
}, {
"id": 4,
"name": "test2",
"annotation": "test annotation",
"kiMin": 1,
"kiMax": 2,
"cosFiMin": 3,
"cosFiMax": 4
}]
But the view looks like this:
Here's my code:
http.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Equipment } from './equipment';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class HttpService {
private Url = "http://localhost:9000/find?term="; // URL to web API
constructor (private http: Http) {}
search ( term :String ): Observable<Equipment[]> {
return this.http.get(this.Url+term)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body || { };
}
private handleError (error: Response | any) {
// In a real world app, we might use a remote logging infrastructure
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}
ngb-typeahead-http.ts
import {Component} from '#angular/core';
import {Observable} from 'rxjs/Observable';
import {HttpService} from './http.service';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
#Component({
selector: 'ngb-typeahead-http',
templateUrl: './typeahead-http.html',
providers: [HttpService],
styles: [`.form-control { width: 300px; display: inline; }`]
})
export class NgbTypeaheadHttp {
model: any;
searching = false;
searchFailed = false;
constructor(private _service: HttpService) {}
search = (text$: Observable<string>) =>
text$
.debounceTime(300)
.distinctUntilChanged()
.do(() => this.searching = true)
.switchMap(term =>
this._service.search(term)
.do(() => this.searchFailed = false)
.catch(() => {
this.searchFailed = true;
return Observable.of([]);
}))
.do(() => this.searching = false);
}
typeahead-http.html
<div class="form-group" [class.has-danger]="searchFailed">
<input type="text" class="form-control" [(ngModel)]="model" [ngbTypeahead]="search" placeholder="search" />
<span *ngIf="searching">searching...</span>
<div class="form-control-feedback" *ngIf="searchFailed">Sorry, suggestions could not be loaded.</div>
</div>
How could I extract data from json object? Any suggestions, please.
When you're using objects with the typeahead you need to make use of the [inputFormatter] and [resultFormatter] inputs. For inputFormatter and resultFormatter you pass functions that take the object from your selection or results list and output the text value you want to display for that object.
Added function to component:
#Component({
selector: 'ngb-typeahead-http',
templateUrl: './typeahead-http.html',
providers: [HttpService],
styles: [`.form-control { width: 300px; display: inline; }`]
})
export class NgbTypeaheadHttp {
model: any;
searching = false;
searchFailed = false;
constructor(private _service: HttpService) {}
// Added
formatMatches = (value: any) => value.name || '';
search = (text$: Observable<string>) =>
text$
.debounceTime(300)
.distinctUntilChanged()
.do(() => this.searching = true)
.switchMap(term =>
this._service.search(term)
.do(() => this.searchFailed = false)
.catch(() => {
this.searchFailed = true;
return Observable.of([]);
}))
.do(() => this.searching = false);
}
Passing function to typeahead inputs
<div class="form-group" [class.has-danger]="searchFailed">
<input type="text" class="form-control"
[(ngModel)]="model" [ngbTypeahead]="search"
placeholder="search"
[resultFormatter]="formatMatches"
[inputFormatter]="formatMatches" />
<span *ngIf="searching">searching...</span>
<div class="form-control-feedback" *ngIf="searchFailed">Sorry, suggestions could not be loaded.</div>
</div>