How can I reload a vue component? - vue.js

I know the solution is update the prop data like this :
this.selectedContinent = ""
But I want to use another solution
After I read some reference, the solution is :
this.$forceUpdate()
I try it, but it does not work
Demo and full code like this :
https://jsfiddle.net/Lgxrcc5p/13/
You can click button test to try it
I need a solution other than update the property data

You have to assign a key to your component. When you want to re-render a component, you just update the key.
More info here.
<template>
<component-to-re-render :key="componentKey" />
</template>
export default {
data() {
return {
componentKey: 0
}
},
methods: {
forceRerender() {
this.componentKey += 1
}
}
}

I use v-if to render the component:
<div id="app">
<button type="button" #click="updateComponent">test</button>
<test-el v-if="show"></test-el>
</div>
demo

You can use Object.assign to assign initial data properties.
Instead of this.$forceUpdate()
You should use Object.assign(this.$data,this.$options.data.call(this)).
Here we using this.$options.data get original data and assigning this values to this.$data.
See the updated fiddle Link.
Update:
Another method: Link

I thought I needed to reload, but I was able to move everything inside the mounted hook to an init method. The mounted hook calls the init method, and if you need the parent to reload everything it can use a ref to call this.$refs.componentName.init();.

Simplest one
For your data in object use your key as JSON.stringify(data)
<component :key="JSON.stringify(data)" :data="data" />
just update your state
$vm.data={
name:'balaji'
}
it automaticly update your component

On vuejs3 :
<template>
<render-component :key="count" />
<span #click="renderComponent">Click to reload render-component</span>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
const renderComponent = () => {
count.value++
}
return {
count,
renderComponent
}
}
}
</script>
see gist

I also face the same issue. I used location.reload() for reloading the component.
This may be not the right way to solve this problem. But this solved my problem.

Related

Vue Rendering of component based on VUEX getter

I have this code to render my main screen. I want to render the components based on the value I get from vuex store.
All is good. The problem the component does not reload when the mutation happens. Any tips, please?
<template>
<component v-bind:is = "showDashboard" > </component>
</template>
<script>
import AllOrganizationDashboard from "../components/AllOrganizationDashboard"
import OrganizationDashboard from "../components/OrganizationDashboard"
import ProjectDashboard from "../components/ProjectDashboard"
export default {
components: {
AllOrganizationDashboard,OrganizationDashboard,ProjectDashboard,
},
data(){
return {
showDashboard: this.$store.getters.getcurrentDashboardToShow,
}
}
}
I hope there is an easy way and that I do not need to re-structure my componenets.
You have to use computed instead of data, because data is executed only once when the component is created. This should work
computed: {
showDashboard() {
return this.$store.getters.getcurrentDashboardToShow
}
}
More about computed

(VueJS) Update component whenever displayed

I want a way to run a function (which talks to the backend) whenever a component is re-displayed.
I understand that the mounted hook will fire if the component is re-added to the DOM by a v-if directive. But, if the component is hidden and re-shown via a v-show directive, this will not fire. I need to update the component regardless of what directive is in control of it's visibility.
I looked at the updated hook but this seems to not be the indented use case.
How do I run a function whenever a component is displayed (not only for the first time)?
updated fires whenever data passed to your component changes. Therefore it will work if you pass in whatever condition controls your v-show, as a prop.
Generic example:
Vue.config.devtools = false;
Vue.config.productionTip = false;
Vue.component('child', {
props: {
shown: {
type: Boolean,
default: true
}
},
template: '<div>{{shown}}</div>',
mounted() {
console.log('child mounted');
},
updated() {
// runs whenever any prop changes
// (optional condition) only run when component is shown
if (this.shown) {
console.log('child updated');
}
}
});
new Vue({
el: '#app',
data: () => ({
showChild: true
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<label><input type="checkbox" v-model="showChild" /> Show child</label>
<child v-show="showChild" :shown="showChild" />
</div>
Now updated hook works properly, because it fires everytime :shown changes its value, which maps precisely on your show/hide logic.
maybe you can achieve it in two ways
1.use :key
whenever you want to rerender your component whether it is shown, change the value of key can rerender it.
<template>
<h1 :key="key">Text</h1>
</template>
<script>
export default{
data(){
return {
key:this.getRandomString()
}
},
methods(){
getRandomString(length = 32) {
  let chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
  let max_pos = chars.length;
  let random_string = '';
  for (let i = 0; i < length; i++) {
    random_string += chars.charAt(Math.floor(Math.random() * max_pos));
  }
  return random_string;
},
yourMethod(){
// communicate with backend
let data = await axios.get(...);
this.key = this.getRandomString();
}
}
}
</script>
use vm.$forceUpdate()
...
yourMethod(){
// communicate with backend
let data = await axios.get(...);
this.$forceUpdate();
}
...
you could implement this in a couple of ways. However since you would like to got the v-show way, here is how I would suggest you go about it.
v-show (v-show, watcher):
The v-show is definitely dependent on a variable (data, or computed). Create a watcher, to watch that data/computed property change. Depending on the value of the data/computed property, execute whatever function you intend to on the watcher.

Vue - conditionally pass down props

I'd like to know if there is a good way to conditionally pass down one or more props to a child component.
If there is sometimes an id set, then I want to pass it down. I can't set id to null because a prop must have a value. I have solved it before by using a "v-if" like this:
<survey-wrapper v-if="id" :inputJson="inputJson" :id="id"></survey-wrapper>
<survey-wrapper v-else :inputJson="inputJson"></survey-wrapper> // <-- no id
But it's a workaround which looks bad and it becomes a lot of code if the component has many props. And what if you have two props that may be set or not?
You can use v-bind and pass it and object containing all your props. and conditionally add your id prop like this.
<survey-wrapper v-bind="{ inputJson, ...(id ? { id } : {}) }"></survey-wrapper>
You can do it using v-bind and dynamically creating the list of props with a method.
Here is an example:
<template>
<div id="app">
<Component v-bind="propsToPass()"/>
</div>
</template>
<script>
export default {
name: "App",
components: {
Component
},
data() {
return {
passId: true
}
},
methods: {
propsToPass() {
const result = {};
if (this.passId) {
result.id = 1;
}
return result
}
}
};
</script>
In the above example, the prop id will only be passed if passId is true.
I think the best way to do so is by using v-bind with a logical operator && :
<survey-wrapper :inputJson="inputJson" v-bind="id && {id}" />
note that it will only pass the prop if (id) is available in such case id should not be required as a prop by the component.
Thanks

Vue: render <script> tag inside a variable (data string)

I'm new to Vue.js
I want to render a script tag inside a variable (data string).
I tried to us a v-html directive to do so, but it doesn't work Nothing is rendered
Any way I can achieve this?
I'd place a v-if directive on the script tag and put the content of it in a variable.
<script v-if="script">
{{script}}
</scrip>
If I understand you correctly, my answer is:
<template>
<div>
{{ strWithScriptTag }}
</div>
</template>
<script>
export default {
name: 'Example',
methods: {
htmlDecode(input) {
const e = document.createElement('div')
e.innerHTML = input
return e.childNodes[0].nodeValue
},
},
computed: {
strWithScriptTag() {
const scriptStr = '<script>https://some.domain.namet</script>'
return this.htmlDecode(scriptStr)
}
},
}
</script>
I think that by safety vue is escaping your <script> automatically and there is no way to avoid this.
Anyway, one thing you can do is eval(this.property) on created() lifecycle hook.
data: {
script: 'alert("this alert will be shown when the component is created")'
},
created() {
eval(this.script)
}
Use it with caution, as stated in vue js docs, this may open XSS attacks in your app

How to set keyup on whole page in Vue.js

Is it possible to set a v-on:keyup.enter on the whole page, not only for an input element in javascript framework Vue.js ?
Perhaps a better way to do this is with a Vue component. This would allow you to control when you listen to events by including or not including the component. Then you could attach event listeners to Nuxt using the no-ssr component.
Here is how you create the component:
<template>
<div></div>
</template>
<script>
export default {
created() {
const component = this;
this.handler = function (e) {
component.$emit('keyup', e);
}
window.addEventListener('keyup', this.handler);
},
beforeDestroy() {
window.removeEventListener('keyup', this.handler);
}
}
</script>
<style lang="stylus" scoped>
div {
display: none;
}
</style>
Then on the page you want to use that component you'd add this HTML:
<keyboard-events v-on:keyup="keyboardEvent"></keyboard-events>
And then you'll have to add your event handler method:
methods: {
keyboardEvent (e) {
if (e.which === 13) {
// run your code
}
}
}
Short answer is yes, but how depends on your context. If you are using vue-router as I am on a current project, you would want to add to the outer-most element you want that applied to. In my case I'm using the actual app.vue entry point's initial div element.
There is one catch that I believe is a hard requirement, the element has to be within the potentially focusable elements. The way I'm dealing with that is setting a -1 tabindex and just declaring my super-hotkeys (mostly for debug purposes right now) on the parent element in my app.
<template>
<div
id="app-main"
tabindex="-1"
#keyup.enter="doSomething"
>
<everything-else></everything-else>
</div>
</template>
EDIT:
As a side note, I also added a touch of additional configuration to my vue-router to make sure the right element is focused when I transition pages. This allows the pageup/pagedown scrolling to already be in the right section based on the content area being the only scrollable section. You'd also have to add the tabindex="-1" to the app-content element as well.
router.afterEach(function (transition) {
document.getElementById('app-content').focus();
});
and the basis of my app-content component:
<template>
<div id="app-content" tabindex="-1">
<router-view
id="app-view"
transition="app-view__transition"
transition-mode="out-in"
></router-view>
</div>
</template>
I created a small npm module that takes care of global keypress events in Vue, hope it makes someone's life easier:
https://github.com/lupas/vue-keypress
My simplest approach:
Add into your root Vue component (or any other component):
new Vue({
//...
created() {
window.addEventListener('keypress', this.onKeyPress);
},
beforeDestroy() {
window.removeEventListener('keypress', this.onKeyPress);
},
methods: {
onKeyPress(e) {
console.log('KEYPRESS EVENT', e)
//... your code
}
}
//...
})
In Vue 3 composition API, you can do it with a composable:
import { onMounted, onUnmounted } from "vue";
export function useKeyupEvent(handler) {
onMounted(() => document.addEventListener("keyup", handler));
onUnmounted(() => document.removeEventListener("keyup", handler));
}
and then in your component setup:
useKeyupEvent( event => console.log(event))