Aurelia Validation validation error detected, but no error message - aurelia

I have a super simple code I'm trying to validate:
<template>
<form role="form" submit.delegate="submit()" validate.bind="validation">
<div class="form-group">
<label>Test Field</label>
<input type="text" value.bind="testField" class="form-control" validate="Description" placeholder="What needs to be done?" />
<button type="submit">Submit</button>
</div>
</form>
</template>
With the following viewmodel
define(["require", "exports", "../scripts/HttpClient", "aurelia-validation", "aurelia-framework"], function(require, exports, HttpClient) {
var AureliaValidation = require('aurelia-validation').Validation;
var MyViewModel = (function () {
function MyViewModel(httpClient, aureliaValidation, isReadyCallback) {
this.httpClient = httpClient;
var self = this;
self.setupValidation(aureliaValidation);
}
MyViewModel.prototype.activate = function (params, queryString, routeConfig) {
};
MyViewModel.prototype.setupValidation = function (validation) {
this.testField = "";
this.validation = validation.on(this).ensure('testField');
//validation
// .on(this.serviceMetadata.ServiceData[0])
// .ensure('Value');
this.validation = this.validation.notEmpty().maxLength(3);
};
MyViewModel.prototype.submit = function () {
debugger;
if (this.validation.checkAll()) {
//Do Something
}
return null;
};
MyViewModel.inject = [HttpClient, AureliaValidation];
return MyViewModel;
})();
return MyViewModel;
});
Now I got it working for the most part, and the validation is showing false on submit check, the textbox outline color changes etc., however it's not injecting the validation error messages into the DOM. There's no script error message either, how can I troubleshoot this?
Yes, I can see the validation messages in the validationProperties, but they're not written to the UI.

If your browser allows it, find the JSPM packages in the sources and put a breakpoint here, it's the point where the view strategy looks for labels to append error messages to. If you'd have this code in the open, I'd be happy to have a look for you.
Also, what version of aurelia/aurelia-validation are you using?
And finally, did you modify your sample before posting?
`<input value.bind="testField" validate="Description" />`
These two attributes are contradictory. It binds the value to testField, but then you use the validate attribute to explicitly show validation messages for property "Description".

Related

How to show page before executing OnGet?

I have a razor page that shows a 'Please wait' box and the OnGet method does some stuff that might take a few seconds and ends with a LocalRedirect.
The razor code:
#page
#inject IStringLocalizer<Startup> localizer
<div class="login-page">
<div class="login-box">
<div class="card">
<div class="card-body login-card-body">
<div class="help-block text-center">
<div class="spinner-border" role="status" />
</div>
<p class="login-box-msg">#localizer["PleaseWait"]</p>
</div>
</div>
</div>
</div>
And the code-behind:
public async Task<IActionResult> OnGet()
{
//Do some stuff that takes a few seconds...
return LocalRedirect("/Dashboard");
}
Everything is working apart from the page first being shown and then executing the code.
Is it possible to first render the page (so that the users can see that something is happening) and then execute the code in the OnGet?
Move your long async routines from OnGet to a number of named handler methods. Allow the page to render a "please wait" message and use client-side code (jQuery AJAX or plain Fetch) to call the named handlers. Keep track of when they complete and when all have completed, redirect to the other page.
Here's an example where the OnGet simply renders the page (can include a "please wait" message"), and a number of named handlers simulate routines of varying length:
public void OnGet()
{
}
public async Task OnGetTwoSeconds()
{
await Task.Delay(2000);
}
public async Task OnGetThreeSeconds()
{
await Task.Delay(3000);
}
public async Task OnGetFiveSeconds()
{
await Task.Delay(5000);
}
The following script goes in the Razor page itself. It consists of three variables for tracking task completion and a method that redirects to the home page when all three have completed. Each of the named handlers is called by the code and sets its tracking variable to true on completion as well as calling the redirect function:
#section scripts{
<script>
let twosecondsdon = false;
let threesecondsdone = false;
let fivesecondsdone = false;
function redirect(){
if (twosecondsdone && threesecondsdone && fivesecondsdone) {
location.href = '/';
}
}
fetch('?handler=TwoSeconds').then(() => {
twosecondsdone = true;
redirect();
})
fetch('?handler=ThreeSeconds').then(() => {
threesecondsdone = true;
redirect();
})
fetch('?handler=FiveSeconds').then(()=>{
fivesecondsdone = true;
redirect();
})
</script>
}
When all three complete, the redirect function does its thing.
Is it possible to first render the page (so that the users can see
that something is happening) and then execute the code in the OnGet?
Yes you can do that. Currently, I am showing you the delay counter where you can replace your page. Please follow the steps below:
HTML:
Script:
#section scripts {
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>
<script>
$(document).ready(function () {
var counter = 5;
(function countDown() {
if (counter-- > 0) {
$('#timer').text("Please wait... we are redirecting you to register page..." + counter + ' s');
setTimeout(countDown, 1000);
} else {
window.location.href = "https://localhost:44361/userlog/ViewCalculateAge";// Here put your controller URL where you would like to redirect
}
})();
});
</script>
}
Output:

Can't return empty value to input box on vue

I want made a validation for input to be number only, whenever someone input a string, the input box will be cleared.
First, I made a method function on $event like this (ps. I use props)
<BaseInput
:value="nama"
#update="nama = ruled($event)"
label="Nama"
type="type"
/>
and this is the method, I use RegExp to check if the $event value is number. When it's false then I return $event value to empty string.
ruled(event) {
console.log(event)
var intRegex = new RegExp(/[0-9]/);
var data = intRegex.test(event)
if(!data) {
alert("Value Must Number")
event = ""
console.log('masuk if' + data)
}
return event
}
but it didn't clear the input box, anyone know why it happened ?
As Creative Learner said, I must clearing input box in child component only, so I did this on my component child
<template>
<input
:value="value"
:placeholder="label"
#input="$emit('update', ruled($event))"
/>
</template>
And this is the methods:
methods: {
ruled(event) {
//console.log(event)
var val = event.target.value
if(event.target.type == "number"){
var intRegex = new RegExp(/[0-9]/);
var intdata = intRegex.test(val)
if(intdata == false) {
error = "Value must contain number"
//alert("Value must contain number")
return event.target.value = ""
}
}
return val
}
}
great thanks to Creative Learner who made me understand
Suggestions :
Instead of #update you have to use #keypress or #change.
You can use v-model for two-way data binding.
Working Demo :
new Vue({
el: '#app',
data: {
nama: ''
},
methods: {
ruled(event) {
var intRegex = new RegExp(/^\d+$/);
var data = intRegex.test(this.nama);
if (!data) {
alert("Value must contain number");
this.nama = "";
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input
v-model="nama"
#change="ruled($event)"
label="Nama"
type="text"
/>
</div>

NuxtJs where to declare a computed property

How can i declare a computed property using Nuxt ? or the equivalent ?
I am using NuxtJs and trying to use a category filter.
I want to filter by unique categories, and i am getting this error message:
Cannot read property 'filter' of undefined
I trying to adapt to Nuxtjs the exemple i found in this pen : https://codepen.io/blakewatson/pen/xEXApK
I declare this computed property below, first at pages/index.vue and after into .nuxt/App.js
filteredStore: function() {
var vm = this;
var category = vm.selectedCategory;
if(category=== "All") {
return vm.stores;
} else {
return vm.stores.filter(function(stores) {
return stores.category === category;
});
}
}
And i try to apply the filter into this list of checkboxes :
<div class="columns is-multiline is-mobile">
<div class="column is-one-quarter" v-for="store in filteredStore" :key="store.id" :store="store">
<label class="checkbox">
<input type="checkbox" v-model="selectedCategory" :value="''+store.category">
{{store.category}}
</label>
</div>
</div>
I'm going to do some guessing at your code situation (based on the example you noted), so just let me know where I make an incorrect assumption. I would guess that something like the following could work for you... maybe you could provide additional details where I'm missing them.
With regards to your error Cannot read property 'filter' of undefined, that probably means your array of stores is undefined. I believe if you create the stores array as empty in the data section, you should at least have it available before your async call returns any results.
One possible thing to you can do to test if your filtering logic is working... is to uncomment the manually created data array that I've created below. It's like an inline test for your data structure and logic, removing the asynchronous retrieval of your data. This basically can check if the filter works without your API call. It would narrow down your issue at least.
export default {
data() {
return {
stores: [
// Let's assume you don't have any static stores to start on page load
// I've commented out what I'm guessing a possible data structure is
//
// Example possible stores in pre-created array
// { name: 'Zales', category: 'Jewelry', id: 1 },
// { name: 'Petco', category: 'Pet Shop', id: 2 },
// { name: 'Trip Advisor', category: 'Tourism', id: 3 },
// { name: 'Old Navy', category: 'Clothes', id: 4 }
],
selectedCategory: 'All'
}
},
computed: {
// Going to make some small js tweaks
filteredStores: () {
const vm = this;
const category = vm.selectedCategory;
if (category === "All") {
return vm.stores;
} else {
return vm.stores.filter(store => {
return store.category === category;
});
}
}
},
async asyncData({ $axios }) {
$axios
.$get('https://yourdomain.com/api/stores/some-criteria')
.then(response => {
this.stores = response.data;
})
.catch(err => {
// eslint-disable-next-line no-console
console.error('ERROR', err);
});
}
};
And then your HTML
<div class="columns is-multiline is-mobile">
<div class="column is-one-quarter" v-for="store in filteredStores" :key="store.id" :store="store">
<label class="checkbox">
<input type="checkbox" v-model="selectedCategory" :value="`${store.category || ''}`">
{{store.category}}
</label>
</div>
</div>
ANYWAY This is all just a big guess and what your scenario is, but I figured I'd try to help shape your question some so that you could get a more meaningful response. In general, I'd suggest trying to provide as much detail as you can about your question so that people really can see the bits and pieces where things might have gone astray.
Don't touch anything in .nuxt Someone noted that above in a comment, and it's very important. Essentially that whole directory is generated and any changes you make in it can be easily overwritten.

How do I go about creating an always capitalized input in vue.js?

I'm creating a form using vue.js and I need to create inputs in vue that is always capitalized. I know I could use the css property
text-transform: uppercase;
and then transform the data before sending using
data.someData.toUpperCase()
But I wonder if there is a more intelligent way of doing that in vue.js. In react we can create controlled inputs and easily do it. Is there anything like that in Vue.js?
I managed to do it using computed fields, however, I would have to create computed getter and setter for each input in the form. Is there a better way of doing it?
You could create a custom directive.
Vue.directive( 'touppercase', {
update (el) {
el.value = el.value.toUpperCase()
},
})
And then use it where you need. For example:
<input type="text" v-model="modelfield" v-touppercase>
Since you don't have a lot of code to run, you should manually bind events to your textfield and then handle the uppercasing there.
Handling events from a text field can be done by adding an input event handler on them, and then updating the initial state again.
<input :value="text" #input="updateText($event.target.value)"/>
export default {
data() {
return {
text: '',
}
},
methods: {
updateText(newValue) {
this.value = newValue.toUpperCase();
},
}
}
You can also do it inline in a template, but this might make it harder to read depending on your code style preferences
<input :value="text" #input="text = $event.target.value.toUpperCase()"/>
This directive works fine with v-model (last character is in upper case too):
Vue.directive('uppercase', {
update(el) {
const sourceValue = el.value;
const newValue = sourceValue.toUpperCase();
if (sourceValue !== newValue) {
el.value = newValue;
el.dispatchEvent(new Event('input', { bubbles: true }));
}
},
});
Usage:
<input type="text" v-model="myField" v-uppercase />
You can do this:
<input :value="theValue" #input="theValue = theValue.toUpperCase()"/>
as a fix for asologor's answer you should reach input element to change it at vuetify
Vue.directive("uppercase", {
update(el) {
const sourceValue = el.getElementsByTagName("input")[0].value
const newValue = sourceValue.toUpperCase()
if (sourceValue !== newValue) {
el.getElementsByTagName("input")[0].value = newValue
el = el.getElementsByTagName("input")[0]
el.dispatchEvent(new Event("input", { bubbles: true }))
}
}
})
and usage is
<v-text-field
type="text"
outlined
placeholder="placeholder"
v-model="name"
prepend-inner-icon="edit"
v-uppercase
/>
this is defnetely working

Vue js dynamic update set/unset class

I have few inputs with submit button. Have some validation logic that adds 'has-error' class to input. How can i unset this class on focus?
Template:
<div class="input-styled badge-icon" :class="{ 'has-error': errors.email}">
<input type="text" #focus="delete errors.email" v-model="email" placeholder="example#gmail.com">
</div>
<button #click="submit">Submit</button>
JS
data() {
return {
errors: {},
email: ''
}
},
methods: {
submit(){
this.errors = {};
if(!this.email){
this.errors.email = 'Something';
}
}
}
I'm trying delete error property, trying #focus='errors.email="" ', but class 'has-error' disappears only when i'm typing something on inputs. #focus event works and i think that i should call some function that will update my DOM?
It is a good practice to move operations on component's data to functions. You can achieve desired validation reset, by creating a resetValidation function and binding it to focus event on input field.
Method itself should reset errors field to falsy values. Example below assumes, there are multiple input fields in the form. Each field should call resetVlidation method with corresponding error field name. If no field is provided, we can reset validation as whole:
resetValidation (field) {
if (field) {
this.errors = {
...this.errors,
[field]: ''
}
} else {
this.errors = {}
}
Please, check the working example below:
codesandbox