below is HTML code for form
<div class="form-group">
<label for="email">Email</label>
<input type="email" class="form-control"
(blur)="suggestEmail(signupForm.controls['userData'].controls.email.value)"
id="email" formControlName="email">
<span class="help-block" *ngIf="!signupForm.get('userData.email').valid && signupForm.get('userData.email').touched">
please enter a valid email id
</span>
</div>
Below is ts code
constructor(private fb: FormBuilder) {
this.signupForm = this.fb.group({
userData: this.fb.group({
email: [null, [Validators.required, Validators.email]]
})
});
}
ngOnInit() {
}
suggestEmail(email) {
Mailcheck.run({
email: email,
domains: ['gmail.com', 'aol.com', 'hotmail.com', 'yahoo.com', 'rediffmail.com', 'edu', 'msn.com',],
secondLevelDomains: ['domain', 'hotmail'],
topLevelDomains: ["com", "net", "org", "info"],
suggested: function (suggestion) {
console.log(suggestion);
if (suggestion) {
alert(suggestion.full);
console.log(suggestion.full + "dkdjdekjekde")
}
},
empty: function () {
}
});
}
Right now, value of suggestions.full comes in alert if its being called. But I am trying to show suggestions.full in html side, like as a error warning.
Below is link to my stackblitz
stackblitz
To avoid potential problems with access to this within the Mailcheck.run suggested callback, you could save the results of Mailcheck.run, check them and, if appropriate, set an error on your form field.
let check = Mailcheck.run({
email: email,
... other stuff ...
suggested: (suggestion) => {
return suggestion;
},
empty: () => {
return false; // or however you want to handle it...
}
if (check && check.full) {
this.suggestedEmail = check.full;
this.signupForm.get('userData.email').setErrors({ 'has_suggestion': true })
}
// then in your template (using a getter)
<span class="help-block"
*ngIf="f.invalid && f.touched && f.errors?.has_suggestion">
Suggestion: {{suggestedEmail}}
</span>
Please find this stackblitz -- hope it helps!
Instead of using a regular function which will be lost this scope whereas arrow function keeps track of this. Read more about the difference here https://stackoverflow.com/a/34361380/5836034
do something like this
....
suggestion: any;
....
suggestEmail(email) {
Mailcheck.run({
email: email,
domains: ['gmail.com', 'aol.com', 'hotmail.com', 'yahoo.com', 'rediffmail.com', 'edu', 'msn.com',],
secondLevelDomains: ['domain', 'hotmail'],
topLevelDomains: ["com", "net", "org", "info"],
suggested: (suggestion) => {
console.log(suggestion);
if (suggestion) {
alert(suggestion.full);
this.suggestion = suggestion;
console.log(suggestion.full + "dkdjdekjekde")
}
},
empty: function () {
}
});
}
Observe the use of arrow function, to keep track of this scope and also, assigning the value of suggestion to your class variable via
this.suggestion = suggestion
in your template, you can now have access to suggestion like so
<div *ngIf="suggestion">{{suggestion.full}} </div>
Source: https://stackblitz.com/edit/angular-email-checker-bjcrcc
Related
<template>
<div>
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" v-model="firstName" placeholder="Enter your name">
</div>
<div class="form-group">
<label for="lastName">Last name</label>
<input type="text" class="form-control" v-model="lastName" placeholder="Enter your last name">
</div>
<div class="form-group">
<label for="message">Type Your message</label>
<textarea class="form-control" v-model="message" rows="3"></textarea>
</div>
<div class="form-group form-check" v-for="number in numbers" :key="number">
<input type="checkbox" :value="number.Broj" v-model="checkedNumbers">
<label class="form-check-label" >{{number.Broj}}</label>
</div>
<button type="submit" class="btn btn-primary" v-on:click="alert" #click="sendMessage">Send message</button>
</div>
</template>
<script>
import http from "../http-common.js";
import userServices from "../services/userServices.js";
export default {
data()
{
return {
firstName: null,
lastName: null,
message: null,
numbers: "",
checkedNumbers: [],
success: 'You have submitted form successfully'
};
},
methods:
{
async sendMessage()
{
await http.post("/message", {firstName: this.firstName, lastName: this.lastName, message: this.message, numbers: this.checkedNumbers});
this.$data.firstName = "",
this.$data.lastName = "",
this.$data.checkedNumbers = [],
this.$data.message = "";
},
alert() {
alert(this.success)
if(event)
alert(event.target.tagName)
},
retrieveNumbers() {
userServices.getNumbers().then(response => {
this.numbers = response.data;
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
},
created() {
this.retrieveNumbers();
}
}
</script>
So I want to add the option of checking input fields when user clicks "Send Message" button. I tried some options but I faield at that. So please I would appretiate if someone would help me. I'm still learning.
I know I have to use v-if and create the method for checking the fields.
So if you would be most kind and help me solve this problem I would be really grateful.
Thank you dev, community <3
Can I please get a concrete answer. Because I'll learn in that way, so please without condescending and "no-answers"
You can do it manually :
<script>
import http from "../http-common.js";
import userServices from "../services/userServices.js";
export default {
data()
{
return {
firstName: null,
lastName: null,
message: null,
numbers: "",
checkedNumbers: [],
success: 'You have submitted form successfully'
};
},
methods:
{
async sendMessage()
{
if(!(this.firstName && this.lastName && this.numbers)) return;
await http.post("/message", {firstName: this.firstName, lastName: this.lastName, message: this.message, numbers: this.checkedNumbers});
this.$data.firstName = "",
this.$data.lastName = "",
this.$data.checkedNumbers = [],
this.$data.message = "";
},
alert() {
alert(this.success)
if(event)
alert(event.target.tagName)
},
retrieveNumbers() {
userServices.getNumbers().then(response => {
this.numbers = response.data;
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
},
created() {
this.retrieveNumbers();
}
}
</script>
Or you can this usefull library
https://vuelidate.js.org/#sub-basic-form
You can simply define a method to check the fields and call that before the HTTP request in the sendMessage method.
You can initialize your data as an empty string "" and have a method like this:
validateForm() {
return this.firstName != "" && this.lastName != "" && this.message != ""
}
Update your sendMessage method to something like this:
async sendMessage() {
const isFormValid = this.validateForm()
if (isFormValid) {
await http.post(....)
...
}
}
I have created a settings page where users can update their email addresses. Everything worked fine but suddenly the validation is not updating anymore. Only the first change of the input field triggers validateState().
Any further changes will not trigger this function so the status of that field stays as it is.
I have compared the code with other components that use the same code and they still work fine.
I am using bootstrap-vue components for the form.
<template>
<div class="row">
<div class="col-md-12">
<b-form #submit="onSubmit">
<b-form-group :label="$t('general.email')"
label-for="settingsEmail"
:invalid-feedback="errors.first('email')">
<b-form-input id="settingsEmail"
type="text"
v-model="form.email"
:disabled="saving"
name="email"
:state="validateState('email')"
v-validate="{required: true, email: true}">
</b-form-input>
</b-form-group>
<b-button type="submit" variant="primary" :disabled="saving || !hasChanged() || errors.any()"><i class="fa fa-refresh fa-spin fa-fw" v-if="saving"></i> {{$t('general.save')}}</b-button>
</b-form>
</div>
</div>
</template>
<script>
import {UPDATE_USER} from '../config/actions'
export default {
name: 'settingsAccount',
data() {
return {
form: {},
saving: false
}
},
computed: {
user: function() {
return this.$store.getters.getUser;
}
},
created() {
this.init();
},
methods: {
init() {
this.form.email = this.user.email;
},
hasChanged() {
if(this.form.email !== this.user.email) {
return true;
}
return false;
},
onSubmit(event) {
event.preventDefault();
this.saving = true;
this.$validator.validateAll().then((result) => {
if (result) {
let data = {};
if(this.form.email !== this.user.email) {
data.email = this.form.email;
}
this.$store.dispatch(UPDATE_USER, data).then(() => {
this.saving = false;
this.$validator.reset();
}).catch(() => {
this.saving = false;
});
} else {
this.saving = false;
}
});
},
validateState(ref) {
if (this.veeFields[ref] && (this.veeFields[ref].dirty || this.veeFields[ref].validated)) {
return !this.errors.has(ref)
}
return null
},
}
}
</script>
The problem you're having is that the form data element is an empty object, so it will only trigger reactivity when the whole object changes. Either you need to change your data to be this:
data() {
return {
form: {email:''},
saving: false
}
},
Or in your init function, explicitly add the email property as reactive:
methods: {
init() {
this.$set(form,'email',this.user.email)
},
//...
If you're not clear on why, you can read the details here: https://v2.vuejs.org/v2/guide/reactivity.html
A working example (minus vuex) here: https://codesandbox.io/s/x4kp93w3o
PS, when writing questions about vue, it's very helpful to boil it down to a simpler example. Get rid of vuex, remove your translation stuff. Sometimes the answer will jump out at you once you have it as simple as possible.
I have a form where I enter an email and confirm email and then continue to the next page and all is well. The validation works fine when the page initially loads and it's the user's first time, so the input field is not prepopulated from cookie data. However, when the user returns, the input field data is prepopulated from cookie data and that is fine but the submit button is still disabled even though the prepopulated text is valid format. I inspected the elements and it seems to think the field is ng-invalid even though it's valid format.
I noticed when I go to one of the fields and backspace to remove the last character and reinsert the same character as before for email and do the same for the next field, the form is valid again. Even though, it's the same text as before.
I'm wondering why validation fails when the form first loads with prepopulated data?
Here's my code:
export class EmailComponent implements OnInit {
public user : User;
Form : FormGroup;
displayErrors : boolean;
ngOnInit() {
// initialize model here
this.user = {
Email: '',
confirmEmail: ''
}
}
constructor(fb: FormBuilder, private cookieService: CookieService, private cryptoService: CryptoService) {
var encryptedEmail = this.cookieService.get(AppCookie.EmailAddress);
var Cookie = null;
if(encryptedEmail != null && encryptedEmail != 'undefined')
Cookie = this.cryptoService.Decrypt(encryptedEmail);
if(Cookie == null) {
this.Form = fb.group({
email: ['', [Validators.required, Validators.pattern(EMAIL_REGEXP)]],
confirmEmail: ['', [Validators.required, Validators.pattern(EMAIL_REGEXP)]]
},
{
validator: this.matchingEmailsValidator('email', 'confirmEmail')
});
}
else {
this.Form = fb.group({
email: [Cookie, [Validators.required, Validators.pattern(EMAIL_REGEXP)]],
confirmEmail: [Cookie, [Validators.required, Validators.pattern(EMAIL_REGEXP)]]
},
{
validator: this.matchingEmailsValidator('email', 'confirmEmail')
});
}
}
save(model: User, isValid: boolean)
{
model.Email = this.Form.get('email').value;
var encrypted = this.cryptoService.Encrypt(model.Email);
this.cookieService.put(AppCookie.EmailAddress, encrypted);
}
matchingEmailsValidator(emailKey: string, confirmEmailKey: string): ValidatorFn {
return (group: FormGroup): {[key: string]: any} => {
let email = group.controls[emailKey];
let confirmEmail = group.controls[confirmEmailKey];
if (email.value !== confirmEmail.value) {
return {
mismatch: true
};
}
};
}
}
and here's my view:
<form [formGroup]="Form" novalidate (ngSubmit)="Form.valid && save(Form.value, Form.valid)">
<div class="login-wrapper">
<div class="login-page">
<section class="login-form form-group">
<p>
<input id="email"
[class.email-address-entry]="!displayErrors"
[class.email-address-entry-text]="!displayErrors && this.Form.get('email').value !='' "
type="email"
placeholder="name#domain.com" formControlName="email" />
</p>
<p class="login-form__msg">Reenter your email to confirm</p>
<input id="reenteremail"
[class.email-address-entry]="!displayErrors"
[class.entry-border-invalid]="displayErrors && !Form.valid && Form.errors?.mismatch"
[class.email-address-entry-text]="!displayErrors && this.Form.get('email').value !='' "
(blur)="displayErrors=true"
type="email" placeholder="name#domain.com"
formControlName="confirmEmail"/>
<p class="error-msg" *ngIf="displayErrors && !Form.valid && Form.errors?.mismatch">The email you entered does not match.</p>
</section>
<p class="login-confirm">
<span>
<button type="submit" [disabled]="!Form.valid" (click)="Form.get('email').length > 0 ? save(Form.value, Form.valid) : NaN">Confirm</button>
</span>
</p>
</div>
</div>
</form>
EDIT: It's similar to this issue as well:
Angular 2 - Form is invalid when browser autofill
I tried adding this:
ngAfterViewChecked() {
if (Cookie) {
// enable to button here.
var element = <HTMLInputElement> document.getElementById("confirmBtn");
element.disabled = false;
}
But it won't work because fields are still invalid. I need a way to manually set re-validation or change ng-invalid to ng-valid.
If you keep a reference to the form instance (either by using reactive forms or by accessing it using #ViewChild) you should be able to write the following in ngAfterViewInit():
for (var i in this.form.controls) {
this.form.controls[i].updateValueAndValidity();
}
Or perhaps marking the fields as touched will be better in your case:
for (var i in this.form.controls) {
this.form.controls[i].markAsTouched();
}
I'm playing around with vue.js and the .vue components, and as newbie, I'm wondering how can I keep track of the element I add in the array.
The situation is the following :
The user add a new element from a form
When he submit, the data are automatically added to a ul>li element, and a POST request is made to the API
When the POST is done, I want to update the specific li with the new data from the server.
The thing is, I can not target the last li because the server can take time to process the request (he do a lot of work), so the user may have added 1, 5, 10 other entries in the meantime.
So how can I do ?
Here's my code so far :
<template>
<form method="post" v-on:submit.prevent="search">
<input type="text" placeholder="Person name" required v-model="name" v-el="nameInput" />
<input type="text" placeholder="Company" required v-model="company" v-el="domainInput" />
<input type="submit" value="Search" class="btn show-m" />
</form>
<ul>
<li transition="expand" v-for="contact in contacts">
<img v-bind:src="contact.avatar_url" width="40px" height="40px" class="cl-avatar" />
<div class="cl-user">
<strong class="cl-name">{{contact.name}} <span class="cl-company">{{contact.company}}</span></strong>
</div>
</li>
</ul>
</template>
<script>
export default {
data () {
return {
contacts: [],
name: null,
company: null
}
},
methods: {
search: function (event) {
this.$http.post('/search', {
name: this.name,
company: this.company
}).then(function (xhr) {
// HERE ! How can I target the exact entry ?
this.contacts.unshift(xhr.data)
})
this.name = ''
this.company = ''
this.contacts.unshift({'name': this.name, 'company': this.company})
},
}
}
</script>
Thank you for your help ! :)
If you know that the name and company fields are unique you could search through the array to find it... otherwise you can just wait to append it to the array until the return function:
search: function (event) {
this.$http.post('/search', {
name: this.name,
company: this.company
}).then(function (xhr) {
this.contacts.unshift(xhr.data)
})
this.name = ''
this.company = ''
},
I finally found a working solution : I use a component instead of <li /> for each entries, and manage the state of these inside the component :
<ul>
<contact-entry v-for="contact in contacts" v-bind:contact="contact"></contact-entry>
</ul>
That way, when I add a new entry in the array (described above), a new instance of the component contact-entry is made.
Inside that component, I did the following :
<script>
export default {
props: ['contact'],
created: function () {
if (this.contact.id === undefined) { // If it's a new contact
this.$http.post('search/name', { // I do the post here
name: this.contact.name,
domain: this.contact.company.name
}).then(function (xhr) {
this.contact = xhr.data // This will update the data once the server has replied, without hassle to find the correct line
})
}
}
}
</script>
That's it ! :) In the parent's component, I removed the xhr request and simplified the method to :
<script>
export default {
data () {
return {
contacts: [],
name: null,
company: null
}
},
methods: {
search: function (event) {
this.name = ''
this.company = ''
this.contacts.unshift({'name': this.name, 'company': this.company})
}
}
}
</script>
I'm trying to create very simple login with backbonejs. Collection stores usernames and passwords. Login view has two inputs and on click it should perform check function and compare input value with data from collection.
Html part looks like this:
<div class="login-block">
<script type="text/template" id="start">
<form id="login">
<div class="input-wrapper"><input type="text" placeholder="Username" id="username" required></div>
<div class="input-wrapper"><input type="password" placeholder="Password" id="password" required></div>
<div class="input-wrapper"><button class="btn">Sign in!</button></div>
</form>
</script>
<div class="error" class="block">
Error
</div>
<div class="success">
Success
</div>
</div>
Here is my Js code:
var User = Backbone.Model.extend({
defaults: {
login: 'root',
mail: 'root#mail.com',
password: ''
}
});
var user = new User();
//variable to store username
var loginData = {
username: "",
password: ""
}
// userbase
var UserCollection = Backbone.Collection.extend({
model: User
});
var userCollection = new UserCollection([
{
username: 'Ivan',
mail: 'ivan#mail.com',
password: '1234'
},
{
username: 'test',
mail: 'test#mail.com',
password: 'test'
}
]);
// login page
var LoginView = Backbone.View.extend({
el: $(".login-block"),
events: {
"click .btn": "check"
},
check: function(){
loginData.username = this.$el.find("#username").val(); // store username
loginData.password = this.$el.find("#password").val();// store password
if (loginData.username === userCollection.each.get("username") && loginData.password === userCollection.each.get("password"))
{appRouter.navigate("success", {trigger: true});
}else{
appRouter.navigate("error", {trigger: true});
}
},
render: function () {
//$(this.el).html(this.template());
var template = _.template($('#start').html())
$(this.el).html(template());
//template: template('start');
return this;
}
});
var loginView = new LoginView({collection: userCollection});
var AppRouter = Backbone.Router.extend({
routes: {
'': 'index', // start page
'/error': 'error',
'/success': 'success'
},
index: function() {
loginView.render();
console.log("index loaded");
},
error: function(){
alert ('error');
},
success: function(){
console.log('success');
}
});
var appRouter = new AppRouter();
Backbone.history.start();
It works fine to the check function, and it stores username and password, but something is clearly wrong either with router or check function when it starts comparison. Instead of routing to success or error page, it rerenders index page.
P.S I didn't use namespacing and code in general is not of a greatest quality, but it was made for educational purpose only.
You have to add the attribute type="button" to your button, otherwise it will submit the form when clicked (See this question):
<script type="text/template" id="start">
<form id="login">
<div class="input-wrapper"><input type="text" placeholder="Username" id="username" required></div>
<div class="input-wrapper"><input type="password" placeholder="Password" id="password" required></div>
<div class="input-wrapper"><button class="btn" type="button">Sign in!</button></div>
</form>
</script>
You can also return false in the click event handler, which would cancel the default action. (submitting the form, if you don't add type="button").
For comparing the values with the hardcoded collection, you can't call each as you where doing (which is an iteration function provided by Underscore) because you would receive an error. You could use Underscore's findWhere method which is also available in Backbone collections. So the click event handler (Your check function) could look like this:
check: function(){
loginData.username = this.$el.find("#username").val(); // store username
loginData.password = this.$el.find("#password").val();// store password
if(userCollection.findWhere({username: loginData.username, password: loginData.password})){
appRouter.navigate("success", {trigger: true});
}else{
appRouter.navigate("error", {trigger: true});
}
return false;
},
You can try it on this fiddle
The logic check you're doing doesn't look like it would work to me. I would expect the following to generate an error:
userCollection.each.get('username')
the function you're calling on your collection, each, is a wrapped underscore method which takes a function callback as a parameter. If you want to check your username and password, I'd do something like this:
var user = userCollection.findWhere({ username: loginData.userName });
This will return you the model where the username matches. Then you can check the password of that model:
if (user.get('password') === loginData.password) {
// do something
} else {
// do something else
}
EDIT Heck, you can do both checks at once:
var user = userCollection.findWhere({ username: loginData.userName, password: loginData.password });
I'll leave the previous code up just to demonstrate.