Property or method "urlImg" is not defined on the instance but referenced during render - forms

I'm trying to create a submit form, but all my v-model are giving me this error.
Here is one example of how I'm creating the form inputs:
<template>
<div class="card mx-xl-5">
<!-- Card body -->
<div class="card-body">
<div>
<form v-on:submit.prevent="pushLocation">
<div class="form-row">
<div class="col-md-6">
<label>Name:</label>
<input
type="text"
class="form-control"
v-model="txtLocationName"
/>
</div>
<div class="col-md-6"> <div class="card mx-xl-5">
<label>Description</label>
<input
type="text"
class="form-control"
v-model="txtLocDescription"
placeholder="Description"
/>
</div>
</div>
<div class="form-group">
<label for="type">Tipo de localização</label>
<select class="form-control" id="type" v-model="typeLoc">
<option value="museum">Museum</option>
<option value="restaurant">Restaurant</option>
<option value="tourist_attraction">Monument</option>
<option value="art_gallery">Galery</option>
</select>
</div>
<div class="col-md-6">
<label>Imagem</label>
<input type="url" class="form-control" v-model="urlImg" placeholder="Link" />
</div>
<div class="form-group row">
<div class="col-auto">
<button type="submit" class="btn btn-primary">Add</button>
</div>
</div>
</form>
</div>
</div>
And here is how I'm setting the properties on my script:
name: "LocationList",
data: function() {
return {
id: 0,
txtLocationName: "",
txtLocDescription: "",
typeLoc: "",
urlImg: "",
listLocation: [],
locationChecked: false
};},
methods: {
getLastLocationId() {
if (this.listLocation.length) {
return this.listLocation[this.listLocation.length - 1].id;
} else {
return 0;
}
},
checkLocationName() {
if (this.listLocation.length) {
for (const location of this.listLocation) {
if (location.Name == this.txtLocName) {
(this.locationChecked = false),
alert("nome de localização indisponivel");
} else {
this.userChecked = true;
}
}
}
},
pushLocation() {
this.checkLocationName();
this.getCoordenates();
if (this.locationChecked == true) {
this.$store.commit("ADD_USER", {
id: this.getLastLocationId() + 1,
Name: this.txtLocationName,
Description: this.txtLocDescription,
Type: this.typeLoc,
imgLink: this.urlImg
});
this.$router.push({ name: "adminLocations" });
} else {
alert("erro no registo");
}
}
}
I've done this type of forms multiple times, yet I can figure out why i'm having this error.I added the rest of my vue component. I get the error on all the property and methods, not only on "urlImg".Meaning all the properties that exist in the data and are in the form are giving me this error.
One last point, the final function, pushLocation() is also giving the error, saying that it is not a real function.

Related

VueJS form item json post

I have a form and when the "new iban" button is clicked in this form, he can add as many "iban" inputs as he wants.
in the same way, when the "add item" button is clicked, as many entries can be added and "SPINNET" can be seen. But when I post them, the input values ​​return empty. I want it to be sent in "iban" and "authoritative" json format.
new Vue({
el: "#app",
data() {
return {
data: {
name: "",
iban: "",
yetkili: "",
},
sections: [{
ibans: [{
item: ""
}]
}],
yetkisection: [{
yetkililer: [{
yetkiliadi: "",
yetkilieposta: "",
yetkilitelefon: "",
yetkilinotu: "",
}]
}],
}
},
methods: {
addNewItem(id) {
this.sections[id].ibans.push({
item: ''
});
},
removeItem(sectionIndex, ibanIndex) {
this.sections[sectionIndex].ibans.splice(ibanIndex, 1);
},
addNewYetkili(id) {
this.yetkisection[id].yetkililer.push({
yetkiliadi: ''
});
},
removeYetkili(sectionIndex, ibanIndex) {
this.yetkisection[sectionIndex].yetkililer.splice(ibanIndex, 1);
},
async addTag() {
const res = await this.callApi('post', 'app/create_musteri', this.data)
if (res.status === 200) {
console.log(res)
} else {
console.log("error");
}
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<template>
<div class="container">
<div class="col-12">
<div class="mb-1 row">
<div class="col-sm-3">
<label class="col-form-label"><span><i data-feather='file-text'></i></span>NAME</label>
</div>
<div class="col-sm-6">
<input type="text" class="form-control" placeholder="Item" v-model="data.name">
</div>
</div>
</div>
<div class="col-12 sections" v-for="(section, sectionIndex) in sections">
<div class="mb-1 row" v-for="(iban, ibanIndex) in section.ibans">
<div class="col-sm-3">
<label class="col-form-label" for="iban"><span><i data-feather='file-text'></i></span>IBAN NUMBER</label>
</div>
<div class="col-sm-6">
<input type="text" class="form-control" placeholder="Item" v-model="iban.item">
<span v-if="section.ibans.length>1"
class="float-right" style="cursor:pointer" #click="removeItem(sectionIndex, ibanIndex)">X</span>
</div>
</div>
<button class="btn btn-success mt-5 mb-5" #click="addNewItem(sectionIndex)">New Iban</button>
</div>
<div>
<div>
<table class="fatura-table">
<colgroup>
<col style="width: 25%;;">
<col style="width: 25%;;">
<col style="width: 25%;;">
<col style="width: ;">
<col style="width: 70px;">
</colgroup>
<thead>
<tr>
<th>YETKİLİ KİŞİNİN ADI</th>
<th>E-POSTA</th>
<th>TELEFON</th>
<th>NOTLAR</th>
<th></th>
</tr>
</thead>
</table>
</div>
<section v-for="(section, sectionIndex) in yetkisection">
<div class="card-body" v-for="(yetkili, ibanIndex) in section.yetkililer">
<div class="row">
<div class="col-sm-3 col-12 mb-1 mb-sm-0 hizmet">
<div class="form-floating">
<input type="text" class="form-control" v-model="yetkili.yetkiliadi" placeholder=" "/>
<label>YETKİLİ KİŞİNİN ADI</label>
</div>
</div>
<div class="col-sm-3 col-12 mb-1 mb-sm-0 hizmet">
<div class="form-floating">
<input type="text" class="form-control" v-model="yetkili.yetkilieposta" placeholder=" "/>
<label>E-POSTA</label>
</div>
</div>
<div class="col-sm-2 col-12 mb-1 mb-sm-0 hizmet">
<div class="form-floating">
<input type="text" class="form-control" v-model="yetkili.yetkilitelefon" placeholder=" "/>
<label>TELEFON</label>
</div>
</div>
<div class="col-sm-3 col-12 mb-1 mb-sm-0 hizmet">
<div class="form-floating">
<input type="text" class="form-control" v-model="yetkili.yetkilinotu" placeholder=" "/>
<label>NOTLAR</label>
</div>
</div>
<div class="col-sm-1 col-12 mb-1 mb-sm-0">
<button class="btn btn-icon btn-secondary waves-effect waves-float waves-light" #click="removeYetkili(sectionIndex, ibanIndex)">
<i data-feather='x'>DELETE</i>
</button>
</div>
</div>
</div>
<div class="mt-1">
<div class="col-12 px-0">
<button type="button" class="btn btn-primary btn-sm btn-add-new" data-repeater-create #click="addNewYetkili(sectionIndex)">
<i data-feather="plus" class="me-25"></i>
<span class="align-middle">Add Item</span>
</button>
</div>
</div>
</section>
</div>
<button #click="addTag" class="btn btn-dark data-submit">SAVE</button>
</div>
</template>
</div>
Edited my answer to handle your new build and structure.
Showing both the approaches below:
new Vue({
el: "#app",
data() {
return {
data: {
name: "",
iban: "",
yetkili: ""
},
sections: [{ item: "" }],
yetkisections: [
{
yetkiliadi: "",
yetkilieposta: "",
yetkilitelefon: "",
yetkilinotu: ""
}
]
};
},
methods: {
addNewItem() {
this.sections.push({ item: "" });
},
removeItem(sectionIndex) {
this.sections.splice(sectionIndex, 1);
},
addNewYetkili() {
this.yetkisections.push({ yetkiliadi: "" });
},
removeYetkili(yetkisectionIndex) {
this.yetkisections.splice(yetkisectionIndex, 1);
},
async addTag() {
/* const res = await this.callApi('post', 'app/create_musteri', this.formData)
if (res.status === 200) {
console.log(res)}
else {
console.log("error");
} */
console.log(this.$data);
console.log(this.formData);
}
},
computed: {
formData() {
return {
data: {
...this.data,
sections: this.sections,
yetkisection: this.yetkisections
}
};
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<template>
<div class="container">
<div class="col-12">
<div class="mb-1 row">
<div class="col-sm-3">
<label class="col-form-label"
><span><i data-feather="file-text"></i></span>NAME</label
>
</div>
<div class="col-sm-6">
<input
type="text"
class="form-control"
placeholder="Item"
v-model="data.name"
/>
</div>
</div>
</div>
<div
class="col-12 sections"
v-for="(section, sectionIndex) in sections"
:key="sectionIndex"
>
<div class="col-sm-3">
<label class="col-form-label" for="iban"
><span><i data-feather="file-text"></i></span>IBAN NUMBER</label
>
</div>
<div class="col-sm-6">
<input
type="text"
class="form-control"
placeholder="Item"
v-model="section.item"
/>
<span
v-if="sections.length > 1"
class="float-right"
style="cursor: pointer"
#click="removeItem(sectionIndex)"
>X</span
>
</div>
</div>
<button class="btn btn-success mt-5 mb-5" #click="addNewItem()">
New Iban
</button>
<div>
<div>
<table class="fatura-table">
<colgroup>
<col style="width: 25%;;" />
<col style="width: 25%;;" />
<col style="width: 25%;;" />
<col style="width: ;" />
<col style="width: 70px;" />
</colgroup>
<thead>
<tr>
<th>YETKİLİ KİŞİNİN ADI</th>
<th>E-POSTA</th>
<th>TELEFON</th>
<th>NOTLAR</th>
<th></th>
</tr>
</thead>
</table>
</div>
<section>
<div
class="card-body"
v-for="(yetkili, yetkisectionIndex) in yetkisections"
:key="yetkisectionIndex"
>
<div class="row">
<div class="col-sm-3 col-12 mb-1 mb-sm-0 hizmet">
<div class="form-floating">
<input
type="text"
class="form-control"
v-model="yetkili.yetkiliadi"
placeholder=" "
/>
<label>YETKİLİ KİŞİNİN ADI</label>
</div>
</div>
<div class="col-sm-3 col-12 mb-1 mb-sm-0 hizmet">
<div class="form-floating">
<input
type="text"
class="form-control"
v-model="yetkili.yetkilieposta"
placeholder=" "
/>
<label>E-POSTA</label>
</div>
</div>
<div class="col-sm-2 col-12 mb-1 mb-sm-0 hizmet">
<div class="form-floating">
<input
type="text"
class="form-control"
v-model="yetkili.yetkilitelefon"
placeholder=" "
/>
<label>TELEFON</label>
</div>
</div>
<div class="col-sm-3 col-12 mb-1 mb-sm-0 hizmet">
<div class="form-floating">
<input
type="text"
class="form-control"
v-model="yetkili.yetkilinotu"
placeholder=" "
/>
<label>NOTLAR</label>
</div>
</div>
<div class="col-sm-1 col-12 mb-1 mb-sm-0">
<button
class="btn btn-icon btn-secondary waves-effect waves-float waves-light"
#click="removeYetkili(yetkisectionIndex)"
>
<i data-feather="x">DELETE</i>
</button>
</div>
</div>
</div>
<div class="mt-1">
<div class="col-12 px-0">
<button
type="button"
class="btn btn-primary btn-sm btn-add-new"
data-repeater-create
#click="addNewYetkili()"
>
<i data-feather="plus" class="me-25"></i>
<span class="align-middle">Add Item</span>
</button>
</div>
</div>
</section>
</div>
<button #click="addTag" class="btn btn-dark data-submit">SAVE</button>
</div>
</template>
</div>

Add class if field is already filled VueJS

I am new in Vue and I need to add a new class to div.input-box where inside is label and input.
I can try more options after google but nothing to help me.
html:
<form id="form" class="container">
<div class="row justify-content-center mt-5">
<div class="input-box col-lg-6" #click="isActive = 1" :class="{'focus': isActive === 1}">
<label class="input-label">
{{ $t("system.first_name") }}
</label>
<input type="text" class="input-1" v-model="first_name" #focus="isActive = true" #blur="isActive = false">
</div>
<div class="input-box col-lg-6" #click="isActive = 2" :class="{'focus': isActive === 2}">
<label class="input-label">
{{ $t("system.last_name") }}
</label>
<input type="text" class="input-1" v-model="last_name" #focus="isActive = true" #blur="isActive = false">
</div>
</div>
<div class="row justify-content-center">
<div class="input-box col-lg-12" #click="isActive = 3" :class="{'focus': isActive === 3}">
<label class="input-label">
E-mail
</label>
<input type="text" class="input-1" v-model="email" #focus="isActive = true" #blur="isActive = false">
</div>
</div>
</form>
script:
export default {
name: "RegistrationSecondVersion",
data(){
return {
isActive: false,
first_name: null,
last_name: null,
email: null
}
},
methods: {
checkForInput: function (e) {
let input = e.target;
if (input.value != '') {
input.classList.add('focus');
} else {
input.classList.remove('focus');
}
}
}
}
I had added the function in input but I need after filled to get 'focus' class to div.input-box.
Thank you so much!
You need to call checkForInput function whenever an input occurs. On the other hand, I see no need to use #blur and #focus in your case.
Try this:
<form id="form" class="container">
<div class="row justify-content-center mt-5">
<div class="input-box col-lg-6" #click="isActive = 1" :class="{'focus': isActive === 1}">
<label class="input-label">
{{ $t("system.first_name") }}
</label>
<input type="text" class="input-1" v-model="first_name" #input="checkForInput">
</div>
<div class="input-box col-lg-6" #click="isActive = 2" :class="{'focus': isActive === 2}">
<label class="input-label">
{{ $t("system.last_name") }}
</label>
<input type="text" class="input-1" v-model="last_name" #input="checkForInput">
</div>
</div>
<div class="row justify-content-center">
<div class="input-box col-lg-12" #click="isActive = 3" :class="{'focus': isActive === 3}">
<label class="input-label">
E-mail
</label>
<input type="text" class="input-1" v-model="email" #input="checkForInput">
</div>
</div>
</form>

Angular 2 POST JSON specific data from form

I have JSON model on backand:
{
"addresses": [
{
"maxTravelTime": 0,
"travelTypeId": 0
}
],
"categoryId": 0,
"maxPrice": 0,
"preferences": [
{
"categoryId": 0,
"travelTypeId": 0,
"travelTime": 0
}
]
}
and I have my ts code:
initForm() {
this.myForm = this._fb.group({
type: '',
category: '',
price: '',
rooms: '',
preferences: this._fb.array([this.initLocationRows()]),
addresses: this._fb.array([this.initAddressRows()]),
});
}
onSubmit(){
console.log(this.myForm.value);
}
html -
<form [formGroup]="searchForm" (ngSubmit)="onSubmit()">
<div class="col-md-10 panel">
<div class="col-md-2">
<div class="form-group">
<label for="type">Type</label>
<select class="form-control" id="type" name="type" formControlName="type">
<option *ngFor="let t of types" [ngValue]="t">{{t.typ}}</option>
</select>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="category">Category</label>
<select class="form-control" id="category" formControlName="category">
<option *ngFor="let c of categories" [ngValue]="c">{{c.cat}}</option>
</select>
</div>
</div>
</div>
<div formArrayName="locationRows">
<div class="row" *ngFor="let itemrow of searchForm.controls.locationRows.controls; let i=index" [formGroupName]="i">
<div class="col-md-3">
<div class="form-group">
<label for="category">Nearby</label>
<select class="form-control" id="nearby" formControlName="nearby">
<option *ngFor="let p of preferences" [ngValue]="p">{{p.pref}}</option>
</select>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="maxtime">Max travel time</label>
<select class="form-control" id="maxtime" formControlName="maxtime">
<option *ngFor="let t of maxTime" [ngValue]="t">{{t.int}}</option>
</select>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="travel">Travel type</label>
<select class="form-control" id="travel" formControlName="travel">
<option *ngFor="let tt of travelType" [ngValue]="tt">{{tt.tr}}</option>
</select>
</div>
</div>
<div class="col-md-1">
<div class="form-group">
<button style="margin-top: 30px;" *ngIf="searchForm.controls.locationRows.controls.length > 1" (click)="removeLocationRow(i)" class="btn btn-danger btn-xs">-</button>
</div>
</div>
</div>
</div>
</form>
console log form data looks like:
addresses: Array [ {…} ]
preferences: Array [ {…}, {…} ]
category: Object { id: 3, cat: "Office" }
price: "100"
rooms: "All"
type: Object { id: 2, typ: "Rent" }
How to get specific controls value from this myForm.value and add it to JSON according to JSON model?
myForm.value is an Object so you can access it's properties like any other object, something like myForm.value.addresses , so if you need only specific value just call it

Angular FormGroup custom validator not triggered on button click

I am using reactive forms Angular 4 and added a custom validator addressValidation to the form group - addressGroup.
I am updating all fields to mark as touched on submit click. Looks like the custom validator addressValidation doesn't trigger eventhough I marked all fields as touched. I tried marking the formgroup (addressGroup) as touched and dirty on submit but no help.
In general what I am trying to achieve is - By default I want to make street number and Street name required. If po box is entered then street number and Name is not required. Apt # is only required only if street number and name is entered. I am trying to achieve this on the custom validator in the formGroup.
Any idea on what I am doing wrong. Any other alternate way to achieve the above requirement. I am new to Angular and slowly learning the concepts. Any suggestion on How to trigger the custom validator on submit.
buildForm(): void {
this.contactForm = this.fb.group({
emailAddressControl: ['', [Validators.required, Validators.email, Validators.maxLength(100)]],
phoneControl: ['', [Validators.required, Validators.minLength(10), Validators.maxLength(10)]],
addressGroup: this.fb.group({
streetNumber: ['', [Validators.maxLength(10)]],
pOBox: ['', [Validators.maxLength(8)]],
aptNumber: ['', [Validators.maxLength(8)]],
streetName: ['', [Validators.maxLength(60)]],
cityControl: ['', [Validators.required, Validators.maxLength(50)]],
stateControl: ['', [Validators.required, Validators.maxLength(2)]],
zipControl: ['', [Validators.required, Validators.maxLength(14)]],
countryControl: ['UNITED STATES OF AMERICA', [Validators.required]],
}, { validator: addressValidation })
})
this.contactForm.valueChanges
.debounceTime(800)
.subscribe(data => this.onValueChanged(data));
this.onValueChanged();
}
onSubmit(): void {
this.markAllFormFieldsAsTouched(this.contactForm);
this.onValueChanged();
}
private markAllFormFieldsAsTouched(formGroup: FormGroup) {
Object.keys(formGroup.controls).forEach(field => {
console.log(field);
const control = formGroup.get(field);
if (control instanceof FormControl) {
control.markAsTouched({ onlySelf: true });
}
else if (control instanceof FormGroup) {
this.markAllFormFieldsAsTouched(control);
control.markAsTouched({ onlySelf: true });
}
else if (control instanceof FormArray) {
for (let formgroupKey in control.controls) {
let formgroup = control.controls[formgroupKey];
if (formgroup instanceof FormGroup) {
this.markAllFormFieldsAsTouched(formgroup);
}
}
}
});
}
function addressValidation(c: AbstractControl): { [key: string]: boolean } | null {
if (c.pristine) {
return null;
}
const pOBoxControl = c.get('pOBox');
const streetNameControl = c.get('streetName');
const streetNumberControl = c.get('streetNumber');
const aptNumberControl = c.get('aptNumber');
if (pOBoxControl.value === null || pOBoxControl.value === "") {
if (streetNumberControl.value === null || streetNumberControl.value === "") {
return { ['streetNumberRequired']: true, ['streetNameRequired']: true };
}
if (streetNameControl.value === null || streetNameControl.value === "") {
return { 'streetNameRequired': true };
}
}
else {
if ((streetNameControl.value === null || streetNameControl.value === "")
&& (streetNameControl.value === null || streetNumberControl.value === "") && aptNumberControl.value !== "") {
return { 'apartmentNumberInvalid': true };
}
}
}
Template
<div class="card">
<div class="card-header bg-info text-white">
<h2>Mailing Address:</h2>
</div>
<div formGroupName="addressGroup" class="card-body">
<div class="row">
<div class="col-lg-4">
<div class="form-group">
<label class="form-control-label">PO Box:</label>
<input class="form-control"
[ngClass]="displayFieldCss('pOBox')"
type="text"
formControlName="pOBox"
placeholder=""
maxlength="8" />
<span class="invalid-feedback" *ngIf="isValidToDisplayErrors('pOBox')">
{{validationMessage.pOBox}}
</span>
</div>
</div>
<div class="col-lg-4">
<div class="form-group">
<label class="form-control-label">Street Number:</label>
<input class="form-control"
[ngClass]="displayFieldCss('streetNumber')"
type="text"
formControlName="streetNumber"
placeholder=""
maxlength="10" />
<span class="invalid-feedback" *ngIf="isValidToDisplayErrors('streetNumber')">
{{validationMessage.streetNumber}}
</span>
</div>
</div>
<div class="col-lg-4">
<div class="form-group">
<label class="form-control-label">Apt Number:</label>
<input class="form-control"
[ngClass]="displayFieldCss('aptNumber')"
type="text"
formControlName="aptNumber"
placeholder=""
maxlength="8" />
<span class="invalid-feedback" *ngIf="isValidToDisplayErrors('aptNumber')">
{{validationMessage.aptNumber}}
</span>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="form-group">
<label class="form-control-label">Street Name:</label>
<input class="form-control"
[ngClass]="displayFieldCss('streetName')"
type="text"
formControlName="streetName"
placeholder=""
maxlength="60" />
<span class="invalid-feedback" *ngIf="isValidToDisplayErrors('streetName')">
{{validationMessage.streetName}}
</span>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-5">
<div class="form-group">
<label class="form-control-label">City:</label>
<input class="form-control"
[ngClass]="displayFieldCss('cityControl')"
type="text"
formControlName="cityControl"
placeholder="(required)"
maxlength="50" />
<span class="invalid-feedback" *ngIf="isValidToDisplayErrors('cityControl')">
{{validationMessage.cityControl}}
</span>
</div>
</div>
<div class="col-lg-4">
<div class="form-group">
<label class="form-control-label">State/Province (Code):</label>
<input class="form-control"
[ngClass]="displayFieldCss('stateControl')"
type="text"
formControlName="stateControl"
placeholder="(required)"
maxlength="3" />
<span class="invalid-feedback" *ngIf="isValidToDisplayErrors('stateControl')">
{{validationMessage.stateControl}}
</span>
</div>
</div>
<div class="col-lg-3">
<div class="form-group">
<label class="form-control-label">Zip:</label>
<input class="form-control"
[ngClass]="displayFieldCss('zipControl')"
type="text"
formControlName="zipControl"
placeholder="(required)"
maxlength="14" />
<span class="invalid-feedback" *ngIf="isValidToDisplayErrors('zipControl')">
{{validationMessage.zipControl}}
</span>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="form-group">
<label class="form-control-label">Country:</label>
<input class="form-control"
[ngClass]="displayFieldCss('countryControl')"
type="text"
formControlName="countryControl"
placeholder="(required)"
maxlength="50" />
<span class="invalid-feedback" *ngIf="isValidToDisplayErrors('countryControl')">
{{validationMessage.countryControl}}
</span>
</div>
</div>
</div>
</div>
</div>
You must use control.updateValueAndValidity() like this
onSubmit(): void {
if (this.form.valid) {
} else {
this.validateAllFormFields(this.committeForm);
this.logValidationErrors(this.committeForm);
this.scrollToError();
}
}
validateAllFormFields(formGroup: FormGroup) {
Object.keys(formGroup.controls).forEach(field => {
const control = formGroup.get(field);
if (control instanceof FormControl) {
control.updateValueAndValidity()
} else if (control instanceof FormGroup) {
this.validateAllFormFields(control);
}
});
}

Bootstrap form validation not working in angular2

I have implemented bootstrap form validation in my angular2 application and some of the form elements are not getting validated even after using the bootstrap form validation directive properly. When I tried inspecting the code, I found the "has-error" class is not appearing in the form fields.
Here is my form code:
<form id="emailForm"
#emailForm= "ngForm"
novalidate="novalidate"
[saBootstrapValidator]="validatorOptions">
<!-- widget content -->
<div class="widget-body">
<!--template Name -->
<fieldset>
<div class="form-group">
<div class="row">
<section class="col-sm-12 col-md-10">
<strong class="control-label">Name</strong><label class="required"></label>
<input class="form-control"
placeholder="Enter template name"
type="text"
value="{{message.name}}"
name="templateName" [(ngModel)]="composeModel.Name">
</section>
<section class="col-sm-12 col-md-2">
<label for="select2-multiple">
<strong>Category</strong></label>
<select
style="width:100%"
class="form-control select2"
name="category">
<option *ngFor = "let category of templateCategories" value="{{category.Id}}">{{category.Name}}</option>
</select>
</section>
</div>
</div>
</fieldset>
<!--Description -->
<fieldset>
<div class="form-group">
<div class="row">
<section class="col-sm-12 col-md-12 ">
<strong>Description</strong>
<textarea class="form-control" name="review" rows="2" placeholder="Enter a brief summary that decribes the puropose of this template " [(ngModel)]="composeModel.Description"></textarea>
</section>
</div>
</div>
</fieldset>
<!-- To -->
<fieldset>
<div class="form-group">
<div class="row">
<section class="col-md-12">
<strong class="control-label">To</strong><label class="required"></label>
<popover-content #multSelectContent>
<span style="color: #3276B1">Hold ctrl-key to select multiple emails<br /> Start typing to search for email</span>
</popover-content>
<a (click)="(null)"
class="fa fa-question-circle"
popoverPlacement="top"
[popoverOnHover]="true"
data-html="true"
popoverTitle="Multiple Selection"
[popover]="multSelectContent"></a>
<div class="boxed">
<select
multiple select2 style="width:100%"
data-select-search="true"
class="form-control select2-multiple"
name="emailTo">
<optgroup label="Responsible Parties">
<option value="1"> sunny.orlaf#smartadmin.com</option>
<option value="2"> rachael.hawi#smartadmin.com</option>
<option value="3"> michael.safiel#smartadmin.com</option>
</optgroup>
<optgroup label="Evaluator">
<option value="alex.jones#infowars.com"> alex.jones#infowars.com</option>
<option value="oruf.matalla#gmail.com"> oruf.matalla#gmail.com</option>
<option value="hr#smartadmin.com"> hr#smartadmin.com</option>
<option value="IT#smartadmin.com"> IT#smartadmin.com</option>
</optgroup>
<optgroup label="Informed Parties">
<option value="alex.jones#infowars.com"> alex1.jones#infowars.com</option>
<option value="oruf.matalla#gmail.com"> oruf1.matalla#gmail.com</option>
<option value="hr#smartadmin.com"> hr1#smartadmin.com</option>
<option value="IT#smartadmin.com"> IT1#smartadmin.com</option>
</optgroup>
</select>
</div>
</section>
</div>
</div>
</fieldset>
<!-- CC -->
<fieldset>
<div class="form-group">
<div class="row">
<section class="col-sm-12 col-md-6">
<label><strong>CC</strong></label>
<popover-content #multSelectContent1>
<span style="color: #3276B1">Hold ctrl-key to select multiple emails<br /> Start typing to search for email</span>
</popover-content>
<a (click)="(null)" class="fa fa-question-circle" popoverPlacement="top" [popoverOnHover]="true" data-html="true" popoverTitle="Multiple Selection" [popover]="multSelectContent1"></a>
<div class="boxed">
<select multiple select2 style="width:100%" data-select-search="true" class="select2">
<optgroup label="Responsible Parties">
<option value="sunny.orlaf#smartadmin.com">sunny.orlaf#smartadmin.com</option>
<option value="rachael.hawi#smartadmin.com">rachael.hawi#smartadmin.com</option>
<option value="michael.safiel#smartadmin.com">michael.safiel#smartadmin.com</option>
</optgroup>
<optgroup label="Evaluator">
<option value="alex.jones#infowars.com">alex.jones#infowars.com</option>
<option value="oruf.matalla#gmail.com">oruf.matalla#gmail.com</option>
<option value="hr#smartadmin.com">hr#smartadmin.com</option>
<option value="IT#smartadmin.com">IT#smartadmin.com</option>
</optgroup>
<optgroup label="Informed Parties">
<option value="alex.jones#infowars.com">alex1.jones#infowars.com</option>
<option value="oruf.matalla#gmail.com">oruf1.matalla#gmail.com</option>
<option value="hr#smartadmin.com">hr1#smartadmin.com</option>
<option value="IT#smartadmin.com">IT1#smartadmin.com</option>
</optgroup>
</select>
</div>
</section>
<!-- BCC -->
<section class="col-sm-12 col-md-6">
<label><strong>BCC</strong></label>
<popover-content #multSelectContent1>
<span style="color: #3276B1">Hold ctrl-key to select multiple emails<br /> Start typing to search for email</span>
</popover-content>
<a (click)="(null)" class="fa fa-question-circle" popoverPlacement="top" [popoverOnHover]="true" data-html="true" popoverTitle="Multiple Selection" [popover]="multSelectContent1"></a>
<div class="boxed">
<select multiple select2 style="width:100%" data-select-search="true" class="select2">
<optgroup label="Responsible Parties">
<option value="sunny.orlaf#smartadmin.com">sunny.orlaf#smartadmin.com</option>
<option value="rachael.hawi#smartadmin.com">rachael.hawi#smartadmin.com</option>
<option value="michael.safiel#smartadmin.com">michael.safiel#smartadmin.com</option>
</optgroup>
<optgroup label="Evaluator">
<option value="alex.jones#infowars.com">alex.jones#infowars.com</option>
<option value="oruf.matalla#gmail.com">oruf.matalla#gmail.com</option>
<option value="hr#smartadmin.com">hr#smartadmin.com</option>
<option value="IT#smartadmin.com">IT#smartadmin.com</option>
</optgroup>
<optgroup label="Informed Parties">
<option value="alex.jones#infowars.com">alex1.jones#infowars.com</option>
<option value="oruf.matalla#gmail.com">oruf1.matalla#gmail.com</option>
<option value="hr#smartadmin.com">hr1#smartadmin.com</option>
<option value="IT#smartadmin.com">IT1#smartadmin.com</option>
</optgroup>
</select>
</div>
</section>
</div>
</div>
</fieldset>
<!--Subject -->
<fieldset>
<div class="form-group">
<div class="row">
<section class="col-md-12 ">
<label><strong>Subject</strong></label>
<popover-content #multSelectContent>
<span style="color: #3276B1">Hold ctrl-key to select multiple emails<br /> Start typing to search for email</span>
</popover-content>
<a (click)="(null)"
class="fa fa-question-circle"
popoverPlacement="top"
[popoverOnHover]="true"
data-html="true"
popoverTitle="Multiple Selection"
[popover]="multSelectContent"></a>
<div class="boxed">
<select
multiple select2 style="width:100%"
data-select-search="true"
class="form-control select2-multiple"
name="emailTo"
placeholder = "Email Subject">
<optgroup label="Evaluation type title">
</optgroup>
<optgroup label="Topic">
<option value="topic1">topic1</option>
<option value="topic2">topic2</option>
</optgroup>
<optgroup label="Question">
<option value="question1">Question1</option>
<option value="question2">Question2</option>
</optgroup>
<optgroup label="Response">
<option value="response1">response1</option>
<option value="response2">response2</option>
</optgroup>
<optgroup label="Value">
<option value="value1">value1</option>
<option value="value2">value2</option>
</optgroup>
<optgroup label="Today's Date'">
<option value="4.5.2017">4.5.2017</option>
</optgroup>
</select>
</div>
<!--<input class="form-control" placeholder="Email Subject" type="text" value="{{message.subject}}">-->
</section>
</div>
</div>
</fieldset>
<!--Message -->
<fieldset>
<div class="form-group">
<div class="row">
<section class="col-md-12 ">
<strong>Message</strong><label class="control-label required"></label>
<div class="summernote form-control" name="messageBody" [summernote]="{height: 120}"></div>
</section>
</div>
</div>
</fieldset>
<!--Action buttons -->
<fieldset>
<div class="smart-form pull-right">
<section class="col-md-12 p ">
<div class="btn-group">
<button class="btn btn-sm btn-primary" type="button"><i class="fa fa-times"></i> Cancel </button>
</div>
<div class="btn-group">
<button class="btn btn-sm btn-success" type="submit" [disabled]="!emailForm.invalid" (click)="onSubmit()"><i class="fa fa-save"></i> Save </button>
</div>
</section>
</div>
</fieldset>
</div>
<!-- end widget content -->
</form>
This is how my form appears :
Here is my component.ts code:
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute, Router } from '#angular/router';
import { OutlookService, Message } from '../shared';
import { EmailComposeService } from '../compose/compose.service';
import { EmailComposeModal } from '../../data-model/email-compose';
declare var $: any;
#Component({
selector: 'sa-compose',
templateUrl: 'compose.component.html',
})
export class ComposeComponent implements OnInit {
public $: any;
validatorOptions = {
feedbackIcons: {
valid: 'glyphicon glyphicon-ok',
invalid: 'glyphicon glyphicon-remove',
validating: 'glyphicon glyphicon-refresh'
},
fields: {
templateName: {
validators: {
notEmpty: {
message: 'The template name is required and cannot be empty'
}
}
},
emailTo: {
validators: {
notEmpty: {
message: 'The sender email is required and cannot be empty'
}
}
},
messageBody: {
validators: {
callback: {
message: 'The content is required and cannot be empty',
callback: function (value, validator, $field) {
var code = $('[name="messageBody"]').summernote('code');
// <p><br></p> is code generated by Summernote for empty content
return (code !== '' && code !== '<p><br></p>');
}
}
}
}
}
};
public templateName: String = '';
public message: Message;
public carbonCopy: boolean = false;
public blindCarbonCopy: boolean = false;
public attachments: boolean = false;
public sending: boolean = false;
templateCategories: any;
constructor(private route: ActivatedRoute,
private router: Router,
private service: OutlookService, private composeservice: EmailComposeService) {
}
ngOnInit() {
this.message = new Message({})
this.composeservice.getTempateCategory(1, JSON.parse(localStorage.getItem('currentUser')).access_token).subscribe(data => {
this.templateCategories = data;
this.composeModel.CategoryId = data[0].Id;
});
}
isUpdate: boolean = false;
isDelete: boolean = false;
isCreateTemplate: boolean = false;
submitted = false;
composeModel = new ComposeClass();
onSubmit() {
//this.uploadImage();
if (this.isUpdate) {
let composePutData: EmailComposeModal = {
Id: +this.composeModel.Id,
IndustryId: +this.composeModel.IndustryId,
CategoryId: +this.composeModel.CategoryId,
Name: this.composeModel.Name,
Content: this.composeModel.Content,
Description: this.composeModel.Description,
Status: this.composeModel.Status,
CreatedBy: this.composeModel.CreatedBy,
CreateTimestamp: this.composeModel.CreateTimestamp,
ModifiedBy: localStorage.getItem('selectedRolesID'),
ModifiedTimestamp: new Date(),
DataStateFlag: this.composeModel.DataStateFlag
}
if (this.isDelete) {
this.composeservice.updateTemplate(composePutData, JSON.parse(localStorage.getItem('currentUser')).access_token).subscribe(res => {
if (res.status === 200) {
this.isUpdate = false;
this.isDelete = false;
this.composeModel = new ComposeClass();
this.isCreateTemplate = false;
$('.divMessageBox').hide();
}
});
} else {
this.composeservice.updateTemplate(composePutData, JSON.parse(localStorage.getItem('currentUser')).access_token).subscribe(res => {
if (res.status === 200) {
this.isUpdate = false;
this.composeModel = new ComposeClass();
this.isCreateTemplate = false;
$('.divMessageBox').hide();
}
});
}
} else {
var code = $('[name="messageBody"]').summernote('code');
let postdata: EmailComposeModal = {
IndustryId: 1,
CategoryId: +this.composeModel.CategoryId,
Name: this.composeModel.Name,
Content: code,
Description: this.composeModel.Description,
Status: "Active",
CreatedBy: localStorage.getItem('selectedRolesID'),
CreateTimestamp: new Date(),
DataStateFlag: "I",
"ModifiedBy": null,
"ModifiedTimestamp": null
}
this.submitted = true;
this.composeservice.createTemplate(postdata, JSON.parse(localStorage.getItem('currentUser')).access_token).subscribe(res => {
if (res.status === 200) {
this.composeModel = new ComposeClass();
this.isCreateTemplate = true;
$('.nav-tabs a[href="#a1"]').tab('show');
$('[name="messageBody"]').summernote('reset');
}
})
}
}
}
export class ComposeClass {
constructor(
public Id?: number,
public IndustryId?: number,
public CategoryId?: number,
public Name?: string,
public Content?: string,
public Description?: string,
public Status?: string,
public CreatedBy?: string,
public CreateTimestamp?: Date,
public ModifiedBy?: string,
public ModifiedTimestamp?: Date,
public DataStateFlag?: string
) {
}
}
The first input element is getting getting validated but the select fields and summernote fields are not getting validated.
I have finally resolved it.
The select2 validation would work simply if the excluded: [':disabled'] is added to the validatorOptions. It checks for null value.
Regarding the summernote, adding the following code to onInit() solved the issue :
$(".summernote")
.on('summernote.change', function(customEvent, contents, $editable) {
// Revalidate the content when its value is changed by Summernote
$('#emailForm').bootstrapValidator('revalidateField', 'content');
Hope this helps!