Update element on button click is not working - vue.js

I'm new using Vuejs so I have a simple page with an input and button like
<input type="text" value="10" v-model="model.rowsPerPage">
<button #click="setNewValue()"> </button>
<script lang="ts">
import { useContact } from '#/composition/useContact'
import { ContactModel } from '#/services/modules/contact/contact'
import { defineComponent, reactive } from '#vue/composition-api'
export default defineComponent({
name: 'Contact',
setup() {
const model = reactive<ContactModel>({
pageNumber: 1,
rowsPerPage: 10,
searchBy: null,
staringDate: null,
endingDate: null,
})
const {
data: contactList,
totalItems: totalItems,
isLoading: isLoading,
error: error,
} = useContact(model)
return {
contactList,
model,
isLoading,
totalItems,
error,
}
},
methods: {
setNewValue() {
model.rowsPerPage = 20
},
},
})
</script>
So what I want to do, is update the input value by 10 if the button is clicked, so as you can see I add a method at the end of the script
methods: {
setNewValue() {
model.rowsPerPage = 20
},
},
But it throws me an error:
Cannot find name model
Why I can't access to const model declared at the top of script? How can I solve this? Regards

inside the methods you need to reference the "this" object to have access to what's returned in data or setup
but if you're using vue 3 with the composition api, you can simply define the function inside setup and return it, your template will then have access to it
(also, I think you should keep your component html inside a template tag)

Related

VueJS Loading template component via code?

I am trying to make a template component that I can use later on in my project. However I'm having a bit of a hard time showing it on the element I want via code.
The code I have so far is as such.
<template>
<div>
<b-alert show dismissible variant="danger" v-show="elementVisible">
<i class="mdi mdi-block-helper mr-2"></i>{{ text }}
</b-alert>
</div>
</template>
<script>
export default {
name: "alertDanager",
props: {
text: null
},
data() {
return {
elementVisible: true
};
},
created() {
setTimeout(() => (this.elementVisible = false), 5000);
}
};
</script>
I am trying to call this on an action by this
I import it
import dangerAlert from "#/components/Alerts/danger";
Then on the function I want to call it on I do this
const error = new dangerAlert({ propsData: { text: "Error message" } });
error.$mount("#error");
However it just gives me an error saying
_components_Alerts_danger__WEBPACK_IMPORTED_MODULE_3__.default is not a constructor
So I'm not sure how to fix this or do what I need to do. I've tried googling but can't seem to find an answer.
The Component imported is not a constructor and it should extends a constructor and to use that you should use Vue.extend()
Vue.extend() is a class inheritance method. Its task is to create a sub-class of Vue and return the constructor.
so instead of this
const error = new dangerAlert({ propsData: { text: "Error message" } });
error.$mount("#error");
make it like this
const DangerAlertExtended= Vue.extend(dangerAlert);
const error = new DangerAlertExtended({ propsData: { text: "Error message" } });
error.$mount("#error");

vuejs treeselect - delay loading does not work via vuex action

Using Vue TreeSelect Plugin to load a nested list of nodes from firebase backend. It's doc page says,
It's also possible to have root level options to be delayed loaded. If no options have been initially registered (options: null), vue-treeselect will attempt to load root options by calling loadOptions({ action, callback, instanceId }).
loadOptions (in my App.vue) dispatch vuex action_FolderNodesList, fetches (from firebase) formats (as required by vue-treeselect), and mutates the state folder_NodesList, then tries to update options this.options = this.get_FolderNodesList but this does not seems to work.
Here is the loadOptions method (in app.vue)
loadOptions() {
let getFolderListPromise = this.$store.dispatch("action_FolderNodesList");
getFolderListPromise.then(_ => {
this.options = this.get_FolderNodesList;
});
}
Vue errors out with Invalid prop: type check failed for prop "options". Expected Array, got String with value ""
I am not sure what am I doing wrong, why that does not work. A working Codesandbox demo
Source
App.vue
<template>
<div class="section">
<div class="columns">
<div class="column is-7">
<div class="field">
<Treeselect
:multiple="true"
:options="options"
:load-options="loadOptions"
:auto-load-root-options="false"
placeholder="Select your favourite(s)..."
v-model="value" />
<pre>{{ get_FolderNodesList }}</pre>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import Treeselect from "#riophae/vue-treeselect";
import "#riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
data() {
return {
value: null,
options: null,
called: false
};
},
components: {
Treeselect
},
computed: mapGetters(["get_FolderNodesList"]),
methods: {
loadOptions() {
let getFolderListPromise = this.$store.dispatch("action_FolderNodesList");
getFolderListPromise.then(_ => {
this.options = this.get_FolderNodesList;
});
}
}
};
</script>
Store.js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
folder_NodesList: ""
},
getters: {
get_FolderNodesList(state) {
return state.folder_NodesList;
}
},
mutations: {
mutate_FolderNodesList(state, payload) {
state.folder_NodesList = payload;
}
},
actions: {
action_FolderNodesList({ commit }) {
fmRef.once("value", snap => {
var testObj = snap.val();
var result = Object.keys(testObj).reduce((acc, cur) => {
acc.push({
id: cur,
label: cur,
children: recurseList(testObj[cur])
});
return acc;
}, []);
commit("mutate_FolderNodesList", result);
});
}
}
});
Any help is appreciated.
Thanks
It seems you are calling this.options which would update the entire element while only the current expanding option should be updated.
It seems loadOptions() is called with some arguments that you can use to update only the current childnode. The first argument seems to contain all the required assets so I wrote my loadTreeOptions function like this:
loadTreeOptions(node) {
// On initial load, I set the 'children' to NULL for nodes to contain children
// but inserted an 'action' string with an URL to retrieve the children
axios.get(node.parentNode.action).then(response => {
// Update current node's children
node.parentNode.children = response.data.children;
// notify tree to update structure
node.callback();
}).catch(
errors => this.onFail(errors.response.data)
);
},
Then I set :load-options="loadTreeOptions" on the <vue-treeselect> element on the page. Maybe you were only missing the callback() call which updates the structure. My installation seems simpler than yours but it works properly now.

Vue component computed not reacting

I have 2 components OperatorsList and OperatorButton.
The OperatorsList contains of course my buttons and I simply want, when I click one button, to update some data :
I emit select with the operator.id
This event is captured by OperatorList component, who calls setSelectedOperator in the store
First problem here, in Vue tools, I can see the store updated in real time on Vuex tab, but on the Components tab, the operator computed object is not updated until I click antoher node in the tree : I don't know if it's a display issue in Vue tools or a real data update issue.
However, when it's done, I have another computed property on Vue root element called selectedOperator that should return... the selected operator : its value stays always null, I can't figure out why.
Finally, on the button, I have a v-bind:class that should update when the operator.selected property is true : it never does, even though I can see the property set to true.
I just start using Vue, I'm pretty sure I do something wrong, but what ?
I got the same problems before I used Vuex, using props.
Here is my OperatorList code :
<template>
<div>
<div class="conthdr">Operator</div>
<div>
<operator-button v-for="operator in operators" :op="operator.id"
:key="operator.id" #select="selectOp"></operator-button>
</div>
</div>
</template>
<script>
import OperatorButton from './OperatorButton';
export default {
name: 'operators-list',
components : {
'operator-button': OperatorButton
},
computed : {
operators() { return this.$store.getters.operators },
selected() {
this.operators.forEach(op =>{
if (op.selected) return op;
});
return null;
},
},
methods : {
selectOp(arg) {
this.$store.commit('setSelectedOperator', arg);
}
},
}
</script>
OperatorButton code is
<template>
<span>
<button type="button" v-bind:class="{ sel: operator.selected }"
#click="$emit('select', {'id':operator.id})">
{{ operateur.name }}
</button>
</span>
</template>
<script>
export default {
name: 'operator-button',
props : ['op'],
computed : {
operator() {
return this.$store.getters.operateurById(this.op);
}
},
}
</script>
<style scoped>
.sel{
background-color : yellow;
}
</style>
and finally my app.js look like that :
window.Vue = require('vue');
import Vuex from 'vuex';
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';
const store = new Vuex.Store({
state: {
periods : [],
},
mutations: {
setInitialData (state, payload) {
state.periods = payload;
},
setSelectedOperator(state, payload) {
this.getters.operateurs.forEach( op => {
op.selected = (op.id==payload.id)
})
},
},
getters : {
operators : (state) => {
if (Array.isArray(state.periods))
{
let ops = state.periods
.map( item => {
return item.operators
}).flat();
ops.forEach(op => {
// op.selected=false; //replaced after Radu Diță answer by next line :
if (ops.selected === undefined) op.selected=false;
})
return ops;
}
},
operatorById : (state, getters) => (id) => {
return getters.operators.find(operator => operator.id==id);
},
}
});
import Chrono from './components/Chrono.vue';
var app = new Vue({
el: '#app',
store,
components : { Chrono },
mounted () {
this.$store.commit('setInitialData',
JSON.parse(this.$el.attributes.initialdata.value));
},
computed: {
...mapState(['periods']),
...mapGetters(['operators', 'operatorById']),
selectedOperator(){
this.$store.getters.operators.forEach(op =>{
if (op.selected) return op;
});
return null;
}
},
});
Your getter in vuex for operators is always setting selected to false.
operators : (state) => {
if (Array.isArray(state.periods))
{
let ops = state.periods
.map( item => {
return item.operators
}).flat();
ops.forEach(op => {
op.selected=false;
})
return ops;
}
}
I'm guessing you do this for initialisation, but that's a bad place to put it, as you'll never get a selected operator from that getter. Just move it to the proper mutations. setInitialData seems like the right place.
Finally I found where my problems came from :
The $el.attributes.initialdata.value came from an API and the operator objects it contained didn't have a selected property, so I added it after data was set and it was not reactive.
I just added this property on server side before converting to JSON and sending to Vue, removed the code pointed by Radu Diță since it was now useless, and it works.

Vuex-map-fields updating multiple stores through modules

I'm working Vuex modules to state my data.
I store the data in multiple modules to keep my code base nice and clean.
When using vuex-map-fields I have a situation where I'm using data from multiple modules.
There seems to be no method to do this or I am doing it wrong.
Below is my current code;
My component
<template>
<div class="">
<input type="text" v-model="no_panels"><br>
<input type="text" v-model="firstName"><br>
<router-link to="/step-2">Go to step 2</router-link>
</div>
</template>
<script>
import { createHelpers } from 'vuex-map-fields';
const { mapFields } = createHelpers({
getterType: [
'getKitchenField',
'getApplicantField',
],
mutationType: 'updateKitchenField',
});
export default {
computed: {
...mapFields(['no_panels', 'firstName', 'lastName'])
},
}
</script>
My store file
import kitchen from './kitchen';
import applicant from "./applicant";
export default {
modules: {
kitchen: kitchen,
applicant: applicant
},
strict: false
}
Applicant.js
import { createHelpers } from 'vuex-map-fields';
const { getApplicantField, updateApplicantField } = createHelpers({
getterType: 'getApplicantField',
mutationType: 'updateApplicantField',
});
export default {
state: {
firstName: '',
lastName: ''
},
getters: {
getApplicantField
},
mutations: {
updateApplicantField
}
}
The code above results in the following error:
Error in render: "TypeError: this.$store.getters[getterType] is not a function"
I'm the creator of vuex-map-fields, the Author asked the same question on GitHub:
Instead of passing multiple getters to createHelpers(), you can destructure and rename the return value of createHelpers() and call it twice.
const { mapFields: mapKitchenFields } = createHelpers({
getterType: 'getKitchenField',
mutationType: 'updateKitchenField',
});
const { mapFields: mapApplicantFields } = createHelpers({
getterType: 'getApplicantField',
mutationType: 'updateApplicantField',
});
export default {
computed: {
...mapKitchenFields(['no_panels']),
...mapApplicantFields(['firstName', 'lastName']),
},
}
If the desctructuring syntax is new to you, you can read more about it from Wes Bos: https://wesbos.com/destructuring-renaming/

Vue : Cant display passed data in component passed information with event bus

I have a vue application and a function that pushes a router and pass data to another router component.
passData: function () {
EventBus.$emit('passName', this.tableRow[0]);
this.$router.push('/analytic-two');
}
Then the other component.
<template>
<p>
This is Data passed from chart component {{passedRow}}
</p>
</template>
<script>
import { EventBus } from '../event-bus.js';
export default {
data() {
return {
passedRow: [
{
"name": "",
"numSongs": "",
"Year": ""
}
],
name: '',
id: '?',
};
},
created: function () {
const self = this;
EventBus.$on('passName', function (value) {
self.passedRow = value;
console.log(self.passedRow);
});
},
}
</script>
I know the data is coming through and its being logged but i can't figure out how to display it in my template does anyone have any ideas.
I would recommend to not using "=" operator instead of cleaning the reactive array and then fill it with the new values
self.passedRow.splice('deleteCount');
self.passedRow.push(...value); //pushes all the values in the value-array
This way you will at least be able see the progress.
You can also force a DOM-Update after setting the new value, but this should be unlikely in this case. To do so call in the component this.$forceUpdate()