input from a scan to a form builder in angular - javascript

I need to fill in a field of a form builder using the zxing-js library, I am able to read the qrcode and insert this data into the input, but when submitting the field it is blank, if the insertion is from the keyboard everything goes perfectly. .. follow the code snippets
my form:
<form [formGroup]="formulario" (ngSubmit)="submit()" action="#">
<div class="divEtiqueta">
<mat-form-field class="inputEtq">
<input
formControlName="numRomaneio"
matInput
[(ngModel)]="romaneio.id"
name="name"
placeholder="Nº Romaneio"
/>
</mat-form-field>
<mat-form-field class="inputEtq">
<input
matInput
id="codEtiqueta"
name="codEtiqueta"
formControlName="codEtiqueta"
placeholder="Cód. Etiqueta"
/>
</mat-form-field>
</div>
<section class="container" id="demo-content">
<div>
<video
id="video"
width="95%"
height="300"
style="border: 1px solid gray"
></video>
</div>
<div class="buttonConfirmar">
<button
type="submit"
class="btnConfirmar"
id="confirmarButton"
mat-raised-button
onclick="location.href='#'"
>Confirmar
</button>
</div>
</section>
</form>
my TS:
formulario!: FormGroup;
romaneio: Romaneio = {
id: 0,
status: ''
}
idRomaneio!: number;
numcarga!: number;
id!: string;
itens!: Item[];
displayedColumns = ['cod_fab', 'descricao', 'qac', 'qc', 'faltam']
constructor(private qrcodeapiService: QrcodeapiService, private fb: FormBuilder, private router: Router, private route: ActivatedRoute) { }
ngOnInit(): void {
this.formulario = this.fb.group({
codEtiqueta: ['', Validators.required],
numRomaneio: ['', Validators.required]
})
this.romaneio.id = this.idRomaneio;
}
buscarItens(){
this.qrcodeapiService.listar(this.romaneio.id).subscribe(itens =>{
this.itens = itens
console.log(itens)
})
}
submit(){
const formValues = this.formulario.value;
const etiqueta: Etiqueta = new Etiqueta(formValues.codEtiqueta, formValues.numRomaneio);
this.qrcodeapiService.salvar(etiqueta).subscribe(etiqueta =>{
this.qrcodeapiService.showMessage('Registrado')
this.formulario = this.fb.group ({
codEtiqueta: ['', Validators.required],
numRomaneio: this.romaneio.id
})
this.buscarItens();
});
}
and the script for read QrCode:
function decodeOnce(codeReader, selectedDeviceId) {
codeReader
.decodeFromInputVideoDevice(selectedDeviceId, "video")
.then((result) => {
console.log(result);
document.getElementById("codEtiqueta").value = result.text;
})
.catch((err) => {
console.error(err);
});
}
from scan:
input scan
payload scan
From keyboard:
input keyboard
payload keyboard

Using the #zxing/ngx-scanner package I used it in the (trimmed down) code as below, hope this helps.
html:
<p-card>
<zxing-scanner [enable]=showScanner
[formats]=allowedFormats
(scanSuccess)="onScanSucces($event)"
></zxing-scanner>
<label for="first_scan">First Scan</label>
<input id="first_scan" type="text"
[value]="firstScanNumber"
required autofocus disabled>
</p-card>
TS:
export class ScanComponent implements OnInit {
firstScanNumber: string = "";
allowedFormats = [ BarcodeFormat.QR_CODE ];
showScanner: boolean = false;
constructor() { }
ngOnInit(): void {
this.showScanner = true;
}
onScanSucces($event: any){
console.log($event)
this.firstScanNumber = $event;
this.showScanner = false;
}
}

Related

My angular component EventEmitter is returning wrong variable value

The code
My Product Component.
This is not complete code. I removed some part of the code like server calls and evrything related to backend.
import {Component, EventEmitter, Input, OnChanges, OnInit, Output} from '#angular/core';
import { DataProvider } from 'src/app/providers/data.provider';
import { AuthService } from 'src/app/services/auth.service';
import { InventoryService } from 'src/app/services/inventory.service';
#Component({
selector: 'app-wide-product-card',
templateUrl: './wide-product-card.component.html',
styleUrls: ['./wide-product-card.component.css']
})
export class WideProductCardComponent implements OnInit,OnChanges {
#Input() img:string = "https://source.unsplash.com/650x940"
#Input() orderTitle:string = "ArtWork Product"
#Input() orderDescription:string = "Lorem ipsum dolor sit amet, consectetur\n" +
" adipiscing elit. Curabitur cursus tincidunt\n" +
" commodo. Nunc justo nisi, vestibulum."
#Input() orderprice:number = 2300;
#Input() category:string;
#Input() subcategory:string;
#Input() productId:string
#Input() extras:any;
#Input() orderconfig:string = "Chosen config"
#Input() quantity:number = 1;
#Input() identifier:string="";
#Input() configData:any;
db:any={};
#Input() showConfig:boolean = true;
#Input() showActions:boolean = true;
#Input() showQuantity:boolean = false;
#Input() showImageInput:boolean = false;
#Output() changeQuantity : EventEmitter<any> = new EventEmitter();
#Output() addImage: EventEmitter<any> = new EventEmitter();
#Output() removeEvent: EventEmitter<any> = new EventEmitter();
constructor(public inventoryService: InventoryService,private authService: AuthService,private dataProvider: DataProvider) { }
changeImage(image){
if (image.target.files[0].size < 500000){
this.dataProvider.data=this.identifier;
const a:any = {productId:this.productId,image:image.target.files[0],refData:this.identifier}
console.log("Event logging just before emitting the event from function changeImage(event)",this.identifier)
this.addImage.emit(a);
this.authService.presentToast("Image added successfully")
} else {
this.authService.presentToast("Image size should be less than 500kb",3000)
}
}
removeFromWishlist(){
this.removeEvent.emit({productId:this.productId,ref:this.identifier})
}
removeQuantity(){
this.quantity=this.quantity-1
this.changeQuantity.emit({quantity:this.quantity,productId:this.productId,ref:this.identifier});
}
addQuantity(){
this.quantity=this.quantity+1
this.changeQuantity.emit({quantity:this.quantity,productId:this.productId,ref:this.identifier});
}
ngOnInit(){
console.log('Event value emitted from ngOnInit()',this.identifier)
}
ngOnChanges(){
console.log('Event value emitted from OnChanges() from child component',this.identifier)
}
}
My component html
<div class="order-card">
<img [src]="img" alt="{{orderTitle}}" class="order-img">
<div class="order">
<div class="order-details">
<h4 class="order-title">{{orderTitle}}</h4>
<p class="order-description">{{orderDescription}}</p>
</div>
<div class="order-price-and-btns">
<p class="order-price">Price: ₹{{orderprice * quantity}}</p>
<ion-button *ngIf="showActions" class="order-remove" (click)="removeFromWishlist()">Remove</ion-button>
<ion-button *ngIf="showActions" class="order-view"
href="./product?productId={{this.productId}}">View
Product</ion-button>
<ion-item button lines="none" *ngIf="showImageInput">
<input type="file" name="file" max="1" (change)="changeImage($event)" id="file" class="inputfile" />
<label for="file">Choose a file</label>
</ion-item>
</div>
</div>
<div class="order-config">
<h4 class="chosen-config">{{orderconfig}}</h4>
<ion-list>
<ion-item *ngFor="let configitem of configData">
<ion-label>{{configitem.title}}</ion-label>
<ion-icon name="arrow-forward"></ion-icon>
<ion-label>{{configitem.value}}</ion-label>
</ion-item>
</ion-list>
<div *ngIf="showQuantity">
<ion-item lines="none">
<button slot="start" [disabled]="quantity<=1" (click)="removeQuantity()">
<ion-icon name="remove"></ion-icon>
</button>
<ion-label color="primary" >{{quantity}}</ion-label>
<button [disabled]="quantity>=10" slot="end" (click)="addQuantity()">
<ion-icon name="add"></ion-icon>
</button>
</ion-item>
</div>
</div>
</div>
My Parent page
import { ChangeDetectorRef, Component, OnInit } from '#angular/core';
import { AngularFirestore } from '#angular/fire/firestore';
import { ModalController } from '#ionic/angular';
import { InvoiceDetailComponent } from 'src/app/modals/invoice-detail/invoice-detail.component';
import {
FormBuilder,
FormControl,
FormGroup,
Validators,
} from '#angular/forms';
import { Router } from '#angular/router';
import { DataProvider } from 'src/app/providers/data.provider';
import { AuthService } from 'src/app/services/auth.service';
import { InventoryService } from 'src/app/services/inventory.service';
import { PaymentService } from 'src/app/services/payment.service';
import { environment } from 'src/environments/environment';
import firebase from 'firebase/app';
import { AngularFireAnalytics } from '#angular/fire/analytics';
import { InvoiceService } from 'src/app/services/invoice.service';
#Component({
selector: 'app-checkout',
templateUrl: './checkout.component.html',
styleUrls: ['./checkout.component.scss'],
})
export class CheckoutComponent implements OnInit {
dataCopy;
constructor(
private afs: AngularFirestore,
public dataProvider: DataProvider,
private paymentService: PaymentService,
private changeRef: ChangeDetectorRef,
private authService: AuthService,
private formbuilder: FormBuilder,
private inventoryService: InventoryService,
private router: Router,
private analytics: AngularFireAnalytics,
public modalController: ModalController,
private invoiceService: InvoiceService,
) {
this.form = this.formbuilder.group({
firstName: this.firstName,
lastName: this.lastName,
email: this.email,
phoneNumber: this.phoneNumber,
addressLine1: this.addressLine1,
city: this.city,
pincode: this.pincode,
state: this.state,
country: this.country,
message: this.message,
santaCredit: this.santaCredit,
});
}
form: FormGroup;
firstName: FormControl = new FormControl('', [
Validators.required,
Validators.minLength(5),
Validators.pattern('[a-zA-Z ]*'),
]);
lastName: FormControl = new FormControl('', [
Validators.required,
Validators.minLength(5),
Validators.pattern('[a-zA-Z ]*'),
]);
email: FormControl = new FormControl('', [
Validators.required,
Validators.minLength(5),
Validators.email,
]);
phoneNumber: FormControl = new FormControl('', [
Validators.required,
Validators.minLength(10),
Validators.maxLength(10),
Validators.pattern('[0-9]{10}'),
]);
addressLine1: FormControl = new FormControl('', [
Validators.required,
Validators.minLength(5),
]);
city: FormControl = new FormControl('', [
Validators.required,
Validators.minLength(5),
Validators.pattern('[a-zA-Z ]*'),
]);
pincode: FormControl = new FormControl('', [
Validators.required,
Validators.pattern('[0-9]*'),
]);
state: FormControl = new FormControl('', [
Validators.required,
Validators.minLength(5),
Validators.pattern('[a-zA-Z ]*'),
]);
country: FormControl = new FormControl('', [
Validators.required,
Validators.minLength(5),
Validators.pattern('[a-zA-Z ]*'),
]);
santaCredit: FormControl = new FormControl(false, [Validators.required]);
message: FormControl = new FormControl('', [
Validators.minLength(10),
Validators.maxLength(100),
]);
orders = [];
objectKeys = Object.keys;
quantity = 1;
offerFlat: number = 0;
payableAmount = 0;
WindowRef: any;
processingPayment: boolean;
paymentResponse: any = {};
orderItems = [];
santaCoins: number = 0;
imageRequired: any = [];
imagesValid: boolean = false;
log(data) {
// console.log(data);
}
addImage(event) {
console.log('Event recieved by the addImage(event) eventHandler function', event);
let allValid = true;
// console.log('imageRequiredLEngth', this.imageRequired,event,this.dataProvider.data);
this.imageRequired.forEach((data, index) => {
if (data.ref == event.refData) {
this.imageRequired[index].imageReference = event.image;
}
if (data.imageReference == undefined) {
allValid = false;
}
});
this.imagesValid = allValid;
// console.log('imageRequired', this.imageRequired);
}
presentInvoice() {
}
get grandTotal(): number {
var total = 0;
this.dataCopy.forEach((order) => {
total += order.price * order.quantity;
});
return total - this.offerFlat;
}
get grandHeight(): number {
var total = 0;
this.orders.forEach((order) => {
total += order.finalPrice;
});
return total;
}
setCashback(event) {
if (event.detail.checked) {
this.offerFlat = this.santaCoins;
} else {
this.offerFlat = 0;
}
}
ngOnInit() {
this.dataProvider.showOverlay = false;
this.inventoryService
.getUserInfo()
.ref.get()
.then((user: any) => {
this.santaCoins = user.data().totalCashback;
});
if (this.dataProvider.checkOutdata) {
this.dataCopy = this.dataProvider.checkOutdata;
this.dataProvider.checkOutdata.forEach((prod) => {
// console.log('prod from checkoutdata', prod);
var docRef = this.afs.collection('products').ref.doc(prod.productData);
docRef.get().then((data) => {
if (data.exists) {
// console.log('Document data:', data);
let dat: any = data.data();
if (dat.imageReference) {
this.imagesValid = false;
// console.log('identifier >>>',prod.identifier);
this.imageRequired.push({
productId: dat.productId,
ref: prod.identifier,
imageReference: undefined,
});
// console.log('imageRequired', this.imageRequired,this.imageRequired.length);
}
this.orderItems.push({
name: dat.productName,
sku: prod.productData,
units: 1,
selling_price: prod.price - this.offerFlat,
});
// console.log('identifier >>>',prod.identifier);
dat['finalPrice'] = prod.price;
dat['selections'] = prod.extrasData;
dat['quantity'] = prod.quantity;
dat['ref']=prod.identifier;
dat['cartId']=prod.cartId;
let config = []
for (let key of Object.keys(dat.selections)) {
let selection = dat.selections[key];
if (selection.type == 'textSel' || selection.type == 'imgSel'){
config.push({title:selection.sectionTitle,value:selection.title});
} else if (selection.type == 'faceCount'){
config.push({title:'Faces',value:selection.faces});
}
}
dat['config'] = config;
// console.log('dat data dt',dat);
this.orders.push(dat);
} else {
// console.log('No such document!');
}
});
});
this.WindowRef = this.paymentService.WindowRef;
} else {
this.authService.presentToast('Oh Ohh! Checkout expired 😅');
this.router.navigate(['']);
}
}
proceedToPay($event) {
console.log('imagereq',this.imageRequired)
console.log('imageVald',this.imagesValid)
if (this.imagesValid) {
this.dataProvider.showOverlay = true;
this.processingPayment = true;
this.payableAmount = this.grandTotal * 100;
this.imageRequired.forEach((data, index) => {
console.log(data);
})
// console.log('payable amount', this.payableAmount);
// this.initiatePaymentModal($event);
// this.analytics.logEvent('Checkout');
} else {
this.authService.presentToast('Please add all images by pressing Choose a file on every product.');
}
}
initiatePaymentModal(event) {
let receiptNumber = `Receipt#${Math.floor(Math.random() * 5123 * 43) + 10}`;
let orderDetails = {
amount: this.payableAmount,
receipt: receiptNumber,
};
this.paymentService.createOrder(orderDetails).subscribe(
(order) => {
console.log(
'TCL: CheckoutComponent -> initiatePaymentModal -> order',
order
);
var rzp1 = new this.WindowRef.Razorpay(
this.preparePaymentDetails(order)
);
this.processingPayment = false;
rzp1.open();
event.preventDefault();
},
(error) => {
console.log(
'TCL: CheckoutComponent -> initiatePaymentModal -> error',
error
);
this.authService.presentToast(error.message);
this.processingPayment = false;
}
);
}
preparePaymentDetails(order) {
var ref = this;
return {
key: environment.RAZORPAY_KEY_ID, // Enter the Key ID generated from the Dashboard
amount: this.payableAmount, // Amount is in currency subunits. Default currency is INR. Hence, 29935 refers to 29935 paise or INR 299.35.
name: 'Pay',
currency: order.currency,
order_id: order.id, //This is a sample Order ID. Create an Order using Orders API. (https://razorpay.com/docs/payment-gateway/orders/integration/#step-1-create-an-order). Refer the Checkout form table given below
image: 'https://angular.io/assets/images/logos/angular/angular.png',
handler: function (response) {
ref.handlePayment(response);
},
prefill: {
name:
this.form.get('firstName')!.value.toString() +
this.form.get('lastName')!.value.toString(),
email: this.form.get('email')!.value,
contact: this.form.get('phoneNumber')!.value,
},
theme: {
color: '#2874f0',
},
};
}
handlePayment(response) {
this.paymentService
.capturePayment({
amount: this.payableAmount,
payment_id: response.razorpay_payment_id,
})
.subscribe(
(res) => {
if (this.offerFlat > 0) {
this.inventoryService.updateUserData({ totalCashback: 0 });
}
this.paymentResponse = res;
this.changeRef.detectChanges();
console.log('success response', this.paymentResponse);
const shippingDetail = {
order_id: `Order#${
Math.floor(Math.random() * 5123435345 * 43) + 10
}`,
billing_customer_name: this.form.get('firstName')!.value,
billing_last_name: this.form.get('lastName')!.value,
billing_city: this.form.get('city')!.value,
billing_pincode: this.form.get('pincode')!.value,
billing_state: this.form.get('state')!.value,
billing_country: this.form.get('country')!.value,
billing_email: this.form.get('email')!.value,
billing_phone: this.form.get('phoneNumber')!.value,
billing_address: this.form.get('addressLine1')!.value,
order_items: this.orderItems,
payment_method: 'Prepaid',
sub_total: this.grandTotal,
length: 1,
height: 1,
weight: 1,
breadth: 1,
};
console.log('shippingDetail', shippingDetail);
this.paymentService.shipOrder(shippingDetail).subscribe(
(res: any) => {
this.authService.presentToast('Payment Successful 😊');
console.log('shipping Confirmed Detail', res);
let currentOrder = {
shippingDetail: res.body,
products: this.orders,
orderStage: 'live',
orderId: res.body.order_id,
shipment_id: res.body.shipment_id,
orderConfirmed: false,
grandTotal:this.grandTotal,
orderMessage: this.message.value || '',
};
this.inventoryService.addUserOrder(currentOrder);
let detail = {
name:shippingDetail.billing_customer_name,
address: shippingDetail.billing_address,
city: shippingDetail.billing_city,
state: shippingDetail.billing_state,
country: shippingDetail.billing_country,
pincode: shippingDetail.billing_pincode,
mobile: shippingDetail.billing_phone,
email: shippingDetail.billing_email,
discount:{available:true,code:'offerCode',price:120},
grandTotal:this.grandTotal,
taxCharges:(this.grandTotal/100)*15,
}
this.invoiceService.createInvoice(this.orders,detail);
this.dataProvider.shippingData =
currentOrder.shippingDetail.shipment_id.toString();
this.authService.presentToast('Order Placed Successfully ');
this.router.navigateByUrl(
'trackorder?shippingId=' +
currentOrder.shippingDetail.shipment_id.toString()
);
},
(error) => {
this.paymentResponse = error;
this.authService.presentToast(
error.message +
'\nPlease contact hello santa, to complete your order',
7000
);
console.log('Error occured while completing shipment');
}
);
},
(error) => {
this.paymentResponse = error;
console.log('failed response', this.paymentResponse);
}
);
}
}
Html of parent component.
<ion-content [ngStyle]="{'filter': (dataProvider.showOverlay) ? 'blur(10px)' : 'blur(0px)' }">
<app-header></app-header>
<div class="home">
<div class="uk-panel">
<div class="inner-box" align="center">
<main id="container">
<h2 id="checkout-title">Checkout</h2>
<section id="orders-card">
<div class="product-row">
<app-wide-product-card *ngFor="let order of orders" [img]="order.productImages[0].image"
[orderTitle]="order.productName" [identifier]="order.ref" [configData]="order.config" [showActions]="false" [orderDescription]="order.seoDescription"
[orderprice]="order.finalPrice" (addImage)="addImage($event)" [productId]="order.productId" [quantity]="order.quantity" [showImageInput]="order.imageReference" [showQuantity]="false"></app-wide-product-card>
</div>
</section>
<section id="orders-table-container">
<table id="orders-table">
<thead id="orders-table-head">
<tr>
<th>Product Name</th>
<th>Quantity</th>
<th>Price</th>
<th>Total Price</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let order of orders">
<td>{{order.productName}}</td>
<td>{{order.quantity}}</td>
<td>{{order.productPrice}}</td>
<td>{{order.finalPrice*order.quantity}}</td>
</tr>
</tbody>
<tfoot>
<tr>
<td></td>
<td></td>
<td>Grand Total</td>
<td>{{grandTotal}}</td>
</tr>
</tfoot>
</table>
</section>
<ion-card>
<h1 style="text-align: center">
Delivery info
</h1>
<ion-list>
<form [formGroup]="form" (ngSubmit)="proceedToPay($event)" method="POST">
<ion-item>
<ion-label position="floating">
Your First Name
</ion-label>
<ion-input type="text" placeholder="Your Name" formControlName="firstName" required></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">
Your Last Name
</ion-label>
<ion-input type="text" placeholder="Your Name" formControlName="lastName" required></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">
Your Email
</ion-label>
<ion-input type="text" placeholder="Enter Your Email" formControlName="email" required>
</ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">
Your Phone number
</ion-label>
<ion-input type="integer" placeholder="Enter Your Phone number" formControlName="phoneNumber"
required></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">
delivery address(including house number)
</ion-label>
<ion-input type="text" placeholder="delivery address(including house number)"
formControlName="addressLine1" required></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">
city
</ion-label>
<ion-input type="text" placeholder="enter your city" required formControlName="city">
</ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">
Pincode
</ion-label>
<ion-input type="text" placeholder="Enter Your Pincode" required formControlName="pincode">
</ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">
State
</ion-label>
<ion-input type="text" placeholder="Enter Your State" required formControlName="state">
</ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">
Country
</ion-label>
<ion-input type="text" placeholder="Enter Your country" required formControlName="country">
</ion-input>
</ion-item>
<ion-item>
<input type="checkbox" #checkbox (change)="log(checkbox)">
<ion-label> Add a message</ion-label>
</ion-item>
<ion-item *ngIf="checkbox.checked">
<ion-label position="floating">Your Message</ion-label>
<ion-textarea placeholder="Enter your message" formControlName="message"></ion-textarea>
</ion-item>
<ion-item *ngIf="santaCoins>0">
<ion-checkbox [checked]="false" formControlName="santaCredit" (ionChange)="setCashback($event)"></ion-checkbox>
<ion-label> Use Your {{santaCoins}} Santa Credit for <strong>₹{{this.santaCoins}}</strong> off.</ion-label>
</ion-item>
<div align="center">
<!-- TODO [disabled]="!form.valid" -->
<button type="submit" >
<div *ngIf="processingPayment">
<ion-spinner name="crescent"></ion-spinner> Processing
</div>
<div *ngIf="!processingPayment">
Proceed to Pay
</div>
</button>
</div>
</form>
</ion-list>
</ion-card>
</main>
</div>
</div>
</div>
<app-footer></app-footer>
</ion-content>
<div id="overlay" *ngIf="dataProvider.showOverlay">
<ion-spinner name="crescent" color="tertiary"></ion-spinner>
</div>
The problem
I have a list of product getting displayed by Wide Product Card Component and I am giving it a identifier input value as a unique alphanumeric string for each product or component.
The problem is that when I am logging the value of this.identifier in ngOnInit() function it is returning the correct value like {'a':b} but when I am emitting the value this.identifier it is emitting the event of the first component in the DOM. Like if there are two element one below another, and if I click on the event button in second element. Then the emitteed event is of first element.
What have I tried
Transferring data with data provider.
Using different dictionary keys for values (A dumb idea I know).
Image reference
TO verify that the CSS is not interfering with events.
And because the event is triggered by the choose file button the event is firing on the correct element but the event value is returned wrong.
Now in this case when I press the choose file button and choose the file it will return two things one identifier and one file object. And when I press the choose file on the second element and choose a file it returns the same identifier but a different file object. So the event emitter is somehow changing the value of the identifier variable whenever I use the value inside changeImage(event) function in the product component file.
Console logs
There is some bug with the custom input-file and Ionic,
How did you hide the real input textbox?
if you use the
<ion-item button>
<input type="file" hidden>
</ion-item>
or with style
display: none;
and there are a multiple instance in the same page Angular, when you click any button, raise the event of the first button/input in the DOM.
A workaround is to use the css style:
.inputfile{
opacity:0;
}
there is some reference to custom input file textbox in Ionic here:
How to create a custom file input in Ionic 2+ with a button styling?
UPDATE
Example to make a work custom input-file button with event on Ionic:
<ion-button>
<input type="file" name="file" id="file" (change)="changeImage($event)" class="inputfile">
<label for="file">Choose Photo</label>
</ion-button>
and the css class:
.inputfile {
opacity: 0;
position: absolute;
top: 0;
width: 100%;
height: 100%;
left: 0;
z-index: 999;
}
This is the example
First, you have to log your #Input() identifier:string="";
in OnChanges() to be sure that you get the value each time when it's changes.
Seconds, can you show exactly where you emitted to your parent component ?

How to asynchronously check if a username is already taken with formControl?

This is my method where i try to check from the database which usernames are already taken:
takenUsernames(control: FormControl): Promise<any> | Observable<any> {
const promise = new Promise<any>((resolve, reject) => {
this.employeeService.getEmployeeList().subscribe((employees) => {
this.employees = employees;
for (let employee of control.value) {
if (this.employees.indexOf(employee) !== -1) {
resolve({ usernameIsTaken: true });
} else {
resolve(null);
}
}
});
});
return promise;
}
And this is my formGroup object:
this.newProjectForm = new FormGroup({
projectName: new FormControl(null, Validators.required),
description: new FormControl(null),
assignedEmployees: new FormControl(null, [Validators.required]),
employees: new FormArray(
[],
[this.forbidUsernames.bind(this)],
[this.takenUsernames.bind(this)]
),
});
As you can see employees is a FormArray so i loop true control.value to check each username but it doesnt seem to work. The problem is in the takenUsernames function because i dont get the pending status if i inspect the element.
Here's the whole html code:
link
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght#600&family=Roboto+Mono:wght#500&display=swap"
type="text"
rel="stylesheet"
/>
<div class="backdrop" (click)="onFormAlertClose()"></div>
<div class="alert-box">
<form [formGroup]="newProjectForm" (ngSubmit)="onSubmit()">
<h3>New Project Form</h3>
<div class="form-group">
<label for="projectName">Project Name</label>
<input
type="text"
id="projectName"
class="form-control"
[formControlName]="'projectName'"
/>
<!-- if omit [] also omit ''-->
<span
*ngIf="
!newProjectForm.get('projectName').valid &&
newProjectForm.get('projectName').touched
"
>Please enter a project name</span
>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea
id="description"
class="form-control"
formControlName="description"
></textarea>
</div>
<div class="form-group">
<label for="assignedEmployees">Assign Employees</label>
<div class="larger-width-divider"></div>
<input type="text" class="search-box" placeholder="Search..." />
<select
name="employees"
class="form-control"
multiple
size="employees.length"
formControlName="assignedEmployees"
>
<ng-container *ngIf="newProjectForm.get('projectName').valid">
<option id="option" *ngFor="let employee of employees">{{
employee.userName
}}</option>
</ng-container>
</select>
<span
*ngIf="
!newProjectForm.get('assignedEmployees').valid &&
newProjectForm.get('assignedEmployees').touched
"
>Please select at least one employee</span
>
</div>
<div>
<button
type="button"
class="add-emp-btn"
*ngIf="!addEmployeeBtnClicked"
(click)="onEmpBtnClicked()"
[disabled]="newProjectForm.invalid"
>
Add Employees
</button>
<ng-container *ngIf="addEmployeeBtnClicked" formArrayName="employees">
<div *ngFor="let employeesControl of getControls(); let i = index">
<label for="userName">Insert one or multiple employee</label>
<input type="text" [formControlName]="i" />
<div class="width-divider"></div>
<button
type="button"
class="goto-add-emp-btn"
(click)="onEmpBtnClicked()"
>
Cancel
</button>
<div>
<span
class="last-span"
*ngIf="
!newProjectForm.get('employees').valid &&
newProjectForm.get('employees').touched
"
>
<span
*ngIf="newProjectForm.get('employees').errors?.nameIsForbidden"
>This name is invalid!</span
>
<span *ngIf="newProjectForm.get('employees').errors?.required"
>Add at least on employee</span
>
<span
*ngIf="newProjectForm.get('employees').errors?.usernameIsTaken"
></span>
</span>
</div>
</div>
</ng-container>
</div>
<div class="height-divider"></div>
<div class="alert-box-actions">
<button type="submit" class="submit" [disabled]="!newProjectForm.valid">
Create
</button>
<div class="width-divider"></div>
<button type="button" (click)="onFormAlertClose()">Cancel</button>
</div>
</form>
</div>
And here's the whole class:
import { Component, Output, OnInit } from "#angular/core";
import { Subject, Observable } from "rxjs";
import { ProjectService } from "src/app/services/project.service";
import { FormGroup, FormControl, Validators, FormArray } from "#angular/forms";
import { IEmployee } from "../entities/employee";
import { EmployeeService } from "src/app/services/employee.service";
#Component({
selector: "app-alert",
templateUrl: "./new-project-form-alert.component.html",
styleUrls: ["./new-project-form-alert.component.css"],
})
export class AlertComponent implements OnInit {
closeNewProjectAlert: Subject<boolean> = new Subject<boolean>();
employees: IEmployee[];
newProjectForm: FormGroup;
addEmployeeBtnClicked = false;
forbiddenUsernames = [
"Admin",
"Manager",
"Developer",
"Guest",
"admin",
"manager",
"developer",
"guest",
];
constructor(
private projectService: ProjectService,
private employeeService: EmployeeService
) {}
ngOnInit() {
this.employeeService.getEmployeeList().subscribe((employees) => {
this.employees = employees;
});
this.newProjectForm = new FormGroup({
// properties are string so when the files is minified they are not destroyed
projectName: new FormControl(null, Validators.required),
description: new FormControl(null),
assignedEmployees: new FormControl(null, [Validators.required]),
employees: new FormArray(
[],
[this.forbidUsernames.bind(this)],
this.takenUsernames
),
});
}
onFormAlertClose() {
this.projectService.closeNewProjectForm$.next(true);
}
onSubmit() {
console.log(this.newProjectForm);
this.newProjectForm.reset();
this.onFormAlertClose();
console.log((<FormArray>this.newProjectForm.get("employees")).controls);
}
onEmpBtnClicked() {
if (!this.addEmployeeBtnClicked) {
this.addEmployeeBtnClicked = true;
const control = new FormControl(null, Validators.required);
(<FormArray>this.newProjectForm.get("employees")).push(control);
} else {
this.addEmployeeBtnClicked = false;
(<FormArray>this.newProjectForm.get("employees")).removeAt(0);
}
}
forbidUsernames(control: FormControl): { [s: string]: boolean } {
for (let employee of control.value) {
if (this.forbiddenUsernames.indexOf(employee) !== -1) {
return { nameIsForbidden: true };
}
}
return null; // this if username is valid, do not return false
}
takenUsernames(control: FormControl): Promise<any> | Observable<any> {
const promise = new Promise<any>((resolve, reject) => {
const observable = this.employeeService.getEmployeeList().subscribe((employees) => {
this.employees = employees;
for (let employee of control.value) {
if (this.employees.indexOf(employee) !== -1) {
resolve({ usernameIsTaken: true });
} else {
resolve(null);
}
}
});
});
return promise;
}
getControls() {
return (<FormArray>this.newProjectForm.get("employees")).controls;
}
}
Sorry i know it a big mess, it's my first web app.
Edit: i provided a small video of the form if it helps: https://streamable.com/ua7mcq
Well yep, i have named the formArray as employees because when i submit the form the username or usernames get added to IEmployee objects and saved in the database.
But of the time of typing the usernames they are just string and i was comparing these strings with the employee objects.
So here's it fixed:
takenUsernames(control: FormControl): Promise<any> | Observable<any> {
const promise = new Promise<any>((resolve, reject) => {
const observable = this.employeeService.getEmployeeList().subscribe((employees) => {
this.employees = employees;
const usernames = this.employees.map(employee => employee.userName)
console.log(usernames)
for (let employee of control.value) {
if (usernames.indexOf(employee) !== -1) {
resolve({ usernameIsTaken: true });
} else {
resolve(null);
}
}
});
});
return promise;
}
But now i will reduce the input box to be able to add just one employee at time, wasted too much time on these component.

Compare Two values using Custom validator in dynamic formArray Angular 7

I have from group "additionalForm" in that i have formArray called "validations"
here the formArray is dynamic which is binding the values to validtionsField array. In the validtionsField array i have three objects in which i have two values which needs to be compared and they are Min-length and Max-Length.
ex. If i enter min length greater then the max length it should give error.
here is code for the above functionality
import {
Component,
OnInit,
Inject
} from "#angular/core";
import {
FormControl,
FormArray,
FormGroup,
FormBuilder,
Validators,
AbstractControl,
ValidationErrors,
NgControlStatus,
ValidatorFn
} from "#angular/forms";
import {
MatDialogRef,
MAT_DIALOG_DATA,
MatSnackBar
} from "#angular/material";
#Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.scss"]
})
export class AppComponent implements OnInit {
validtionsField = [{
validField: "Min Length",
type: false,
fieldType: "input",
value: 1,
keyval: "minLength"
},
{
validField: "Max Length",
type: false,
fieldType: "input",
value: 50,
keyval: "maxLength"
},
{
validField: "DataType",
type: false,
fieldType: "dropDown",
dataTypeList: [],
dataTypeId: "minLength",
keyval: "dataTypeId",
value: 874
}
];
dataType = [{
id: 3701,
localeId: 1,
tenantId: 1,
parentCategoryId: null,
parentContentId: 873,
name: "Alphabets",
description: null
},
{
id: 3702,
localeId: 1,
tenantId: 1,
parentCategoryId: null,
parentContentId: 874,
name: "Alphanumeric",
description: null
}
];
additionalForm: FormGroup = this.fb.group({
fieldName: ["", [Validators.required]],
validations: this.fb.array([])
});
constructor(public fb: FormBuilder) {}
ngOnInit() {
let frmArray = this.additionalForm.get("validations") as FormArray;
for (let data of this.validtionsField) {
frmArray.push(this.initSection(data));
}
}
initSection(data) {
return this.fb.group({
validField: [data.validField, [Validators.required]],
type: [data.type, [Validators.required]],
value: [data.value, [Validators.required]],
dataTypeList: [this.dataType, [Validators.required]],
fieldType: [data.fieldType, [Validators.required]],
validArray: []
}, {
validator: this.customValidator
});
}
checkFieldType(data): any {
return data === "dropDown";
}
// trying to access using below functioa to compare values min and max length
public customValidator(control: AbstractControl): ValidationErrors | null {
const newValue = control.get("value") ? control.get("value").value : null;
const values = control.get("value") ? control.get("value").value : [];
console.log("1 " + newValue);
console.log(values);
for (let i = 0, j = values.length; i < j; i++) {
if (newValue === values[i]) {
return {
duplicate2: true
};
}
}
return null;
}
}
<form [formGroup]="additionalForm">
<mat-form-field>
<input formControlName='fieldName' placeholder="Field Name" required matInput>
</mat-form-field>
<div class="row">
<div class="col-md-12 col-sm-12">
\
<div formArrayName="validations">
<ng-container *ngFor="let validationForm of additionalForm.controls.validations.controls; let i = index">
<div class="valid-data" [formGroupName]="i">
<span>
<label>{{validationForm.value.validField }}</label>
</span>
<span>
<ng-container *ngIf="checkFieldType(validationForm.value.fieldType ); else input">
<mat-form-field class="select-dataType">
<mat-select required formControlName='value' placeholder="Datatype">
<mat-option *ngFor="let fieldTypeData of validationForm.value.dataTypeList"
[value]='fieldTypeData.parentContentId'>
{{fieldTypeData.name}}</mat-option>
</mat-select>
</mat-form-field>
</ng-container>
<ng-template #input>
<mat-form-field>
<input required formControlName='value' pattern= "[0-9]+" matInput>
</mat-form-field>
</ng-template>
</span>
<div *ngIf="validationForm.get('value')?.touched ">
<div class="error" *ngIf="validationForm.get('value').hasError('required')">
{{validationForm.value.validField}} is required
</div>
</div>
</div>
</ng-container>
</div>
</div>
</div>
</form>
above is the TS and HTML code and below is the function which i am trying to get old and new value from control but its failing, its giving me value from same input field from same min-length
/// trying this below functio to compare the min and max length.
public customValidator(control: AbstractControl): ValidationErrors | null {
const newValue = control.get('value') ? control.get('value').value : null;
const values = control.get('value') ? control.get('value').value : [];
console.log("1 " + newValue);
console.log(values);
for (let i = 0, j = values.length; i < j; i++) {
if (newValue === values[i]) {
return {
'duplicate2': true
};
}
}
return null;
}
Please help me to compare values from dynamic form array, and here what ever values enters to from-array object are all bind to formcontrolName "value"
here is the link for code :
https://stackblitz.com/edit/angular6-material-components-demo-wgtafn
Since you have two fields minLength & maxLength which validation depends on each other, you can add a validator to the parent group and use a custom ErrorStateMatcher to translate the parent group errors to the children. I also used a FormGroup instead of FormArray, in this case it's more convenient.
#Component({...})
export class AppComponent {
...
readonly invalidLengthMatcher: ErrorStateMatcher = {
isErrorState: () => {
const control = this.additionalForm.get('validations');
return control.hasError('invalidLength');
}
};
readonly controlFields = this.validtionsField.map(field => ({
field,
control: new FormControl(field.value, Validators.required),
errorMatcher: this.errorMatcherByFieldId(field.keyval)
}));
private readonly controlMap = this.controlFields.reduce((controls, controlField) => {
controls[controlField.field.keyval] = controlField.control;
return controls;
}, {});
readonly additionalForm = new FormGroup({
fieldName: new FormControl("", [Validators.required]),
validations: new FormGroup(this.controlMap, {
validators: (group: FormGroup) => {
const [minLength, maxLength] = ['minLength', 'maxLength'].map(fieldId => {
const control = group.get(fieldId);
return Number(control.value);
});
if (minLength > maxLength) {
return {
'invalidLength': true
};
} else {
return null;
}
}
})
});
private errorMatcherByFieldId(fieldId: string): ErrorStateMatcher | undefined {
switch (fieldId) {
case 'minLength':
case 'maxLength':
return this.invalidLengthMatcher;
}
}
}
<form [formGroup]="additionalForm">
<mat-form-field>
<input formControlName='fieldName' placeholder="Field Name" required matInput>
</mat-form-field>
<div class="row">
<div class="col-md-12 col-sm-12">
<div formGroupName="validations" >
<div *ngFor="let controlField of controlFields" class="valid-data">
<span>
<label>{{controlField.field.validField}}</label>
</span>
<span [ngSwitch]="controlField.field.fieldType">
<mat-form-field *ngSwitchCase="'dropDown'" class="select-dataType">
<mat-select required placeholder="Datatype" [formControlName]="controlField.field.keyval">
<mat-option *ngFor="let fieldTypeData of dataType"
[value]='fieldTypeData.parentContentId'>{{fieldTypeData.name}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field *ngSwitchCase="'input'">
<input matInput
required
type="number"
pattern= "[0-9]+"
[formControlName]="controlField.field.keyval"
[errorStateMatcher]="controlField.errorMatcher">
</mat-form-field>
</span>
...
</div>
</div>
</div>
</div>
</form>
StackBlitz
You had to attach this validator to form array so that you can have access to all controls.
My Custom Validator :-
export function customValidateArray(): ValidatorFn {
return (formArray:FormArray):{[key: string]: any} | null=>{
console.log('calling');
let valid:boolean=true;
setTimeout(()=>console.log(formArray),200);
let minIndex = Object.keys(formArray.controls).findIndex((key) => formArray.controls[key].value.validField.toLowerCase()==="min length");
let minLengthValue = formArray.controls[minIndex].value.value;
let maxLengthValue = formArray.controls[Object.keys(formArray.controls).find((key) => formArray.controls[key].value.validField.toLowerCase()==="max length")].value.value;
return minLengthValue < maxLengthValue ? null : {error: 'Min Length Should be less than max length', controlName : formArray.controls[minIndex].value.validField};
}
};
I added it to form array ngOnInit of your code :-
ngOnInit() {
let frmArray = this.additionalForm.get("validations") as FormArray;
for (let data of this.validtionsField) {
frmArray.push(this.initSection(data));
}
frmArray.setValidators(customValidateArray());
}
Use it in template like :-
<div class="error" *ngIf="additionalForm.controls.validations.errors && validationForm.value.validField === additionalForm.controls.validations.errors.controlName">
{{additionalForm.controls.validations.errors.error}}
</div>
Working stackblitz :-
https://stackblitz.com/edit/angular6-material-components-demo-p1tpql

binding data to form array in angular

I have created an angular 7 app that has the ability to add and remove the controls dynamically. However, I am not sure how to bind the data to the form.
In the code attached below, I am trying to add and remove websites. I figured out the reason why the data was not getting bound to the UI elements. Websites under FirmDetails model is an array of objects. At the moment for testing, I have added websiteUrl: FirmDetails.Websites[0].WEBSITE_URL. What if I have more than one object. How do I bind then?
UI
<div class="form-group row">
<label for="inputEmail" class="col-md-1 col-form-label header">Websites</label>
<div class="col-md-9">
<div class="form-group row">
<div class="col-md-3">
<label for="inputEmail">Website URL</label>
</div>
<div class="col-md-3">
<label for="inputEmail">User Name</label>
</div>
<div class="col-md-3">
<label for="inputEmail">Password</label>
</div>
</div>
<div formArrayName="websites"
*ngFor="let item of frmFirm.get('websites').controls; let i = index; let last = last">
<div [formGroupName]="i">
<div class="form-group row">
<div class="col-md-3">
<input style="width:100%" formControlName="websiteUrl"
placeholder="Website Url">
</div>
<div class="col-md-3">
<input style="width:100%" formControlName="username" placeholder="User Name">
</div>
<div class="col-md-3">
<input style="width:100%" formControlName="password" placeholder="Password">
</div>
<div class="col-md-3">
<button (click)="removeWebsite()">Remove Website</button>
</div>
</div>
</div>
</div>
<button (click)="addWebsite()">Add Website</button>
</div>
</div>
Component
import { Component, Injectable, NgZone, ViewEncapsulation, ViewChild, Input } from '#angular/core';
import { OnInit } from '#angular/core';
import { FirmService } from '../services/firm.service';
import * as ClassicEditor from '#ckeditor/ckeditor5-build-classic';
import { CommonDataService } from '../services/common.data.service';
import { FormGroup, FormControl, FormBuilder, FormArray } from '#angular/forms';
import { ListItem } from '../models/listItem';
#Component({
selector: 'mgr-firm',
templateUrl: './firm.component.html'
})
export class FirmComponent implements OnInit {
private Error: string;
public FirmDetails: any;
public EditMode: boolean;
public Editor = ClassicEditor;
public EditorConfig: string;
public events: string[] = [];
#Input() FirmId: number;
DateFoundedDate: Date;
public frmFirm: FormGroup;
constructor(private _fb: FormBuilder, private firmService: FirmService, private commonDataService: CommonDataService) {
}
ngOnInit() {
this.initializeFormModel();
this.getFirmDetails();
}
initializeFormModel() {
this.frmFirm = this._fb.group({
firmName: [''],
shortName: [''],
alternateName: [''],
dateFounded: [''],
intraLinks: this._fb.array([
this.createCredentials()
]),
firmHistory: [''],
People: [''],
websites: this._fb.array([
this.createWebsite()
]),
addressess: this._fb.array([
this.createAddress()
])
});
}
// public addWebsite(): void {
// const websites = this.frmFirm.get('websites') as FormArray;
// websites.push(this._fb.control(''));
// }
public addWebsite(): void {
this.websites.push(this.createWebsite());
}
public removeWebsite(index: number): void {
const websites = this.frmFirm.get('websites') as FormArray;
websites.removeAt(index);
}
private createWebsite(): FormGroup {
return this._fb.group({
websiteUrl: [''],
username: [''],
password: ['']
});
}
public addAddress(): void {
this.addressess.push(this.createAddress());
}
public removeAddress(index: number): void {
const addressess = this.frmFirm.get('addressess') as FormArray;
addressess.removeAt(index);
}
private createAddress(): FormGroup {
return this._fb.group({
city: [''],
street: [''],
line2: [''],
line3: [''],
zipCode: [''],
phone: ['']
});
}
public addCredentials(): void {
this.intraLinks.push(this.createCredentials());
}
public removeCredentials(index: number): void {
const intraLinks = this.frmFirm.get('intraLinks') as FormArray;
intraLinks.removeAt(index);
}
private createCredentials(): FormGroup {
return this._fb.group({
intraUsername: [''],
intraPassword: ['']
});
}
get websites(): FormArray {
return <FormArray>this.frmFirm.get('websites');
}
get addressess(): FormArray {
return <FormArray>this.frmFirm.get('addressess');
}
get intraLinks(): FormArray {
return <FormArray>this.frmFirm.get('intraLinks');
}
get cities(): ListItem[] {
return JSON.parse(this.FirmDetails.LongCitiesJson).map(x => new ListItem(x.CITY_ID, x.CITY_NAME, null));
}
setFormValues(FirmDetails: any) {
this.frmFirm.patchValue({
firmName: FirmDetails.Firm.NAME,
shortName: FirmDetails.Firm.SHORT_NAME,
alternateName: FirmDetails.Firm.ALTERNATE_NAME,
dateFounded: this.getDate(FirmDetails.Firm.DATE_FOUNDED),
firmHistory: FirmDetails.Firm.HISTORY_HTML,
People: FirmDetails.People
});
const websiteGroup = this._fb.group({
// websiteUrl: FirmDetails.Websites[0].WEBSITE_URL,
// username: FirmDetails.Websites[0].USERNAME,
// password: FirmDetails.Websites[0].PASSWORD
websites: FirmDetails.Websites
});
this.frmFirm.setControl('websites', this._fb.array([websiteGroup]));
const addressGroup = this._fb.group({
city: this.FirmDetails.LongCitiesJson,
addressess: FirmDetails.Addresses
// street: FirmDetails.Firm.Addresses[0].LINE1,
// line2: FirmDetails.Firm.Addresses[0].LINE2,
// line3: FirmDetails.Firm.Addresses[0].LINE3,
// zipCode: FirmDetails.Firm.Addresses[0].POSTAL_CODE,
// phone: FirmDetails.Firm.Addresses[0].SWITCHBOARD_INT
});
this.frmFirm.setControl('addressess', this._fb.array([addressGroup]));
const intraLinksGroup = this._fb.group({
intraUsername: FirmDetails.Intralinks[2].USERNAME,
intraPassword: FirmDetails.Intralinks[2].PASSWORD
});
this.frmFirm.setControl('intraLinks', this._fb.array([intraLinksGroup]));
}
getFirmDetails() {
if (this.FirmId != null) {
this.firmService.getFirmDetails(this.FirmId)
.subscribe(data => {
this.FirmDetails = data;
this.setFormValues(this.FirmDetails);
},
err => {
this.Error = 'An error has occurred. Please contact BSG';
},
() => {
});
}
}
get dateFoundedDate(): string {
const dateString = this.FirmDetails.Firm.DATE_FOUNDED;
const results =parseInt(dateString.replace(/\/Date\(([0-9]+)[^+]\//i, "$1"));
const date = new Date(results);
const month = date.toLocaleString('en-us', { month: 'long' });
return (month + '-' + date.getFullYear());
}
private getDate(dateFounded: string): Date {
const results =parseInt(dateFounded.replace(/\/Date\(([0-9]+)[^+]\//i, "$1"));
const date = new Date(results);
return new Date (date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate());
}
saveManager() {
this.firmService.createFirm(this.FirmDetails)
.subscribe(data => {
this.getFirmDetails();
this.EditMode = !this.EditMode;
},
err => {
this.Error = 'An error has occurred. Please contact BSG';
},
() => {
});
}
dateFoundedChanged(dateFoundedDate: Date) {
this.DateFoundedDate = dateFoundedDate;
}
}
You can bind the array of values using patchValue like this,
setFormValues(FirmDetails: any) {
this.frmFirm.patchValue({
firmName: FirmDetails.Firm.NAME,
shortName: FirmDetails.Firm.SHORT_NAME,
alternateName: FirmDetails.Firm.ALTERNATE_NAME,
dateFounded: FirmDetails.Firm.DATE_FOUNDED,
firmHistory: FirmDetails.Firm.HISTORY_HTML,
People: FirmDetails.People,
websites: FirmDetails.Websites
});
}
since, the FirmDetails.Websites value is an array the value will get binded if you do like the above method
First you have to invoke the initializeFormModel() function and then
you have to invoke getFirmDetails() function in the ngOnInit() so
that the form gets initialized first and the value gets binded.
patchValue can also be done for form array

Angular: Cannot find control with name: 'date'

I am creating a form where a user select date and fill the form and subimit the form, here is what I have . for reference I am using igx-calendar
calendar component.ts:
import { Component, OnInit, forwardRef, Input, ElementRef, ViewChild } from '#angular/core';
import { IgxCalendarComponent, IgxDialogComponent } from 'igniteui-angular';
import { NgModel, FormControl, ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '#angular/forms';
#Component({
selector: 'app-calendar',
templateUrl: './calendar.component.html',
styleUrls: ['./calendar.component.scss'],
providers: [
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CalendarComponent), multi: true }
]
})
export class CalendarComponent implements ControlValueAccessor, OnInit {
#ViewChild('calendar') public calendar: IgxCalendarComponent;
#ViewChild('alert') public dialog: IgxDialogComponent;
#Input()
label: string;
private _theDate: string;
constructor() { }
propagateChange = (_: any) => { };
onTouched: any = () => { };
writeValue(obj: any): void {
console.log('writeValue => obj : ', obj);
if (obj) {
this._theDate = obj;
}
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
console.log('registerOnChange => fn : ', fn);
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
console.log('registerOnTouched => fn : ', fn);
}
get theDate() {
console.log('get theDate()');
return this._theDate;
}
set theDate(val) {
console.log('set theDate(val) - val => ', val);
this._theDate = val;
this.propagateChange(val);
}
public verifyRange(dates: Date[]) {
if (dates.length > 5) {
this.calendar.selectDate(dates[0]);
this.dialog.open();
}
}
ngOnInit() {
}
}
calender.html
<div class="sample-wrapper">
<div class="sample-content">
<!-- Single selection mode -->
<article class="sample-column calendar-wrapper">
<igx-calendar></igx-calendar>
</article>
</div>
</div>
Booking.component.ts UPDATE
export class BookingComponent implements OnInit {
comments: {};
addcomments: Comment[];
angForm: FormGroup;
// tslint:disable-next-line:max-line-length
validEmail = false;
constructor(private flashMessages: FlashMessagesService,
private fb: FormBuilder,
private activeRouter: ActivatedRoute,
private moviesService: MoviesService) {
this.comments = [];
this.createForm();
}
onChange(newValue) {
// tslint:disable-next-line:max-line-length
const validEmail = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (validEmail.test(newValue)) {
this.validEmail = true;
} else {
this.validEmail = false;
}
}
createForm() {
this.angForm = this.fb.group({
email: new FormControl('', [Validators.required, Validators.email])
});
}
addReview(date, email, city, hotel) {
this.moviesService.addReview(date, email, city, hotel).subscribe(success => {
this.flashMessages.show('You are data we succesfully submitted', { cssClass: 'alert-success', timeout: 3000 });
// get the id
this.activeRouter.params.subscribe((params) => {
// tslint:disable-next-line:prefer-const
let id = params['id'];
this.moviesService.getComments(id)
.subscribe(comments => {
console.log(comments);
this.comments = comments;
});
});
}, error => {
this.flashMessages.show('Something went wrong', { cssClass: 'alert-danger', timeout: 3000 });
});
}
ngOnInit() {
}
}
Booking.component.html
<div class="row about-booking">
<flash-messages></flash-messages>
<form [formGroup]="angForm" class="form-element">
<div class="col-sm-4 offset-sm-2 about-booking_calendar">
<div class="form-group form-element_date">
<app-calendar formControlName="date" [(ngModel)]="theDate" #date></app-calendar>
</div>
</div>
<div class="col-sm-4 about-booking_form">
<div class="form-group form-element_email">
<input type="email" class="form-control info" placeholder="Email" formControlName="email" #email (ngModelChange)="onChange($event)">
</div>
<div *ngIf="angForm.controls['email'].invalid && (angForm.controls['email'].dirty || angForm.controls['email'].touched)"
class="alert alert-danger">
<div *ngIf="angForm.controls['email'].errors.required">
Email is required.
</div>
</div>
<div class="input-group mb-3 form-element_city">
<select class="custom-select" id="inputGroupSelect01" #cityName>
<option selected *ngFor="let city of cities" [ngValue]="city.name">{{city.name}}</option>
</select>
</div>
<div class="input-group mb-3 form-element_hotel">
<select class="custom-select" id="inputGroupSelect01" #hotelName>
<option selected *ngFor="let hotel of hotels" [ngValue]="hotel.name">{{hotel.name}}</option>
</select>
</div>
<div class="form-group">
<button type="submit" (click)="addReview(date.value, email.value, cityName.value , hotelName.value)" class="btn btn-primary btn-block form-element_btn"
[disabled]="!validEmail">Book</button>
</div>
</div>
</form>
</div>
when I submit the data I get the following error:
BookingComponent.html:59 ERROR Error: Cannot find control with name:
'date'
what is wrong with my code?
in your createForm() function you didn't add the date FormControl
createForm() {
this.angForm = this.fb.group({
email: new FormControl('', [Validators.required, Validators.email]),
date: new FormControl('') // this line missing in your code
});
}
you shouldn't use both ngModel (template driven forms) and formControlName (reactive forms) in the same input.
In my case I was getting this same error for all the fields in the FormGroup, the reason was the data from server was NOT received and the html UI code attempted to executed the reactive form and hence the error. So to solve this issue I had to use the *ngIf condition on the form element and stopped the rendering of the form until the server response was completed.
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()" *ngIf="userProfile != undefined">

Categories