Vue Watcher not working on component created with Vue.extend - javascript

I have a Parent component with a select input which is bound through v-model to a variable in data.
Besides, I create child components dynamically using Vue.extend, which i pass the propsData which also includes the value of the select.
This components have a watcher for the prop that is related to the select input.
When i create the component it receives the props succesfully, The problem comes when I update the value of the select input that doesn't trigger the watcher on the child component.
I've been looking for similar situations but have not found something that helps me solve this problem, i don't know why it doesn't trigger the watcher on the child component when the select input changes.
Any help would be very preciated.
Here i create the component dynamically:
let PresupuestoFormularioVue = Vue.extend(PresupuestoFormulario)
let instance = new PresupuestoFormularioVue({
propsData: {
//The prop related to select input
seguro: this.seguro,
}
})
instance.$mount()
this.$refs.formularioContenedor.appendChild(instance.$el)
And this is the watcher in the component which isn't working:
watch:{
seguro:{
handler: function( newVal ){
console.log(newVal)
},
},
},

It's not the watch that doesn't work. It's the bindings. You're assigning the current value of this.seguro, not the reactive object itself. However, a new Vue() can add this binding for you.
As a sidenote, whether PresupuestoFormulario is a Vue.extend() doesn't matter. It can be any valid VueConstructor: a Vue.extend(), Vue.component() or a valid SFC (with name and template): export default {...}.
Here's how to do it:
methods: {
addPresupuestoFormulario() {
const div = document.createElement('div');
this.$el.appendChild(div);
new Vue({
components: { PresupuestoFormulario },
render: h => h("presupuesto-formulario", {
props: {
seguro: this.seguro
}
})
}).$mount(div)
}
}
The <div> initially appended to the parent will get replaced upon mounting with the actual template of PresupuestoFormulario and the bindings will be set, exactly as if you had <presupuesto-formulario :seguro="seguro" /> in the parent template from the start.
The really cool part about it is that the parent component doesn't need to have PresupuestoFormulario declared in its components.
Here's a working example:
const Test = Vue.component('test', {
template: `<div>message: {{message}}</div>`,
props: ['message'],
watch: {
message: console.log
}
})
new Vue({
el: '#app',
data: () => ({
msg: "¯\\_(ツ)_/¯"
}),
methods: {
addComponent() {
const div = document.createElement("div");
this.$el.appendChild(div);
new Vue({
components: {
Test
},
render: h => h("test", {
props: {
message: this.msg
}
})
}).$mount(div);
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2"></script>
<div id="app">
<input v-model="msg">
<button #click="addComponent">Add dynamic child</button>
</div>
A separate note, about using this.$el.appendChild(). While this works when you're using a root Vue instance (a so-called Vue app), it will likely fail when using a normal Vue component, as Vue2 components are limited to having only 1 root element.
It's probably a good idea to have an empty container (e.g: <div ref="container" />) in the parent, and use this.$refs.container.appendChild() instead.

All of props that you want check in watcher, should be a function. If you want read more about this go to vue document codegrepper.
watch: {
// whenever seguro changes, this function will run
seguro: function (newValue, oldValue) {
console.log(newValue,oldValue)
}
}

Related

How do I use $emit in the loop to pass the array value to the parent?

The child component will pass the value to the parent component through a loop
And the parent component will do something with the value
I have a mistake, the parent component cannot receive the child component, and each passed value will be missing
child component
let array =[{a:'1',b:'2'},{a:'3',b:'4'},{a:'5',b:'6'},{a:'1',b:'2'},....]
for(let index in array){
this.$emit('update', array[index])
}
parent component
update(product) {
console.log(product);
}
In my console I can only get part of the children pass value
What is this error and how can I fix it?
This is my sample, although the error cannot be reproduced, the code is the same, except that the parent does not display all
example
Your code should work fine, I did not see any issue in your code. I am assuming you are capturing the event emitted from the child into the parent component like this.
<child #update="update"></child>
Working Demo :
Vue.component('child', {
data() {
return {
arr: [{a:'1',b:'2'}, {a:'3',b:'4'}, {a:'5',b:'6'},{a:'1',b:'2'}]
}
},
mounted() {
for(let index in this.arr){
this.$emit('update', this.arr[index])
}
}
});
var app = new Vue({
el: '#app',
methods: {
update(product) {
console.log(product);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.0/vue.js"></script>
<div id="app">
<child #update="update"></child>
</div>
Suggestion : To improve the performance, Instead of emitting each element into parent component, My suggestion would be to pass whole array and then do the data manipulations in the parent component itself.

How to get the props value and use in lifehooks cycle of Vue JS

I'm searching a way to get the props value through some lifehooks like mounted or updated and trying to save the value with my v-model with some string. But I can't get it.
Though I tried :value on the input element with the props value and some string and I was able to get it, but it seems like I can't access it without v-model, as I researched v-model and :value can't be together.
The purpose is to get the value(with from props and some string) of a input tags.
Parent Component
<invite :user_token="user_token"/>
Child Component
export default {
props: ['user_token'],
data() {
return {
link: ''
}
},
mounted() {
console.log(this.user_token);
this.link = `"http://localhost/johndoe-vue/public/#/invite${this.user_token}"`;
},
updated() {
console.log(this.user_token);
this.link = `"http://localhost/johndoe-vue/public/#/invite${this.user_token}"`;
}
}
Welcome to SO Nigel!
Are you looking for something like this, perhaps?
ParentComponent.vue
<template>
<div id="wrapper">
<invite :userToken="userToken"></invite>
</div>
</div>
<script>
import Invite from "#/Invite.vue";
export default {
components: {
Invite
},
data() {
return {
userToken: "fooBar",
};
}
}
</script>
ChildComponent.vue
<template>
<div id="wrapper">
<p v-if="inviteLink != ''">{{ inviteLink }}</p>
</div>
</template>
export default {
props: {
userToken: {
type: String,
}
},
data() {
return {
inviteLink: ""
}
},
created() {
if(this.userToken != "") {
this.inviteLink == "/your-link-here/"+this.userToken;
}
}
}
Also, you should check out the Vue.js Style Guide. They've marked multi-word component names as essential. Your Invite component should be renamed to BaseInvite or something like that.
Have you tried to $emit this.link
Props is accessible through the $props property of your component. You would reference it like: this.$props.[property name]. $props is called an instance property; there are many of them and they are each accessible this way. See https://v2.vuejs.org/v2/api/#Instance-Properties
Keep in mind that the Vue life cycle methods are somewhat inconsistent. Which instance properties are accessible depends on the method (ie: you can't reference $el in created(...).
https://v2.vuejs.org/v2/guide/instance.html#Lifecycle-Diagram

Wrapping a JS library in a Vue Component

I need to know which is best practice for wrapping a JS DOM library in Vue. As an example I will use EditorJS.
<template>
<div :id="holder"></div>
</template>
import Editor from '#editorjs/editorjs'
export default {
name: "editorjs-wrapper",
props: {
holder: {
type: String,
default: () => {return "vue-editor-js"},
required: true
}
},
data(){
return {
editor: null
};
},
methods: {
init_editor(){
this.editor = new Editor({
holder: this.holder,
tools: {
}
});
}
},
mounted(){
this.init_editor();
},
//... Destroy on destroy
}
First:
Supose I have multiple instances of <editorjs-wrapper> in the same View without a :hook then all intances would have the same id.
<div id="app">
<editorjs-wrapper></editorjs-wrapper>
<editorjs-wrapper></editorjs-wrapper>
</div>
Things get little weird since they both try to process the DOM #vue-editor-js. Would it be better if the component generated a random id as default?
Second:
EditorJS provides a save() method for retrieving its content. Which is better for the parent to be able to call save() method from the EditorJS inside the child?
I have though of two ways:
$emit and watch (Events)
// Parent
<div id="#app">
<editorjs-wrapper #save="save_method" :save="save">
</div>
// Child
...
watch: {
save: {
immediate: true,
handler(new_val, old_val){
if(new_val) this.editor.save().then(save=>this.$emit('save', save)) // By the way I have not tested it it might be that `this` scope is incorrect...
}
}
}
...
That is, the parent triggers save in the child an thus the child emits the save event after calling the method from the EditorJS.
this.$refs.childREF
This way would introduce tight coupling between parent and child components.
Third:
If I want to update the content of the child as parent I don't know how to do it, in other projects I have tried without success with v-modal for two way binding:
export default{
name: example,
props:{
content: String
},
watch:{
content: {
handler(new_val, old_val){
this.update_content(new_val);
}
}
},
data(){
return {
some_js_framework: // Initialized at mount
}
},
methods: {
update_content: function(new_val){
this.some_js_framework.update(new_val)
},
update_parent: function(new_val){
this.$emit('input', this.some_js_framework.get_content());
}
},
mounted(){
this.some_js_framework = new Some_js_framework();
this.onchange(this.update_parent);
}
}
The problem is:
Child content updated
Child emit input event
Parent updates the two-way bidding of v-model
Since the parent updated the value the child watch updates and thus onchange handler is triggered an thus 1. again.

Trigger Method in Sibling Component Vue JS

need your assistance once again. Here's my situation. VueJS2 (vue-cli project) no Vuex.
I have parent component, child1 and child2.
child1 is a form that gets data from child2(which is a table). Upon clicking on table row's checkbox in child2, we fill up the form in child1 this is done by event emitting via parent(picture 2).
child1 has a button and reset method(see picture 1) to clear its fields. My task is: when I 'uncheck' checkbox in table row child2, form in child1 has to be cleared. How do I do this, specifically, how do I access reset method in child1 from child2 and use that method from child2 basically.
I know how to pass data from child to child, but cant get my head around how to be able just manipulate child's data from its sibling.
Please help! Thank you in advance!
If I understand it correctly, you have 2 child components, and you want to tell child1 to execute a method (reset) from your child2 compoent without passing any props.
Well, in that cases your option is limited to using an event Bus.
Check this example. When you click on a button from CompB it executes a methods inside another child component: CompA
var Bus = new Vue()
var compA = {
data: () => ({ name: 'Primary' }),
template: `<p> Comp A is: {{name}}</p>`,
methods: {
reset () {
this.name = 'RESETED'
}
},
created () {
let self = this;
Bus.$on('resetA', function (payload) {
self.reset()
})
}
}
var compB = {
template: `<p> Comp B <button #click="resetCompA"> Clear A </button> </p>`,
methods: {
resetCompA () {
Bus.$emit('resetA')
}
}
}
new Vue({
el: '#app',
components: {
'comp-a' : compA,
'comp-b' : compB
}
})
<script src="https://unpkg.com/vue#2.5.9/dist/vue.js"></script>
<div id="app">
<comp-a></comp-a>
<comp-b></comp-b>
</div>

ReactJS - Assign context/owner in 13.x

I am trying to render a child element that has already been initialized and is passed through a prop. My child depends on its context, yet I don't know how to apply that context before a render. Example:
http://jsfiddle.net/5qyqceaj/1/ (code below on React 0.13.3):
var Parent = React.createClass({
childContextTypes: {
test: React.PropTypes.string
},
getChildContext: function() {
return { test: "Working :)" };
},
render: function() {
return (
<div>
<b>Initialized Globally:</b><br/> {this.props.child}
<hr/>
<b>Initialized Inline:</b><br/> <Child/>
</div>
);
}
});
var Child = React.createClass({
contextTypes: {
test: React.PropTypes.string
},
render: function() {
return <div><h1>Context: {this.context.test || "Broken"}</h1></div>;
}
});
var ChildInstance = <Child/>;
React.render(<Parent child={ChildInstance} />, document.getElementById('container'));
In the example above, <Child/> when initialized globally fails to inherit the context passed by Parent.
According to https://github.com/facebook/react/issues/3451#issuecomment-104978224, this is an open issue with React 0.13.x where context is currently assigned via the owner and by 0.14 they will have it inherited from parents.
There are 3 ways I can imagine solving this:
Find a way to assign context within a component upon render or manually switch the owner of an element.
Reconstruct elements by passing in props and tagnames
Wait for 0.14
2 is really un-maintainable for more complicated structures and 3 is the surrender, is there any ways I can achieve 1?
You can use React.cloneElement to re-assign the owner of a React element. It will return a new element. Note that you will need to pass a new ref property, otherwise the previous owner will be preserved.
In your case, you would clone this.props.child in the render method of Parent.

Categories

Resources