How can I change selected item in qselect on page load - vue.js

I use a QSelect Dropdown with some options on my page header, like the following:
<q-select
filled
v-model="model"
use-input
hide-selected
fill-input
input-debounce="0"
:options="options"
#filter="filterFn"
hint="Basic autocomplete"
style="width: 250px; padding-bottom: 32px"
emit-value
map-options
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
No results
</q-item-section>
</q-item>
</template>
</q-select>
const stringOptions = [
{label:'Google', value:'g1111111111'}, {label:'Facebook', value:'f2222222222'}, {label:'Twitter', value:'t3333333'}, {label:'Apple', value:'a44444444'}, {label:'Oracle', value:'o555555555'}
]
new Vue({
el: '#q-app',
data () {
return {
model: 'f2222222222',
options: stringOptions
}
},
methods: {
filterFn (val, update, abort) {
update(() => {
const needle = val.toLowerCase()
this.options = stringOptions.filter(v => v.label.toLowerCase().indexOf(needle) > -1)
})
}
}
})
How can I use a method to change the selected value on pageload for example from facebook to google?
I think with something like the following but cant get it working:
mounted: function () {
this.model = 'g1111111111'
},
codepen: https://codepen.io/aquadk/pen/JQbbKw
Thanks

You can use updated method, it called after the data changed and virtual dom is created for that component. Then you can update the value of the model.
const stringOptions = [
{label:'Google', value:'g1111111111'}, {label:'Facebook', value:'f2222222222'}, {label:'Twitter', value:'t3333333'}, {label:'Apple', value:'a44444444'}, {label:'Oracle', value:'o555555555'}
]
new Vue({
el: '#q-app',
data () {
return {
model: 'f2222222222',
options: stringOptions
}
},
methods: {
filterFn (val, update, abort) {
update(() => {
const needle = val.toLowerCase()
this.options = stringOptions.filter(v => v.label.toLowerCase().indexOf(needle) > -1)
})
}
},
updated(){
// Update the value of model
this.model = 'g1111111111';
}
})

The mounted should work, if it's not working the way you expect, try inside-mounted nextTick().
Here is an example with your code:
mounted () {
this.$nextTick(() => {
this.model = 'a44444444'
})
},

Related

Problems with chart.js redrawing or not redrawing graphs

I am Japanese. Therefore, my sentences may be strange. Please keep that in mind.
I am writing code using vue.js, vuex, vue-chart.js and vue-chart.js to display the population of each prefecture of Japan when checked.I’m code is written to redraw the graph when the input element for each prefecture is checked.However, it does not redraw when checked.Also, it may redraw after half of the check.I believe this phenomenon can be confirmed from the following URL.
https://yumemi-coding.web.app/
※There are no errors.
Here's a question: what causes the graphs to redraw or not? Also, how can I code to remedy this?
What I have done to counteract the cause is as follows
I went to the official website and used the rendering process as a reference.
 URL:https://vue-chartjs.org/migration-guides/#new-reactivity-system
 => The way we did it was right.
We thought there was a problem with VueX and coded in a way that did not use it. => There was nothing wrong with vuex.
TopFroont.vue
<template>
<div class="Bar_area">
<Bar :options="chartOptions" :data="chartData" class="Bar_item" />
</div>
</template>
<script>
import { Bar } from "vue-chartjs"
import { Chart as ChartJS, registerables } from "chart.js"
ChartJS.register(...registerables)
export default {
name: "BarChart",
components: { Bar },
data() {
return {
chartOptions: {
responsive: true,
},
}
},
computed: {
chartData() {
return {
labels: this.$store.state.years,
datasets: this.$store.state.prefectures,
}
},
},
}
</script>
NaviBar.vue
<template>
<div class="navApp">
<ul>
<li v-for="(pref, index) in prefData" :key="index" class="pref_itemBox">
<label>
<input type="checkbox" #change="checkItem(pref)" />
<span class="pref_text">{{ pref.prefName }}</span>
</label>
</li>
</ul>
</div>
</template>
<script>
import resasInfo from "#/library/resas.js"
import axios from "axios"
export default {
data() {
return {
resasInfo: resasInfo,
url: resasInfo.url_prefectures,
api: resasInfo.api,
prefData: [],
prefectures: [],
}
},
async created() {
const request_Header = {
headers: { "X-API-KEY": this.api.key },
}
await axios.get(this.url, request_Header).then((res) => {
const value = res.data.result
this.prefData.push(...value)
})
},
methods: {
checkItem(pref) {
// チェックされてる都道府県のみを配列に入れる
const isExistencePref = this.prefectures.indexOf(pref)
isExistencePref === -1
? this.prefectures.push(pref)
: this.prefectures.splice(isExistencePref, 1)
this.$store.dispatch("getPrefectures", this.prefectures)
},
},
}
</script>
vuex => store/index.js
import axios from "axios"
import { createStore } from "vuex"
import createPersistedState from "vuex-persistedstate"
export default createStore({
state: {
prefectures: [],
years: [],
},
mutations: {
getPrefs(state, payload) {
state.prefectures = payload
},
getYears(state, payload) {
state.years = payload
},
},
actions: {
getPrefectures({ commit }, payload) {
// payload => 各都道府県のprefCode + prefName
const allPrefecture_Data = []
const result = payload.map(async (el) => {
const prefCode_data = el.prefCode
axios
.get(
`https://opendata.resas-portal.go.jp/api/v1/population/composition/perYear?prefCode=${prefCode_data}&cityCode=-`,
{
headers: {
"X-API-KEY": "5RDiLdZKag8c3NXpEMb1FcPQEIY3GVwgQwbLqFIx",
},
}
)
.then((res) => {
const value = res.data.result.data[0].data
const TotalPopulation_Year = []
const TotalPopulation_Data = []
// 都道府県の総人口データと年データを各配列に入れ込む
value.forEach((element) => {
TotalPopulation_Data.push(element.value)
TotalPopulation_Year.push(element.year)
})
// rgbaを自動生成する関数 => backgroundColor
const generateRGBA = () => {
const r = Math.floor(Math.random() * 256)
const g = Math.floor(Math.random() * 256)
const b = Math.floor(Math.random() * 256)
const a = 0.8
return `rgba(${r}, ${g}, ${b}, ${a})`
}
// chart.jsに入れ込むデータ
const prefData = {
label: el.prefName,
data: TotalPopulation_Data,
backgroundColor: generateRGBA(),
}
allPrefecture_Data.push(prefData)
commit("getPrefs", allPrefecture_Data)
commit("getYears", TotalPopulation_Year)
})
.catch((err) => {
console.log(err)
})
})
return result
},
},
plugins: [createPersistedState()],
getters: {},
modules: {},
})

Vue2 composition API array property update not updating

I have this component that accepts an array as a property:
import {
defineComponent,
getCurrentInstance,
toRefs,
watch,
} from "#vue/composition-api";
import { RecommendationAnswer, RecommendationQuestion } from "#models";
import { useCalculateInitialCount } from "./calculate-count";
import { useGetAnsweredQuestions } from "./list-questions";
export default defineComponent({
name: "StepThree",
emits: ["onSelect"],
props: {
products: {
type: Array,
required: false,
default: () => [],
},
questions: {
type: Array,
required: false,
default: () => [],
},
},
setup(props) {
const instance = getCurrentInstance();
const { products, questions } = toRefs(props);
watch(
products,
(currentProducts: any[]) => {
if (!currentProducts) return;
const currentQuestions = <RecommendationQuestion[]>questions.value;
useCalculateInitialCount(currentProducts, currentQuestions);
},
{
immediate: true,
}
);
const selectAnswer = (answer: RecommendationAnswer) => {
answer.selected = !answer.selected;
questions.value.forEach((question: RecommendationQuestion) => {
question.selected = !!question.answers.find(
(item: RecommendationAnswer) => item.selected
);
});
const answeredQuestions = useGetAnsweredQuestions(
<RecommendationQuestion[]>questions.value
);
instance.proxy.$emit("onSelect", {
step: 3,
questions: answeredQuestions,
});
};
return { selectAnswer };
},
});
The watch is triggered whenever the products array changes (which happens outside of this component).
I can see that the watch fires and then the function useCalculateInitialCount fires, which updates the count property on an answer.
This is displayed in the template:
<v-col cols="6">
<base-fade-up class="row" :duration="0.1" tag="div">
<v-col
class="text-center"
cols="12"
v-for="question in questions.slice(
0,
Math.ceil(questions.length / 2)
)"
:key="question.id"
>
{{ question.title }}
<v-card
class="w-100"
outlined
#click="selectAnswer(answer)"
v-for="answer in question.answers"
:key="answer.id"
>
<v-card-text class="text-center">
{{ answer.title }} ({{ answer.count }})
</v-card-text>
</v-card>
</v-col>
</base-fade-up>
</v-col>
When the component loads, the watch fires and the counts are displayed correctly:
But when the products update, even though I see the changes in the console.log:
The template does not update.
Does anyone know how I can get around this?
I think it's because your array does not have a new item, so for the watcher is the same array with the same amount of items even if one of them has changed. I'm not sure why you have to watch a property but if you need to watch all the changes in the array you can try to make a copy of the array first and then watch that copied array
I figured a work around for this, by created a computed property instead of watching the products.
The entire code looks like this:
import {
computed,
defineComponent,
getCurrentInstance,
toRefs,
watch,
} from "#vue/composition-api";
import { RecommendationAnswer, RecommendationQuestion } from "#models";
import { useCalculateInitialCount } from "./calculate-count";
import { useGetAnsweredQuestions } from "./list-questions";
export default defineComponent({
name: "StepThree",
emits: ["onSelect"],
props: {
products: {
type: Array,
required: false,
default: () => [],
},
questions: {
type: Array,
required: false,
default: () => [],
},
},
setup(props) {
const instance = getCurrentInstance();
const { products, questions } = toRefs(props);
const questionsWithCount = computed(() => {
const currentProducts = <any[]>products.value;
const currentQuestions = [...(<RecommendationQuestion[]>questions.value)];
if (!currentProducts?.length || !currentQuestions?.length) return;
useCalculateInitialCount(currentProducts, currentQuestions);
return currentQuestions;
});
const selectAnswer = (answer: RecommendationAnswer) => {
answer.selected = !answer.selected;
questions.value.forEach((question: RecommendationQuestion) => {
question.selected = !!question.answers.find(
(item: RecommendationAnswer) => item.selected
);
});
const answeredQuestions = useGetAnsweredQuestions(
<RecommendationQuestion[]>questions.value
);
instance.proxy.$emit("onSelect", {
step: 3,
questions: answeredQuestions,
});
};
return { questionsWithCount, selectAnswer };
},
});
This fixed the issue, because in the template I use the questionsWithCount instead of the questions

Vue. Autosave input value with localstorage

I have a component with a form for which I need to implement data autosave when entering content. How to apply an example for v-model = "titleName" to v-model = "article.title[currentLanguage]"?
P.S. currentLanguage is taken from store
<div id="app">
Title is <input type="text" name="titleName" id="titleName" v-model="titleName">
Title is <input type="text" name="article" id="article"
v-model="article.title.en">
</div>
<script>
var app = new Vue({
el:'#app',
data: {
titleName: '',
article: {
title: {
en: null,
ru: null
}
}
},
computed: {
availableLanguages() {
return this.$store.state.languages;
}
},
created() {
this.setDefaults();
},
mounted () {
const SAVED = localStorage.getItem('content')
if (SAVED) {
this.titleName = SAVED
}
},
watch: {
titleName(newTitleName) {
localStorage.content = newTitleName;
}
}
</script>
https://codepen.io/pershay/pen/EJQGGO
Just use localStorage.setItem('titleName', newTitleName) and localStorage.getItem('titleName')
You don't need to load on mounted. Place it in the data function itself. I think this is the better way:
var app = new Vue({
data:() => ({
titleName: localStorage.getItem('titleName'),
article: JSON.parse(localStorage.getItem('article'))
}),
watch: {
titleName(newTitleName) {
localStorage.setItem('titleName', newTitleName)
},
article(newArticle) {
handler: function(newArticle) {
localStorage.setItem('article', JSON.stringify(newArticle))
},
deep: true
},
}
The example above also includes a deep object watch.

Updating a prop inside a child component so it updates on the parent container too

So I have a simple template like so:
<resume-index>
<div v-for="resume in resumes">
<resume-update inline-template :resume.sync="resume" v-cloak>
//...my forms etc
<resume-update>
</div>
<resume-index>
Now, inside the resume-updatecomponent I am trying to update the prop on the inside so on the outside it doesn't get overwritten, my code is like so;
import Multiselect from "vue-multiselect";
import __ from 'lodash';
export default {
name: 'resume-update',
props: ['resume'],
components: {
Multiselect
},
data: () => ({
form: {
name: '',
level: '',
salary: '',
experience: '',
education: [],
employment: []
},
submitted: {
form: false,
destroy: false,
restore: false
},
errors: []
}),
methods: {
update(e) {
this.submitted.form = true;
axios.put(e.target.action, this.form).then(response => {
this.resume = response.data.data
this.submitted.form = false;
}).catch(error => {
if (error.response) {
this.errors = error.response.data.errors;
}
this.submitted.form = false;
});
},
destroy() {
this.submitted.destroy = true;
axios.delete(this.resume.routes.destroy).then(response => {
this.resume = response.data.data;
this.submitted.destroy = false;
}).catch(error => {
this.submitted.destroy = false;
})
},
restore() {
this.submitted.restore = true;
axios.post(this.resume.routes.restore).then(response => {
this.resume = response.data.data;
this.submitted.restore = false;
}).catch(error => {
this.submitted.restore = false;
})
},
reset() {
for (const prop of Object.getOwnPropertyNames(this.form)) {
delete this.form[prop];
}
}
},
watch: {
resume: function() {
this.form = this.resume;
},
},
created() {
this.form = __.cloneDeep(this.resume);
}
}
When I submit the form and update the this.resume I get the following:
[Vue warn]: Avoid mutating a prop directly since the value will be
overwritten whenever the parent component re-renders. Instead, use a
data or computed property based on the prop's value. Prop being
mutated: "resume"
I have tried adding computed to my file, but that didn't seem to work:
computed: {
resume: function() {
return this.resume
}
}
So, how can I go about updating the prop?
One solution:
simulate v-model
As Vue Guide said:
v-model is essentially syntax sugar for updating data on user input
events, plus special care for some edge cases.
The syntax sugar will be like:
the directive=v-model will bind value, then listen input event to make change like v-bind:value="val" v-on:input="val = $event.target.value"
So the steps:
create one prop = value which you'd like to sync to parent component
inside the child component, create one data porperty=internalValue, then uses Watcher to sync latest prop=value to data property=intervalValue
if intervalValue change, emit one input event to notice parent component
Below is one simple demo:
Vue.config.productionTip = false
Vue.component('container', {
template: `<div>
<p><button #click="changeData()">{{value}}</button></p>
</div>`,
data() {
return {
internalValue: ''
}
},
props: ['value'],
mounted: function () {
this.internalValue = this.value
},
watch: {
value: function (newVal) {
this.internalValue = newVal
}
},
methods: {
changeData: function () {
this.internalValue += '#'
this.$emit('input', this.internalValue)
}
}
})
new Vue({
el: '#app',
data () {
return {
items: ['a', 'b', 'c']
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<div>
<p>{{items}}
<container v-for="(item, index) in items" :key="index" v-model="items[index]">
</container>
</div>
</div>
or use other prop name instead of value (below demo use prop name=item):
Also you can use other event name instead of event name=input.
other steps are similar, but you have to $on the event then implement you own handler like below demo.
Vue.config.productionTip = false
Vue.component('container', {
template: `<div>
<p><button #click="changeData()">{{item}}</button></p>
</div>`,
data() {
return {
internalValue: ''
}
},
props: ['item'],
mounted: function () {
this.internalValue = this.item
},
watch: {
item: function (newVal) {
this.internalValue = newVal
}
},
methods: {
changeData: function () {
this.internalValue += '#'
this.$emit('input', this.internalValue)
this.$emit('test-input', this.internalValue)
}
}
})
new Vue({
el: '#app',
data () {
return {
items: ['a', 'b', 'c']
}
},
methods: {
syncChanged: function (target, index, newData) {
this.$set(target, index, newData)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<div>
Event Name=input
<p>{{items}}</p>
<container v-for="(item, index) in items" :key="index" :item="item" #input="syncChanged(items, index,$event)">
</container>
</div>
<hr> Event Name=test-input
<container v-for="(item, index) in items" :key="index" :item="item" #test-input="syncChanged(items, index,$event)">
</container>
</div>
I usually use vuex to manage variables that I will be using in multiple components and like the error says, load them in the various components using the computed properties. Then use the mutations property of the store object to handle changes
In component files
computed: {
newProfile: {
get() {
return this.$store.state.newProfile;
},
set(value) {
this.$store.commit('updateNewProfile', value);
}
},
In the vuex store
state: {
newProfile: {
Name: '',
Website: '',
LoginId: -1,
AccountId: ''
}
},
mutations: {
updateNewProfile(state, profile) {
state.newProfile = profile;
}
}

Getting state, getters, actions of vuex module in vue component

I tried the syntax given in vuex doc.
store.state.a // -> moduleA's state
store.state.b // -> moduleB's state
app.js
/**
* First we will load all of this project's JavaScript dependencies which
* includes Vue and other libraries. It is a great starting point when
* building robust, powerful web applications using Vue and Laravel.
*/
require('./bootstrap');
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
Vue.component('task-index', require('./components/TaskList.vue'));
Vue.component('task-show', require('./components/TaskShow.vue'));
Vue.component('note-index', require('./components/NoteList.vue'));
Vue.component('note-show', require('./components/NoteShow.vue'));
const notes = {
state: {
edit: false,
list:[],
note: {
note : '',
id : ''
}
},
mutations: {
SET_EDIT: (state, data) => {
state.edit = data
},
SET_LIST: (state, data) => {
state.list = data
},
SET_NOTE: (state, data) => {
state.note.id = data.id;
state.note.note = data.note;
},
SET_EMPTY: (state) => {
state.note.note = '';
}
},
getters: {
noteCount: (state) => state.list.length
},
actions : {
getNote: ({commit,state}) => {
axios.get('/api/note/list')
.then((response) => {
commit('SET_LIST', response.data);
commit('SET_EDIT',false);
commit('SET_EMPTY');
})
},
}
};
const tasks = {
state: {
edit: false,
list:[],
task: {
body : '',
id : ''
}
},
mutations: {
SET_EDIT: (state, data) => {
state.edit = data
},
SET_LIST: (state, data) => {
state.list = data
},
SET_TASK: (state, data) => {
state.task.id = data.id;
state.task.body = data.body;
},
SET_EMPTY: (state) => {
state.task.body = '';
}
},
getters: {
taskCount: (state) => state.list.length
},
actions : {
getTask: ({commit,state}) => {
axios.get('/api/task/list')
.then((response) => {
commit('SET_LIST', response.data);
commit('SET_EDIT',false);
commit('SET_EMPTY');
})
},
}
};
const store = new Vuex.Store({
modules : {
task : tasks,
note : notes
}
});
const app = new Vue({
el: '#app',
store
});
TaskList.vue
<template>
<div >
<h4>{{count}} Task(s)</h4>
<ul class="list-group">
<li class="list-group-item" v-for="item in list">
{{item.body}}
<button class="btn btn-primary btn-xs" #click="showTask(item.id)">Edit</button>
<button class="btn btn-danger btn-xs" #click="deleteTask(item.id)">Delete</button>
</li>
</ul>
</div>
</template>
<script>
export default{
computed :{
list() {
return this.$store.state.task.list;
},
count(){
return this.$store.getters.taskCount;
}
},
mounted(){
this.$store.dispatch('getTask');
},
methods : {
showTask: function(id){
axios.get('/api/task/'+ id)
.then(response => {
this.$store.commit('SET_TASK',response.data);
this.$store.commit('SET_EDIT',true);
});
},
deleteTask: function(id){
axios.delete('/api/task/delete/' + id)
this.$store.dispatch('getTask');
}
}
}
</script>
I'am getting "Uncaught TypeError: Cannot read property 'task' of undefined " in this line of code 'return this.$store.state.task.list;'
acoording to documentation of vuex
By default, actions, mutations and getters inside modules are still
registered under the global namespace
so you can only use getters in vuex root context.
Well, the state you're trying to retrieve doesn't match the structure of your state:
state: {
edit: false,
list:[],
note: {
note : '',
id : ''
}
},
If you change this.$store.state.task.list to this.$store.state.list then you should be all patched up.