Input validation: change default error message - javascript

If I do this on vue:
<input pattern="\d+">
It gets properly validated, but I get a popup with a message "Please match the requested format".
Is there any way to change this message? I can't find the documentation on the accepted validation tags.
JSFiddle: http://jsfiddle.net/ovz5p3wt/

so, as noted in comments, the way to do it is to use oninvalid and setCustomValidity
<input pattern="\d+" oninvalid="setCustomValidity('please use a number')">
However, if you want to do it using a script (since you are tagging at as vue) here is another solution that allows you to dynamically change the value you can use the script version. unfortunately #oninvalid doesn't seem to be supported by vue, so you'd need to set the functionality using $refs
updateCustomValidity(lang){
var el = this.$refs.el;
el.oninvalid = el.setCustomValidity(this[lang].message);
}
new Vue({
el: "#app",
data() {
return {
lang: 'en',
en: {
message: 'nope, sorry',
},
fr: {
message: 'sacre bleu'
}
}
},
watch: {
lang: {
handler(lang) {
this.updateCustomValidity(lang);
}
}
},
methods: {
updateCustomValidity(lang){
var el = this.$refs.el;
el.oninvalid = el.setCustomValidity(this[lang].message);
}
},
mounted() {
this.updateCustomValidity(this.lang);
}
})
body { background: #20262E; padding: 20px; font-family: Helvetica;}
#app { background: #fff; border-radius: 4px; padding: 20px; transition: all 0.2s;}
input { margin: 8px 0;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
EN: <input type="radio" v-model="lang" value="en">
FR: <input type="radio" v-model="lang" value="fr">
<form>
<input pattern="\d+" value="text" ref="el">
<button type="submit">Submit</button>
</form>
</div>

Related

Vuejs populate input fields value on a element select

I am not sure if I am terming this correctly. I have a very simple vuejs application (I just started learning vue a couple of days ago, so my knowledge of vue is really limited).
I have a input field which acts as a search box. When we feed some text input, it triggers v-on:blur event to call a function. It then sets the suggestions which are displayed just below the searchbox.
What I am trying to achieve is, when any of those anchor tags are clicked (from the search suggestions), two new input boxes should be automatically populated with the values from the search suggestions.
{name: 'Some Name', state: 'Some State'}
A very simple and stripped version of the code is as https://jsfiddle.net/dfhpj08g/
new Vue({
el: "#app",
data: {
suggestions: [],
showSuggestions: false,
},
methods: {
suggest() {
// this is dynamically generated via ajax
this.suggestions = [{
name: 'A',
state: 'OH'
},
{
name: 'B',
state: 'OR'
},
];
this.showSuggestions = true;
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-on:blur="suggest" placeholder="search">
<div v-show="showSuggestions">
<span>Did you mean</span>
<li v-for="s in suggestions">
<a href="#">
{{s.name}} - ({{s.state}})
</a>
</li>
</div>
<input type="text" name="name" placeholder="name">
<input type="text" name="state" placeholder="state">
</div>
If you want to insert the values into your name and state fields, I would suggest using v-model on them and declaring the corresponding data in your component. In that way, you can simply set them using this.name and this.state:
data: {
suggestions: [],
showSuggestions: false,
name: '',
state: ''
},
Use v-model to bind name and state data to your input elements:
<input type="text" name="name" placeholder="name" v-model="name">
<input type="text" name="state" placeholder="state" v-model="state">
You can bind a click handler to each of the <a> elements, so that you will can pass the index of the clicked suggestion. This will allow you to do this.suggestion[i] to retrieve the data:
<li v-for="(s, i) in suggestions" v-bind:key="i">
<a href="#" v-on:click.prevent="suggestionSelected(i)">
{{s.name}} - ({{s.state}})
</a>
</li>
Then, in your methods, you can create a new function suggestionSelected, which accepts the index of the suggestion as i. In that way, you can use the bracket syntax to access the selected suggestion:
suggestionSelected(i) {
this.name = this.suggestions[i].name;
this.state = this.suggestions[i].state;
}
See proof-of-concept example below:
new Vue({
el: "#app",
data: {
suggestions: [],
showSuggestions: false,
name: '',
state: ''
},
methods: {
suggest() {
// this is dynamically generated via ajax
this.suggestions = [{
name: 'A',
state: 'OH'
},
{
name: 'B',
state: 'OR'
},
];
this.showSuggestions = true;
},
suggestionSelected(i) {
this.name = this.suggestions[i].name;
this.state = this.suggestions[i].state;
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-on:blur="suggest" placeholder="search">
<div v-show="showSuggestions">
<span>Did you mean</span>
<li v-for="(s, i) in suggestions" v-bind:key="i">
<a href="#" v-on:click.prevent="suggestionSelected(i)">
{{s.name}} - ({{s.state}})
</a>
</li>
</div>
<input type="text" name="name" placeholder="name" v-model="name">
<input type="text" name="state" placeholder="state" v-model="state">
</div>

How to properly apply ternary operator in v-model in vue.js?

I am trying to apply the ternary operator in the v-model but it's not working. I also read a lot of similar questions and answers on StackOverflow but none answer my query.
I have created a data variable testCondition which is set to false by default. Using this condition testCondition?name:place, place is returned in v-model if testCondition is false but if testCondition is true then v-model does not return anything.
Here is my code:
new Vue({
el: "#app",
data: {
name: '',
place: '',
testCondition: false,
},
})
body {
padding: 15px;
}
input {
border-radius: 3px;
padding: 5px;
border: thin solid lightgrey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<br>
<input type="text" v-model="testCondition?name:place">
<br><br> Test condition status: {{testCondition}}
<br> Name: {{name}}
<br> Place: {{place}}
</div>
Expected result: If I change the value of testCondition from false to true, output should be shown in {{name}}
Actual result: Only working for {{place}} if testCondition is set to false
Try this: <input type="text" v-model="$data[testCondition ? 'name' : 'place']">
new Vue({
el: "#app",
data: {
name: '',
place: '',
testCondition: false,
},
})
body {
padding: 15px;
}
input {
border-radius: 3px;
padding: 5px;
border: thin solid lightgrey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<br>
<input type="text" v-model="$data[testCondition ? 'name' : 'place']">
<br><br> Test condition status: {{testCondition}}
<br> Name: {{name}}
<br> Place: {{place}}
</div>
You need a computed property with a getter and a setter: https://v2.vuejs.org/v2/guide/computed.html#Computed-Setter
computed: {
myModel: {
get() {
return this.testCondition ? this.name : this.place;
}
set(newValue) {
this.doSomethingWith(newValue);
}
}
// then in template: v-model="myModel"

Vue 2 event listener on component root

I'm trying to capture an event on the component root node, but the following does not work. I don't want to just listen on a node in the component. I want to be able to click on any element and then hit backspace to remove it. The code below is a basic example of how I setup my code.
<template>
<div v-on:keydown.delete="delete()">
<img id="image" src="..." v-on:click="set_active()">
</div>
</template>
<script>
export default {
return {
data() {
active: ''
},
methods: {
delete(){
delete this.$refs[this.active][0];
},
set_active() {
this.active = event.target.getAttribute('id');
}
}
}
}
</script>
After doing some tests, here is what I discovered:
Having a method called delete won't work. I don't know why, the question remains unanswered here. Rename it to remove, for example.
When trying to catch keyboard events on a div, you may need to add a tabindex attribute for it to work. (See here)
Interactive demo
Vue.component('my-component', {
template: '#my-component',
data() {
return {
images: [
"https://media.giphy.com/media/3ohs7KtxtOEsDwO3GU/giphy.gif",
"https://media.giphy.com/media/3ohhwoWSCtJzznXbuo/giphy.gif",
"https://media.giphy.com/media/8L0xFP1XEEgwfzByQk/giphy.gif"
],
active: null
};
},
methods: {
set_active(i) {
this.active = i;
},
remove() {
if (this.active !== null) {
this.images = this.images.filter((_, i) => i !== this.active);
this.active = null;
}
}
}
});
var vm = new Vue({
el: '#app'
});
div {
outline: none; /* Avoid the outline caused by tabindex */
border: 1px solid #eee;
}
img {
height: 80px;
border: 4px solid #eee;
margin: .5em;
}
img:hover {
border: 4px solid #ffcda9;
}
img.active {
border: 4px solid #ff7c1f;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.min.js"></script>
<div id="app">
<my-component></my-component>
</div>
<template id="my-component">
<div #keydown.delete="remove" tabindex="0">
<img
v-for="(img, i) in images"
:key="i"
:src="img"
:class="{ active: active === i }"
#click="set_active(i)"
/>
</div>
</template>

Using jquery validate errorPlacement to get error message in a specific div

I am attempting to use jQuery Validate's errorPlacement function to place an error message, but I cannot figure it out. Whenever I hit submit, the input slides down, instead of the error placing in the newsletterValidate div.
Does anyone see what I am doing wrong in my errorPlacement code?
$('#newsletterForm').validate({
//errorClass: 'invalid',
errorPlacement: function(error, element) {
//element.val(error[0].outerText);
//error.appendTo(element.next('div'));
error.appendTo(element.parent('div').next('div').find('#newsletterValidate'));
},
rules: {
email: {
required: true,
email: true
}
},
messages: {
email: {
required: "Please enter your email address",
email: "Please enter a valid email address"
}
},
submitHandler: function(form) {
event.preventDefault();
var datastring = $('#newsletterForm').serialize();
$.ajax({
url: 'http://localhost:8080/php/newsletterSend.php',
type: 'POST',
data: datastring
,
success: function(data) {
console.log(data);
if (data == 'Error!') {
alert('Unable to submit form!');
alert(data);
} else {
$('#newsletterInput')[0].reset();
$('#newsletterSuccess').show();
}
},
error: function(xhr, textStatus, errorThrown) {
alert(textStatus + '|' + errorThrown);
console.log('error');
}
});
}
});
#newsletterInput {
width: 70%;
border: none;
padding: 15px 10px;
outline: none;
font-size: 1.1rem;
font-family: 'Nunito', sans-serif;
display: inline-block;
background: pink;
}
#newsletterSubmit {
width: 25%;
display: inline-block;
border: none;
font-size: 1.1rem;
font-family: 'Nunito', sans-serif;
padding: 15px 10px;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.16.0/jquery.validate.min.js"></script>
<div>
<form id="newsletterForm">
<input type="email" id="newsletterInput" name="email" placeholder="Email Address *"><input type="submit" id="newsletterSubmit">
</form>
</div>
<div id="newsletterValidate"></div>
error.appendTo(element.parent('div').next('div').find('#newsletterValidate'));
I'm not sure why you are using jQuery to traverse around inside the DOM when you already know the unique id. So just skip right to your target...
error.appendTo($('#newsletterValidate'));
Demo 1: jsfiddle.net/b2enbs0s/
However, now that it's working, you can see there's a different problem. The message is being repeated.
That's because you're trying to place the message outside of the form container...
<div>
<form id="newsletterForm">
<input type="email" id="newsletterInput" name="email" placeholder="Email Address *"><input type="submit" id="newsletterSubmit">
</form>
</div>
<!--// outside of the form //-->
<div id="newsletterValidate"></div>
In this case, the plugin creates the validation message but cannot find it again in order to properly toggle it.
The fix is to place the message element inside of the form container where the plugin can automatically find & toggle after it's created. The following structure visually renders identical to your original layout...
<form id="newsletterForm">
<div>
<input type="email" id="newsletterInput" name="email" placeholder="Email Address *"><input type="submit" id="newsletterSubmit">
</div>
<!--// inside of the form //-->
<div id="newsletterValidate"></div>
</form>
Final Working Demo: jsfiddle.net/b2enbs0s/1/

Polymer - <iron-ajax> data binding

I have created a polymer element and inside the element I am fetching a small little .json file that I will need to use for various parameters.
My JSON file looks like this.
{
"server_name" : "XMS Development Site",
"server_url" : "test0",
"xms_version" : "3.0.0 BETA",
"rest": {
"os_url" : "test1",
"mbo_url": "test2",
"login_url": "test3",
"logout_url": "test4",
}
}
I am unable to access the values in this JSON object from my iron-ajax request. The {{response.xms_version}}binding just appears blank. The on-response function just displays null. Looking in chrome's dev tools, the JSON file is retrieved and the data is all there. It seems that for some reason I am just unable to bind to it. I am not trying to use the dom-repeat method as I just need to be able to bind to these data points.
My element looks like this:
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-input/paper-input.html">
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
<link rel="import" href="../../bower_components/iron-ajax/iron-ajax.html">
<dom-module id="xms-login">
<template>
<style>
:host {
display: block;
}
.login-form-button{
padding: 16px;
text-align: center;
}
.login-form{
}
.login-field{
padding-top: 16px;
padding-left: 16px;
padding-right: 16px;
}
.login-button{
background-color: var(--default-primary-color);
color: var(--text-primary-color);
}
.xms-logo-svg{
text-align: center;
padding-top: 64px;
}
.version-number-text{
text-align: center;
postion: absolute;
bottom: 0;
}
</style>
<iron-ajax id="testAjax" auto
url="../../xms.json"
handle-as="json"
method="GET"
on-response="handleResponse"
last-response="{{response}}"></iron-ajax>
<xms-auth id="xmsAuthHandler" authheader="{{computeEncodedLogin(username, password)}}" provider="rest"></xms-auth>
<div class="login-form">
<div class="login-form-fields">
<paper-input class="login-field" type="text" label="Username" value="{{username}}"></paper-input>
<paper-input class="login-field" type="password" label="Password" value="{{password}}"></paper-input>
</div>
<div class="login-form-button">
<paper-button raised class="login-button" onclick="xmsAuthHandler.login()">Login</paper-button>
</div>
<div class="version-number-text">{{response.xms_version}}</div>
</div>
</template>
<script>
(function() {
'use strict';
Polymer({
is: 'xms-login',
properties: {
user: {
type: String,
notify: true
},
username: {
type: String,
notify: true
},
password: {
type: String,
notify: true
},
},
computeEncodedLogin: function( username, password ){
return btoa(username + ':' + password);
},
handleResponse: function(request){
var myResponse = request.detail.response;
console.log(myResponse);
}
});
})();
</script>
</dom-module>
Issue is fixed, was caused by incorrect JSON data.

Categories