i wanted to validate the sy_EndYear value so that its minimum value should be higher than the value of sy_StartYear by 1.
the code below shows how i wanted to do it, but obviously, it does not work.
can i do this with vuelidate or should i look for another work around?
i wanted to do the validation through vuelidate so that the error messages are uniform when displayed.
im new to vue and still has trouble understanding its concepts from just reading the documentations, so any insights from this forum would be really helpful. thank you.
validations: {
school: {
sy_StartYear: {minValue: minValue(2020)},
sy_EndYear: {minValue: minValue(endYr_comp)
}
},
computed: {
endYr_comp() {
return parseInt(this.school.sy_StartYear) +1
}
}
Related
I might have somewhat a tricky question and just want to be sure, I went the correct way.
To be precise, it's vuelidate 0x on vue 2 with good old boostrap-vue
Let's pressure we have a simple form, with the following validation
<b-form>
.....
<b-form-group
id="title"
label="Title"
label-for="Title"
>
<b-form-input
id="title-input"
v-model="$v.form.title.$model"
:state="validateState('title')"
type="text"
></b-form-input>
<b-form-invalid-feedback id="title-input-live-feedback">
You must enter a title.
</b-form-invalid-feedback>
</b-form-group>
....
validations: {
form: {
title: { required }
}
},
So when we create this item. I do the following:
async createCompany() {
if (!this.$v.$dirty) {
this.$v.$touch()
}
if (this.$v.$invalid) return
try {
await this.storeCompany()
...
} catch (error) {
//
}
},
Now, I come back to the form, for example, to modify it. This means, the item has once passed the validation successfully, especially for required fields.
I noticed that in that sense $touch() is not necessary, unless you add new validations, which would always result in $invalid. Still, I wanted to have a solution that will also work for this case, just so I do not stumble over it in the future.
So, to come up with a "flexible" solution, I did the following:
async updateCompany() {
if (this.$v.$invalid && !this.$v.$dirty) {
this.$v.$touch()
return
}
if (this.$v.$invalid) return
try {
let response = await CompaniesApi.update(this.id, this.form)
...
} catch (error) {
//
}
},
Basically, I first check if $invalid is true and if the form was not submitted (by checking for !this.$v.$dirty).
It works exactly as I want, no double, triple touches and no flickering in terms of validation changes due to touches.
What gives me a bit of a headache is the fact that I first check for this.$v.$invalid before I actually $touch().
Somehow, I thought it must be the other way around.
But I guess I misunderstood $touch() in a way that it's main job is really to show the $errors if needed (vuelidate only shows errors when $invalid and $dirty is set to true).
I know I could go the other way, disabling the submit button until the form is valid, but then I would need to add further elements to the form, e.g. make clear what is missing or maybe even $touch() the validation in the created() or mounted() hook.
Which however is also not what I want, because it would show errors to the user without any interaction.
So, the only solution I could find is the one described.
Did anybody else come across this? I mean, it does what I want, still would appreciate any feedback/ideas.
I'm attempting to set data fields provided by an array based on the Vue Router query. For example, when someone lands on my website using example.com/?location=texas, I want to set the location data by an array.
An example the array:
locations {
{
slug: "texas",
tagline: "Welcome to Texas",
}, {
slug: "california",
tagline: "Welcome to California",
}
}
I know this should be done using a computed property, however I am unable to get anything functioning. I've tried simple tests like if (this.slug.location === "texas"), and I cannot get the location data to populate. I would also like to provide default data in case there are no route matches.
Any help is extremely appreciated!
Edit:
I can accomplish this in a very manual way. Right now, I'm setting the query in data by the following:
slug: this.$route.query.location
I can display specific text by doing something like:
h3(v-if="slug === 'texas'") This will show for texas
h3(v-else-if="slug === 'california'") This will show for California
h3(v-else) This is default
The issue with this approach is there are various elements I need to customize depending on the slug. Is there any way I can create an array, and move whichever array matches a key in an array to the data??
You should be able to access a query param using the following (link to Vue Router documentation):
this.$route.query.location
So based on what you listed I would do something like...
export default {
computed: {
displayBasedOnLocationQueryParam() {
switch(this.$route.query.location) {
case 'texas':
return 'Welcome to Texas'
default:
return 'hello there, generic person'
}
}
}
}
Note that I'm not using your array explicitly there. The switch statement can be the sole source of that logic, if need be.
Ok, so I have a Vuex store that manages dropdown content on my page. The page I'm currently working on has two sets of three identical dropdowns that should behave exactly the same. One on a modal and the other one on the main page. So what I did is this:
this.$store.commit('setModule', 'manageSchedules');
this.$store.commit('setModule', 'manageSchedulesModale');
Each store has the following:
// These are arrays with the lists' content
sites
Profiles
Employees
//These are the actual value of each of my list
CurrentSite
CurrentProfile
CurrentEmployee
My problem is that I'd like the value of my list to by synched with the corresponding "currentXXX" of my vuex store. Normally, I'd go with something like:
:value.sync="currentXXX"
However, I can't find anywhere how exactly to reference the store in a value.sync statement. Could anyone help me?
Okay, Ricky was right, using a computed property with getter and setter worked perfectly and his link provided me with all the info I needed. In essence here's what I now have:
<template lang="pug">
.modal(v-if="popupShown", style="display:block", v-on:click="hideModalSafe")
.modal-dialog.modal-lg(v-on:keydown.enter="syncSchedule()")
.modal-content
h3.modal-header {{moment(currentSchedule.start).format('YYYY-MM-DD')}}
button.close(type="button",v-on:click="popupShown=false") ×
.modal-body
.row
.col.form-group
label(for="schedule-start") {{__('Start')}}
k-input-time(:date.sync="currentSchedule.start")
.col.form-group
label(for="schedule-end") {{__('End')}}
k-input-time(:date.sync="currentSchedule.end")
.col.form-group
label(for="schedule-pause") {{__('Pause')}}
k-input-minutes(:minutes.sync="currentSchedule.pause")
.row
.col.form-group
label(for="schedule-site-id") {{__('Site')}}
k-combobox(model="modSites", context="selection",
:value.sync="currentSiteMod")
.col.form-group
label(for="schedule-profile-id") {{__('Profile')}}
k-combobox(model="modProfiles", context="selection",
:value.sync="currentProfileMod")
.col.form-group
label(for="schedule-employee-id") {{__('Employee')}}
k-combobox(model="modEmployees", context="selection",
:value.sync="currentEmployeeMod")
.modal-footer
.btn.btn-danger.mr-auto(v-on:click="deleteSchedule()")
k-icon(icon="delete")
| {{__('Remove')}}
.btn.btn-primary(v-on:click="syncSchedule()", tabindex="0") {{__('Save')}}
</template>
export default {
......
computed:{
currentSiteMod:{
get(){
return this.$store.state.manageSchedulesModale.currentSite;
},
set(value){
this.$store.dispatch("manageSchedulesModale/updateCurrentSite", value);
}
},
currentProfileMod:{
get(){
return this.$store.state.manageSchedulesModale.currentProfile;
},
set(value){
this.$store.dispatch("manageSchedulesModale/updateCurrentProfile", value);
}
},
currentEmployeeMod: {
get(){
return this.$store.state.manageSchedulesModale.currentEmployee;
},
set(value){
this.$store.dispatch("manageSchedulesModale/updateCurrentEmployee", value);
}
}
}
}
Once again, Vue.js actually impressed me with its magic. Before going with computed properties, I got tangled in a slew of ajax calls and promises that kept going back and forth, producing inconsistent results. Now, the store's value gets updated perfectly and the lists react accordingly without me having to worry about an insane amount of parameters.
Anyway, there's probably a few things I didn't do right. I know I probably should have defined getters instead of accessing the property directly, but as it stands, it works and my client's happy (and so am I).
This looks to be answered many different times but I can't seem to get it working with my implementation. I am trying to format and limit the data in a sap.m.Input element. I currently have the following:
var ef_Amount = new sap.m.Input({
label: 'Amount',
textAlign: sap.ui.core.TextAlign.Right,
value: {
path: '/amount',
type: 'sap.ui.model.type.Currency'
}
});
The first problem is that it kind of breaks the data binding. When I inspect the raw data (with Fiddler) submitted to the server it is an array like this:
"amount": [1234.25,null]
The server is expecting a single number and as such has issues with the array.
When I use the following, the binding works as desired but no formatting is performed.
var ef_Amount = new sap.m.Input({
label: 'Amount',
textAlign: sap.ui.core.TextAlign.Right,
value: '{/amount}'
});
The second problem is that the data entered is not limited to numbers.
I have tried using sap.m.MaskedInput instead but I don't like the usage of the placeholders because I never know the size of the number to be entered.
And lastly, it would be nice if when focus is placed on the input field, that all formatting is removed and re-formatted again when focus lost.
Should I be looking into doing this with jQuery or even raw Javascript instead?
Thank you for looking.
the array output is a normal one according to documentation. So you need to teach your server to acccept this format or preprocess data before submission;
this type is not intended to limit your data input;
good feature, but ui5 does not support this, because the Type object has no idea about control and it's events like "focus" it only deals with data input-output. So you have to implement this functionality on your own via extending the control or something else.
I would suggest using amount and currency separately. It's likely that user should be allowed to enter only valid currency, so you can use a combobox with the suggestions of the available currencies.
So, after much work and assistance from #Andrii, I managed to get it working. The primary issue was that onfocusout broke the updating of the model and the change event from firing. Simply replacing onfocusout with onsapfocusleave took care of the issues.
The final code in the init method of my custom control:
var me = this;
var numberFormat = sap.ui.core.NumberFormat.getCurrencyInstance({maxFractionDigits: 2});
me.addEventDelegate({
onAfterRendering: function() {
// for formatting the figures initially when loaded from the model
me.bindValue({
path: me.getBindingPath('value'),
formatter: function(value) {
return numberFormat.format(value);
}
});
},
onfocusin: function() {
// to remove formatting when the user sets focus to the input field
me.bindValue(me.getBindingPath('value'));
},
onsapfocusleave: function() {
me.bindValue({
path: me.getBindingPath('value'),
formatter: function(value) {
return numberFormat.format(value);
}
});
}
});
This was originally posted on discuss.emberjs.com. See:
http://discuss.emberjs.com/t/what-is-the-proper-use-of-store-filter-store-find-for-infinite-scrolling/3798/2
but that site seems to get worse and worse as far as quality of content these days so I'm hoping StackOverflow can rescue me.
Intent: Build a page in ember with ember-data implementing infinite scrolling.
Background Knowledge: Based on the emberjs.com api docs on ember-data, specifically the store.filter and store.find methods ( see: http://emberjs.com/api/data/classes/DS.Store.html#method_filter ) I should be able to set the model hook of a route to the promise of a store filter operation. The response of the promise should be a filtered record array which is a an array of items from the store filtered by a filter function which is suppose to be constantly updated whenever new items are pushed into the store. By combining this with the store.find method which will push items into the store, the filteredRecordArray should automatically update with the new items thus updating the model and resulting in new items showing on the page.
For instance, assume we have a Questions Route, Controller and a model of type Question.
App.QuestionsRoute = Ember.Route.extend({
model: function (urlParams) {
return this.get('store').filter('question', function (q) {
return true;
});
}
});
Then we have a controller with some method that will call store.find, this could be triggered by some event/action whether it be detecting scroll events or the user explicitly clicking to load more, regardless this method would be called to load more questions.
Example:
App.QuestionsController = Ember.ArrayController.extend({
...
loadMore: function (offset) {
return this.get('store').find('question', { skip: currentOffset});
}
...
});
And the template to render the items:
...
{{#each question in controller}}
{{question.title}}
{{/each}}
...
Notice, that with this method we do NOT have to add a function to the store.find promise which explicitly calls this.get('model').pushObjects(questions); In fact, trying to do that once you have already returned a filter record array to the model does not work. Either we manage the content of the model manually, or we let ember-data do the work and I would very much like to let Ember-data do the work.
This is is a very clean API; however, it does not seem to work they way I've written it. Based on the documentation I cannot see anything wrong.
Using the Ember-Inspector tool from chrome I can see that the new questions from the second find call are loaded into the store under the 'question' type but the page does not refresh until I change routes and come back. It seems like the is simply a problem with observers, which made me think that this would be a bug in Ember-Data, but I didn't want to jump to conclusions like that until I asked to see if I'm using Ember-Data as intended.
If someone doesn't know exactly what is wrong but knows how to use store.push/pushMany to recreate this scenario in a jsbin that would also help too. I'm just not familiar with how to use the lower level methods on the store.
Help is much appreciated.
I just made this pattern work for myself, but in the "traditional" way, i.e. without using store.filter().
I managed the "loadMore" part in the router itself :
actions: {
loadMore: function () {
var model = this.controller.get('model'), route = this;
if (!this.get('loading')) {
this.set('loading', true);
this.store.find('question', {offset: model.get('length')}).then(function (records) {
model.addObjects(records);
route.set('loading', false);
});
}
}
}
Since you already tried the traditional way (from what I see in your post on discuss), it seems that the key part is to use addObjects() instead of pushObjects() as you did.
For the records, here is the relevant part of my view to trigger the loadMore action:
didInsertElement: function() {
var controller = this.get('controller');
$(window).on('scroll', function() {
if ($(window).scrollTop() > $(document).height() - ($(window).height()*2)) {
controller.send('loadMore');
}
});
},
willDestroyElement: function() {
$(window).off('scroll');
}
I am now looking to move the loading property to the controller so that I get a nice loader for the user.