vue.js watcher value does not update value in select v-model - vue.js

I have a form with a two select boxes.sb1 and sb2. sb2 with a v-model: result.
If I change the value of the first one (sb1) I have to do a some async operation with the value and to obtain a new value that must be placed in the other select box(sb2). For this I've used a watch executing an async fuction, and after I obtain the async value I make this.result = newValue.
In the console.log is ok, but not in the select box (sb2) that shows nothing
but then, when I unselect a value in the first box (sb1) the value I previously obtained shows up in (sb2)
in general, what is going on here? How can I workaround this?
Thanks in advance
EDIT:
<label for="timezone" >Timezone</label>
<select
id="timezone"
v-model="localValue.timezone"
>
<option v-for="(timeZone, index) in arrayTZ()" :key="index" :value="timeZone" :selected="localValue.timezone === timeZone">
{{ timeZone }}
</option>
</select>
and the watcher
methods:{
async getFunction1(id){
await dispatchingActions
return objectFromgetFunction1.current
}
},
watch: {
async "localValue.ManagerId"(id){
console.log(id)
if(id !== null){
await this.getFunction1(id)
this.localValue.timezone = objectFromgetFunction1.current.timezone;
}
if(id === null){
this.localValue.timezone = null
}
console.log(this.localValue.timezone)
},

Related

Laravel/Ajax/SQL: trying to show the current value on when null or not

Below is a screenshot of my table. My goal is in the effective_start_datetime, I want it to show as status active if there is a value in it, and inactive if it is null when editing. (show current status on edit click)
Model: (getting the SQL data)
$group_edit = HmsBbrGroup::find($group_id);
Table:
<select id="edit_effective_start_datetime" class="form-control w-100 edit_effective_start_datetime">
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
Form: (only status is not showing)
Ajax: (output form content when the edit button is clicked)
$(document).on('click','.edit_group',function (e) {
e.preventDefault();
var g_id = $(this).val();
console.log(g_id);
$('#editGroupModal').modal('show');
$.ajax({
type: "GET",
url: "/clinical/bbr-group-configuration-edit/"+g_id,
success: function (response) {
console.log(response);
if(response.status == 404) {
$('#success_message').html("");
$('#success_message').addClass('alert alert-danger');
$('#success_message').text('response.message');
} else {
$('#edit_group_name').val(response.group_edit.group_name);
$('#edit_group_description').val(response.group_edit.group_description);
$('#edit_group_id').val(response.group_edit.group_id);
$('#edit_effective_start_datetime').val(response.group_edit.effective_start_datetime).change();
$('#edit_group_type_id').val(response.group_edit.group_type_id).change();
}
}
});
});
As you can see from the form, my ajax outputs the contents besides the status here in $('#edit_effective_start_datetime').val(response.group_edit.effective_start_datetime).change(); I am trying to figure out the solution to show the <option> as inactive or active if the datetime is null or not
I tried to add a function but this is not working:
$('#edit_effective_start_datetime').val(getgroupstatus(response.group_edit.edit_effective_start_datetime)).change();
function getgroupstatus(status) {
var g_status = '';
if (status === null) {
g_status = 'inactive'
} else {
g_status = 'active'
}
return g_status;
}
Any help/advice on how to show the status would help alot, thanks.
Use boolean values instead of strings - and your code will be simpler
<select id="edit_effective_start_datetime">
<option value=0>Inactive</option>
<option value=1>Active</option>
<select>
$("#edit_effective_start_datetime").val(response.group_edit.effective_start_datetime === null ? 0 : 1);
You can use prop
var effective_start_datetime=response.group_edit.edit_effective_start_datetime?
'active':
'inactive';
$('#edit_effective_start_datetime option[value='+effective_start_datetime+']').prop("selected",true);

how to append multiple query parameters in url - NuxtJS

I am creating a nuxt ecommerce application. I have a situation where I have more than 10,000 items in a category and i want to create related filters for the products.
My question is how do i append url (add & remove query parameters) so that i can filter products.
I have tried something like this by adding a change event to !
<ul>
<li>
<b-form-checkbox #change="filterProduct">
<label class="ui__label_checkbox">Apple</label>
</b-form-checkbox>
</li>
<li >
<b-form-checkbox #change="filterProduct">
<label class="ui__label_checkbox">Mango</label>
</b-form-checkbox>
</li>
</ul>
methods:{
filterProduct() {
this.$router.push({ query: Object.assign({}, this.$route.query, { random: "query" }) });
},
}
This approach does append the url only once but removes the checked state of the checkbox which i don't want
I want similar to below everytime i click checkbox, it must retain the state of the checkbox at the same time append to the url
www.foobar.com/?first=1&second=12&third=5
Here's what you should do. First of all, you should all your filters state in data()
data() {
return {
filter: {
first: this.$route.query.first || null,
second: this.$route.query.second || null,
third: this.$route.query.third || null
}
}
}
Then you set up a watcher that fires when any filter changes, obviusly you need to v-model the inputs in your <template> to the fields in data()
watch() {
filter: {
handler(newFilters) {
const q = complexToQueryString({
...this.filter,
})
const path = `${this.$route.path}?${q}`
this.$router.push(path)
}
}
}
The complexToQueryString function is a thing of mine which removes null values from the query and also works for filters that are arrays. I did this because my API reads null as String 'null'.
const complexToQueryString = (object, parentNode = null) => {
const query = Object.entries(object).map((item) => {
const key = parentNode ? `${parentNode}[${item[0]}]` : item[0]
const value = item[1]
if (Array.isArray(value)) {
return arrayToQueryString(value, key)
} else if (value instanceof Object) {
return complexToQueryString(value, key)
} else if (item[1] !== undefined) {
return [
Array.isArray(item[0]) ? `${key}[]` : key,
encodeURIComponent(item[1]),
].join('=')
}
return ''
})
.filter(empty => empty)
.join('&')
return query
}
Now it should work, if you change the filter value then the data.filter.first changes the value, which fires the watcher, which updates the URL.
The best thing about this aproach is that now you can copy & paste the URL and the filter is exactly the same and returns the same result.
Your approach is almost correct, except that on page request, router-level You should append all the query parameters to route params.
Then asign those params to data inside Your filter page, and mutate them, also updating the query like You're doing now. This way You'll have query updated, and checkboxes wont lose state as they will depend on data, rather than on params.
routes: [{
path: '/path',
component: Component,
props: (route) => ({
filter1: route.query.filter1,
filter2: route.query.filter2,
filter3: route.query.filter3
})
}]

Vuejs Updating v-for by reference

So I have a simple v-for and each item in the v-for has a #click on it
<result-index>
<li v-for="(result, index) in results" #click="getReult(results[index])">
{{ result.name }}
</li>
</result-index>
Now, my method for getResult just assigns that result to a result data:
methods: {
getResult: function(result) {
// when the child <result-update> updates this, it updates fine, but it doesn't update the v-for reference of this.
this.result = result;
}
}
Now I have another component that get the data for that result and display it:
<result-index>
<li v-for="(result, index) in results" #click="getReult(results[index])">
{{ result.name }}
</li>
<result-update v-if="result" v-model="result">
//... here is a form to access the result and update it
</result-update>
</result-index>
In my result-update I am updating via the index and value like so:
methods: {
update(e) {
this.$emit("input", //data here...);
},
}
watch: {
value: function() {
this.form = this.value;
},
},
created() {
this.form = __.cloneDeep(this.value);
}
Which update the parent result fine (the one we used the #click on), but not the v-for reference of that result, so how can I update the v-for reference of the result when it changes in , also please note, it is not possible for me to put the inside the v-for due to the css design of this, it needs to be seperate from the ...
When this.result = result, this.result points to one address of the memory.
When <result-update v-if="result" v-model="result"> receives input event then assign new value to this.result, it will make this.result = newValue (actually point to another address of the memory for newValue), so it will not change the value for result[index] as you expected.
Check below demo:
const test = {result: []}
let value1 = ['a']
console.log('org', test)
test.result = value1 // assign with new array
console.log('Replace the whole array', test)
value1[0] = 'b' // assign new value to first element of value1
console.log('Update one item of the array', test) //test.result and value1 point to same address of the memory
The solution:
You can save the index for current <result-index>, then change the value by this.results[index].
So adjust your codes to below then should work fine.
For the template of component <result-index>, change it to:
<result-index>
<li v-for="(result, index) in results" #click="getReult(index)">
{{ result.name }}
</li>
</result-index>
For the method=getResult inside component <result-index>, change it to:
methods: {
getResult: function(index) {
this.selected = index;
}
}
Inside the parent component, change the template to:
<result-update v-if="selected >= 0" v-model="results[selected]">
//... here is a form to access the result and update it
</result-update>

How to initialize value based on ajax response?

I need to create a checkbox that will be checked/unchecked depending on the value of a parameter coming from the database.
I'm not able to load that value when I'm rendering the page, so the idea is: render the page, "tell" the checkbox to "ask" the server what is the current value of the parameter and then check/uncheck the checkbox depending on the response. Then, if the user checks/unchecks the checkbox, make a new Ajax request to update the value in the database.
I wrote some code (I'm new in Vuejs, so for sure I'm doing something wrong):
var vm = new Vue({
el: '#root',
computed: {
checked() {
return this.initialize()
},
value() {
return this.checked
}
},
watch: {
checked() {
alert('watcher')
this.update();
}
},
methods: {
initialize(){
// Just pretending an initial value
var randomBoolean = Math.random() >= 0.5;
alert('Ajax request here to initialize it as ' + (randomBoolean ? 'checked' : 'unchecked'));
return randomBoolean;
},
update(){
alert('ajax request here to set it to ' + this.value)
}
}
});
You can check and run the code here: https://jsfiddle.net/hyn9Lcv2/
Basically it works to initialize the checkbox, but then it fails to update. If you check the console, there is this error:
[Vue warn]: Computed property "checked" was assigned to but it has no setter.
First have you thought of using the created() hook from the vue instance instead of watcher?
It's recommended and will execute the code as soon as the component is created.
From the doc:
new Vue({
data: {
a: 1
},
created: function () {
//Ajax call:
//onsuccess(response){
this.a = reponse.data.a
}
}
})
in the created hook you can do your ajax call, (axios is good library for that, worth checking it out: https://github.com/axios/axios ).
Then from your ajax response you can link the desired value to your checkbox by assigning it to a variable in the data object of the instance (in our case 'a')
Then bind it to your checkbox with the v-model like this:
<input
type="checkbox"
v-model="a">
I recommend to check the vue doc for more info on biding: https://v2.vuejs.org/v2/guide/forms.html#Checkbox-1
Hope it helps.
Just add bind click event
<div id="root">
<input id="check" type="checkbox" name="active" v-model="checked" #click="update">
<label for="check">Click me</label>
</div>
You need to fetch the database value when the component is created or mounted.
You then need to bind your checkbox with the initialized data.
Finally you need to watch the data to send an update to the database.
var vm = new Vue({
el: '#root',
data: {
//Your data
checked: null
},
// Function where you are going to fetch your data
mounted: function () {
console.log("Ajax call to initialize");
this.checked = Math.random() >= 0.5;
},
watch: {
// Watcher to save your data in the database
checked: function(newValue, oldValue){
if (oldValue === null) { return; } // to not make an useless update when data has been fetched
console.log("Ajax call to update value " + newValue);
}
}
});
<div id="root">
<input id="check" type="checkbox" name="active" v-model="checked" :disabled="checked === null">
<label for="check">Click me</label>
</div>
To fetch your data you can use for example Axios that works great with Vue.
To know more about life cycle of a component (to know if you should do the fetching at created or mounted) : https://v2.vuejs.org/v2/guide/instance.html

Vue.js 2 - $forceUpdate() on components doesn't refresh computed properties?

I'm not sure if I'm doing this right or wrong, but all the answers I seem to find how to update the dom for computed values...
I have this component:
Vue.component('bpmn-groups', {
props: ['groups', 'searchQuery'],
template: '#bpmn-groups',
computed: {
filteredGroups: function () {
var self = this;
return this.groups.filter(function(group) {
self.searchQuery = self.searchQuery || '';
return _.includes( group.name.toLowerCase(), self.searchQuery.toLowerCase() );
});
}
},
methods: {
clearFilter: function () {
this.searchQuery = '';
},
deleteGroup: function(group) {
Vue.http.delete('api/groups/'+group.id ).then(response => { // success callback
var index = this.groups.indexOf(group); // remove the deleted group
this.groups.splice(index, 1);
this.$forceUpdate(); // force update of the filtered list?
toastr.success('Schemų grupė <em>'+group.name+'</em> sėkmingai pašalinta.');
}, response => { // error callback
processErrors(response);
});
this.$forceUpdate();
},
},
});
And in the template I just have a simple v-for to go through filteredGroups:
<input v-model="searchQuery" type="text" placeholder="Search..." value="">
<div v-for="group in filteredGroups" class="item">...</div>
The deletion works fine, it removes it from groups property, however the filteredGroups value still has the full group, until I actually perform a search or somehow trigger something else...
How can I fix it so that the filteredGroup is updated once the group is updated?
Don't mutate a prop - they are not like data defined attributes. See this for more information:
https://v2.vuejs.org/v2/guide/components.html#One-Way-Data-Flow
Instead, as recommended in the link, declare a local data attribute that is initialized from the prop and mutate that.