Vuejs v-for re-build when update vuex object - html

I am new to vuejs I want re-build my v-for loop after update vuex object. see following example code.
<div class="row search-result-row" v-for="report in reports" :key="report">
<p>{{ report.description }}</p>
</div>
here is my vuex object called globalReports. when I equal globalReports to reports it doesn't work.
computed: {
updateReports: function() {
return this.reports = this.$store.state.globalReports;
}
},
How can I do this without page reload?

Try with {{updateReports}}. Computed will not be executed util it is monitored or called, just called {{updateReports}}
<div class="row search-result-row" v-for="report in reports" :key="report">
<p>{{ report.description }}</p>
</div>
{{updateReports}}
And don't return anything just update/Assign value of this.reports
computed: {
updateReports: function() {
this.reports = this.$store.state.globalReports;
}
},

by using mapState, you can map the value of globalReports to reports automatically.
Everytime globalReports change, reports gets updated automatically, and the rebuild will happen automatically when it gets updated.
<script>
import { mapState } from "vuex";
export default {
computed: mapState({
reports: "globalReports"
})
};
</script>

Vuex is reactive so when you update the state,this change will affect all components where you use the state properties.To be more specific:
I will show you an example:
//VUEX STORE
state: {
property1
},
getters: {
getProperty1(state) {
return state.property1
}
},
mutations: {
setProperty1(state, payload) {
state.property1 = payload
}
},
actions: {
changeProperty1({commit}, payload) {
commit('setProperty1', payload)
}
}
And below is the component in which you interact with state
<template>
<p>this is from state of store {{ getProperty1 }</p>
<input type="text" v-model="value">
<button #click="changeState">Sumbit</button>
</template>
<script>
export default {
data() {
return {
value: ''
}
},
computed: {
getProperty1() {
return this.$store.getters.getProperty1
}
},
methods: {
changeState() {
this.$store.dispatch('changeProperty1', this.value)
}
}
}
</script>
Getters are to get the state properties
Mutations to change the state properties
Actions to perform async code and then to call mutations to change the state
For more please visit vuex docs

Related

Nuxt is returning html instead of JSON that I got from the api

I have a simple api returning a json object that I want to display unto the screen.
However when I display the $data property I'm getting html and not the json from the api.
However whenever I refresh the page with f5 or manually the data then shows up on the screen and not the html.
{
"data": {
"positions": 2,
"departments": 2,
"paygrades": 8
}
}
<template>
<v-container>
<v-row> {{ $data.dbData }}</v-row>
</v-container>
</template>
<script>
export default {
async asyncData({ $axios }) {
const dbData = await $axios.$get('/dashboard')
return dbData
},
data() {
return {
}
},
}
</script>
Edited code
<template>
<v-container>
<v-row> {{ dbData }}</v-row>
</v-container>
</template>
<script>
export default {
data() {
return {
dbData: null,
}
},
async fetch() {
this.dbData = await this.$axios.$get('/dashboard')
},
}
</script>

Vue.js: Dynamically render html in child component's slot

In my vue.js application, I am trying to render the html content dynamically in child component's slot. If I enter the text only then there is no issue, it works with fine.
Here is my code:
ChildComponent.vue
<template>
<div :class="type" class="message" v-if="type">
<slot />
</div>
</template>
<script>
export default {
...
props: ['type'],
...
}
</script>
ParentComponent.vue
<script>
import Alert from '#/Alert';
export default {
components: {
Alert,
},
data() {
return {
...
alert: {
error: '',
message: '',
}
};
},
created () {
this._onLoad()
},
methods: {
_onLoad() {
axios.get(`api.call.here`).then((res) => {
...
}).catch(error => {
this.alert.error = error.type;
this.alert.message = `<p>message here</p>`; // from response
});
},
}
}
</script>
Here is the screenshot of the issue:
What you trying to do can be achieved with v-html. You can use it while calling your ChildComponent.vue. This is a small example for your case:
<Alert>
<span v-html="alert.message"></span>
</Alert>

Import external component and loose reactivity in vue3

I have 2 projects/folders (with Lerna on the root).
The first one is uicomponents with some components and the second one is testing a simple app which uses some component from uicomponents.
I created a simple counter component (Counter.vue) :
<template>
<div>
<h3>Total clicks: {{ count }}</h3>
<div class="button-container">
<button class="inc" #click.prevent="increment">Add</button>
<button class="dec" #click.prevent="decrement">Subtract</button>
</div>
</div>
</template>
<script>
import { defineComponent, ref } from 'vue';
export default defineComponent({
name: 'Counter',
props: {
startingNumber: {
type: Number,
required: false,
default: 0,
},
},
setup(props) {
const count = ref(props.startingNumber);
const increment = () => {
count.value += 1;
alert(count.value);
};
const decrement = () => {
count.value -= 1;
};
return {
count,
increment,
decrement,
};
},
});
</script>
And I import it in my app on a simple page :
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<counter :starting-number="5"></counter>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import Counter from '#uicomponents/counter';
export default defineComponent({
name: 'HelloWorld',
components: {
Counter,
},
props: {
msg: {
type: String,
required: false,
default: 'Me',
},
},
});
</script>
Lerna correctly replace all my components path and I retrieve my components Counter in my pages with all HTML. Buttons works well and my alert are displays with the correct value BUT my html are not refreshed.
This text <h3>Total clicks: {{ count }}</h3> stay "Total clicks: 0". My "count" ref is well updated because the alert displayed it correct but not in html.
I have a similar problem with lost reactivity. My setup is a bit different, but in the end it's the same result.
I'm trying to build a small plugin system which loads external components
Roughly I try to do this
// pluginSystem.js is accessible through window.myps
// ...
init(app) {
vueApp = app;
},
// ...
loadPlugin(data) {
vueApp.component(data.component.name, data.component);
}
And my external component looks like this
// main.js
import Counter from './components/Counter.vue';
window.myps.loadPlugin({
component: Counter,
});
Button click in counter, etc. works, console logging is fine as well, but component data is not updated.
I also tried defineComponent and defineAsyncComponent, but as you I had no luck with it...
Try it
import { defineAsyncComponent, defineComponent } from "vue"
components: {
Counter:defineAsyncComponent(() => import("#uicomponents/counter"))
}

How to let one component correspond with the other for a specific function

I've got a component where I click a color of a machine, when I change colors, the machine gets loaded with a different color inside a image carousel.
Now I also created a component in the bottom with a image gallery of the same machine. How can I make it that the image gallery also changes color when I click the color button in the top of the page?
Important notice: The two components are not in the same parent component but they do load in the same machine images already, so the methods are not wrong I believe.
this is the clickable color button:
<li
v-for="(color, index) in machine.content[0].machine_colors"
:key="color.color_slug"
v-if="color.inStock"
v-on:click="selectColor(index)"
v-bind:class="{ active: (color.color_slug === selectedColor.color_slug)}">
<img v-bind:src="color.color_dash">
</li>
this is the component that changes color:
<div class="product__carousel">
<Carousel showIcon v-if="selectedColor" :machineColor="selectedColor"/> <!-- Image carousel gets loaded in -->
</div>
and the component that needs to change color but does not:
<div id="tab-two-panel" class="panel">
<footerGallery v-if="selectedColor && machine" :machineColor="selectedColor"/>
</div>
Heres the script of the partent component:
export default {
name: 'aboutMachine',
components: {
Collapse,
footerGallery,
},
data() {
return{
selectedColor: this.getMachineColorContent(),
}
},
props: {
main: {
default () {
return {};
},
},
machine: {
default () {
return {};
},
},
},
methods: {
getMachineColorContent() {
if (this.selectedColor) {
return null;
}
return this.machine.content[0].machine_colors[0];
},
selectColor(index) {
this.selectedColor = this.machine.content[0].machine_colors[index];
},
},
}
and the component itself:
export default {
name: 'footerGallery',
props: {
showIcon: Boolean,
machineColor: {
default () {
return {};
},
},
},
data() {
return {
highLightedThumbIndex: 0,
isActive: undefined,
};
},
created() {
this.highLightedThumbIndex = this.highLightedThumbIndex || 0;
},
methods: {
selectThumb(index) {
this.highLightedThumbIndex = index;
},
},
};
This is my main.js
import Vue from 'vue';
import VueYouTubeEmbed from 'vue-youtube-embed'
import FontAwesome from './libs/fa';
import App from './App';
const eventHub = new Vue();
Vue.use(VueYouTubeEmbed);
Vue.component('font-awesome-icon', FontAwesome);
Vue.config.productionTip = false;
/* eslint-disable no-new */
new Vue({
el: '#app',
components: { App },
template: '<App/>',
});
I would use events to accomplish this. The migration guide to Vue2 has a good short explanation of how to do simple event routing without using a full Vuex solution. In your case, you would declare a global event hub in one of your js files:
var eventHub = new Vue();
In your selectColor method you would emit the index selected:
selectColor(index) {
this.selectedColor = this.machine.content[0].machine_colors[index];
eventHub.$emit("select-color",index);
}
And in the footer, you would register a listener for the select-color event that calls selectThumb with the payload of the event (which is the selected index):
created() {
this.highLightedThumbIndex = this.highLightedThumbIndex || 0;
eventHub.$on("select-color",this.selectThumb);
}

v-on:click in component not working

A vue.js component creates a button which should call a function, but the function is never called and the v-on:click is not visible in Chrome's element inspect. My html goes like this:
<bottomcontent></bottomcontent>
And my Vue is like this:
var bottomcontent = {
template: '<div class="bottomcontent"><div class="moreresults" v-on:click="appendcontent">More Results</div></div>'
}
new Vue({
el : 'body',
data : {
homepage:{
numberofdivs: 60
}
},
methods : {
appendcontent: function() {
homepage.numberofdivs += 60
}
},
components: {
'bottomcontent': bottomcontent
}
})
The problem is that methods has to use funcions, not objects.
methods: {
appendcontent: function() {
homepage.numberofdivs += 60
}
}
You also have to correct your markup accordingly.
var bottomcontent = {
template: '<div class="bottomcontent"> <div class="moreresults" v-on:click="appendcontent"> More Results </div></div>'
}
There are some problems lead to the crack.
In the function appendcontent,you should call the data "this.homepage.numberofdivs".
and the correct demo is posted on https://jsfiddle.net/atsknydr/
methods : {
appendcontent: function() {
this.homepage.numberofdivs += 60;
console.log(this.homepage.numberofdivs);
}
}
First, as the warning says:
[Vue warn]: Do not mount Vue to <html> or <body> - mount to normal elements instead.
So, you should create an element like <div id="app"></div> to mount your app instead of <body>.
The problem you are facing is a scope problem. You are trying to call a method from the inside a component scope, that is why it's not finding the method.
Take a look at the docs to understand better.
So, in order to make this work you should change the method from the app scope to the template scope.
Your html:
<body>
<div id="app">
<bottomcontent></bottomcontent>
</div>
</body>
Your js:
<script>
var bottomcontent = {
template: '<div class="bottomcontent"><div class="moreresults" v-on:click="appendcontent">More Results</div></div>',
data: function () {
return {
homepage: {
numberofdivs: 60
}
}
},
methods: {
appendcontent: function () {
console.log('Called method!');
this.homepage.numberofdivs += 60
}
}
}
new Vue({
el: '#app',
components: {
'bottomcontent': bottomcontent
}
})
</script>