Reputation: 63
I recently built a small application with Vue.js and Express.js. There are few components needed to be prepared by the servers, e.g., the combobox for Article.Category and Article.User. The options for these 2 components needed to be rendered from server. I use <component />
as the placeholder for these 2 components in the article edit form:
<component v-bind:is="user_selection_component"></component>
<component v-bind:is="category_selection_component"></component>
I use the template string for initialising the components, the template string result.data.template
is passed by server:
let org_data = original_store;
let new_data = () => {
org_data['remote_options'] = result.data.remote_options;
//if there is any default value, then assign the value to field referred by "model_name"
if(model_name && result.data.preset_value){
let previous_value = $shared.index(org_data, model_name);
if(!previous_value){
$shared.index(org_data, model_name, result.data.preset_value);
}
}
return org_data;
}
var default_cb = ()=>{console.info('['+model_name+'].default_cb()')};
let TempComponent = {
template: result.data.template,
methods: component_ref.methods,
data: new_data,
mounted: cb !== null ? cb.bind(this, org_data) : default_cb.bind(this)
};
app[mount_component] = TempComponent;
Here is the problem, the data method returns a new Observable store for the dynamically loaded components, they don't share the same store object with the parent component, which is the article edit from. Hence, if I want to modify the category field value or user field value, I have to let the callback function cb
to accept the store objects of these 2 dynamically loaded components. Otherwise, from the parent component, I could not modify the values in these 2 components.
So I came up with a temporary workaround, I passed the setter method as the callback function to these dynamically loaded functions:
let set_user_id = null;
let set_cate_id = null;
(org_store) => { set_user_id = (new_id) => { org_store.form.user_id = new_id; }}
(org_store) => { set_cate_id = (new_id) => {org_store.form.category_id = new_id; }}
After I load other components or anytime I want to set the category/user value, I can just call set_user_id($new_user_id)
or set_cate_id($new_category_id)
;
I don't like this work around at all. I tried to use the event handler to emit the new values into these 2 components. But I couldn't access these 2 dynamically loaded component via $ref
. Is there a better way to let data be shared between dynamically loaded components? Thanks.
Upvotes: 1
Views: 412
Reputation: 43881
If your components will accept props, you can localize your event bus, which is a little nicer than having a global. The parent component creates the bus as a data item:
data() {
...
bus: new Vue()
}
The components accept it as a prop:
<component v-bind:is="user_selection_component" :bus="bus"></component>
<component v-bind:is="category_selection_component" :bus="bus"></component>
and you use it as in your answer, except referring to this.bus
instead of just bus
.
Upvotes: 2
Reputation: 63
I don't think what I have now is the best solution. After consulting with other people, I took event bus as the better solution. So I modified my code as:
In my init component:
let org_data = original_store;
let new_data = () => {
org_data['remote_options'] = result.data.remote_options;
//if there is any default value, then assign the value to field referred by "model_name"
if(model_name && result.data.preset_value){
let previous_value = $shared.index(org_data, model_name);
if(!previous_value){
$shared.index(org_data, model_name, result.data.preset_value);
}
}
return org_data;
}
var default_cb = () => { console.info('['+model_name+'].default_cb()') };
let TempComponent = {
template: result.data.template,
methods: component_ref.methods,
data: new_data,
mounted: cb !== null ? cb.bind(this, org_data) : default_cb.bind(this)
};
bus.$on('set.' + model_name, function(value){
$shared.index(org_data, model_name, value);
});
The difference is here:
bus.$on('set.' + model_name, function(value){
$shared.index(org_data, model_name, value);
});
The bus
is the common event bus created by Vue()
:
let bus = new Vue()
From the parent component, I can just use this event bus to emit the event:
bus.$emit('set.form.user_id', this.form.user_id);
I do feel better after changing to this solution. But I still appreciate if there is an even better way. Thanks.
Upvotes: 1