I'm working with a modular vue application that registers the modules at compile time. Please see the code below -
app.js
import store from './vue-components/store';
var components = {
erp_inventory: true,
erp_purchase: true,
};
// Inventory Module Components
if (components.erp_inventory) {
// erp_inventory.
store.registerModule('erp_inventory', require('./erp-inventory/vue-components/store'));
// erp_inventory/product_search_bar
store.registerModule([ 'erp_inventory', 'product_search_bar' ], require('./erp-inventory/vue-components/store/products/search-bar'));
}
./erp-inventory/vue-components/store/index.js
export default {
namespaced: true,
state() {
return {};
},
getters: {},
actions: {}
}
./erp-inventory/vue-components/store/products/search-bar/index.js
export default {
namespaced: true,
state() {
return {
supplier_id
};
},
getters: {
supplier_id: (state) => {
return state.supplier_id;
}
},
actions: {
set_supplier_id({ commit }, supplier_id) {
commit('set_supplier_id', supplier_id);
}
},
mutations: {
set_supplier_id(state, supplier_id) {
state.supplier_id = supplier_id;
}
}
}
When I use context.$store.dispatch('erp_inventory/product_search_bar/set_supplier_id', e.target.value, {root:true}); to dispatch the action in search-bar/index.js, vue is unable to find the namespace stating [vuex] unknown action type: erp_inventory/product_search_bar/set_supplier_id
I've read the documentation of vuex and dynamic modules and even though I've set namespaced: true, in each store, this problem persists. After dumping the store of my app I found that namespaced was never being set for registered modules (see image below).
Unless I'm doing something wrong, could it be a bug?
You have to use require(....).default, otherwise you won't get the default export pc fro your ES6 module file, but object by webpack that's wrapping it.
Related
I am trying to move some functionality to a vue mixin from the component, to be able to use it in multiple components.
This (simplified version of the code) works:
export default {
data() {
return {
file: {},
audioPlayer: {
sourceFile: null,
},
};
},
watch: {
'audioPlayer.SourceFile': function (nextFile) {
console.log('new sourceFile');
this.$data.file = nextFile;
},
}
}
But if I move the audioPlayer data object to a mixin, the watch does no longer fire.
Is this expected behavior?
N.b. I resolved this by directly making the 'file' data property into a computed value, which works in this particular case, but the behavior is still strange.
You need a lowercase s. sourceFile not SourceFile
watch: {
'audioPlayer.sourceFile': function (nextFile) {
console.log('new sourceFile');
this.$data.file = nextFile;
},
}
8.4 of react-admin. I've been trying to implement a custom action that connects with the custom reducer but so far nothing has worked.
I've Implemented this part of the guide in the official documentation for the action side https://marmelab.com/react-admin/doc/3.8/Actions.html#querying-the-api-with-fetch and this for the reducer https://marmelab.com/react-admin/doc/3.8/Admin.html#customreducers. The problem stems from that I can only use useUpdate method which sends update request, instead of a get without connecting to the reducer and there is no clear explanation of how I can chain those two things together. I also tried using an older way of dispatching actions, but still didn't work. Please help I've been trying this for 2 weeks now. Nothing gets updates and the redux store stays the same.
component
const { data, loading, error } = useQueryWithStore({
type: 'getList',
resource: 'goals',
action: "GET_USER_GOALS",
payload: { pagination: { page: 1, perPage: 10 }, sort: { field: "a-z", order: "ABC" }, filter: {} }
});
reducer
export default (previousState = 0, { type, payload }) => {
console.log(type)
if (type === 'GET_USER_GOALS') {
return payload.rate;
}
return previousState;
}
I even wrote a custom action
but it says that "Cannot read property 'update' of undefined" which isn't supported in the newer version I guess.
import { UPDATE } from 'react-admin';
export const UPDATE_PAGE = 'GET_USER_GOALS';
export const setGoals = (id, data) => {
return {
type: UPDATE_PAGE,
payload: { id, data: { ...data, is_updated: true } },
meta: { fetch: UPDATE, resource: 'goals' },
}
};
admin
<Admin
locale="en"
customReducers={{ userGoals: userGaolsReducer }}
loginPage={LoginPage}
authProvider={authProvider}
dataProvider={testProvider}
i18nProvider={i18nProvider}
history={history}
dashboard={Dashboard}
customSagas={[userGoalsSaga]}
>
I had to include it in the store.js as well
const reducer = combineReducers({
admin: adminReducer,
router: connectRouter(history),
userDashboardSettings: userGaolsReducer
});
I have setup hot reloading and dynamic loading of my vuex modules.
store.js file - hot update section
if (module.hot) {
// accept actions and mutations as hot modulesLoader
module.hot.accept([
'./store/modules/index.js',
'./store/helpers/global-actions',
'./store/helpers/global-mutations',
...modulePaths,
// './store/helpers/global-actions',
], () => {
let newModules = require('./store/modules').modules
store.hotUpdate({
actions: require('./store/helpers/global-actions'),
mutations: require('./store/helpers/global-mutations'),
modules: newModules,
})
})
}
modules/index.js file
const requireModule = require.context('.', true, /index.js$/)
const modules = {}
const modulePaths = []
requireModule.keys().forEach(fileName => {
if (fileName === './index.js') {
modulePaths.push(fileName.replace('./', './store/modules/'))
return
} else {
let moduleName = fileName.match(/(?<=\/)\w*(?=\/)/g)[0]
modulePaths.push(fileName.replace('./', './store/modules/'))
modules[moduleName] =
{
namespaced: false,
...requireModule(fileName),
}
}
})
export {modulePaths, modules}
Basically what this code does is loading folders with index.js file as modules (where module name is foldername) dynamically.
If I update module actions or getters or mutations everything works as expected I do get new actions added to store as well as mutations, when either of modules is updated.
The only thing I can't get to work is to get modules state changed on update. So if I change modules state it does not get reflected. Is it a normal behaviour? Or am I doing something wrong?
I have a mobile webview that is injected with some global config object:
Vue.prototype.$configServer = {
MODE: "DEBUG",
isMobile: false,
injected: false,
version: -1,
title:"App",
user: null,
host: "http://127.0.0.1:8080"
}
Later the WebView inject this:
Vue.prototype.$configServer = {
MODE: "DEBUG",
title: "App",
version: "2.0",
isMobile: true,
injected: true,
host: "http://127.0.0.1:8081"
}
And try to use it for the component:
const HomePage = {
key: 'HomePage',
template: '#HomePage',
components: { toolbar },
data() {
return {
items: [
{name:"Login", link:"login"},
]
}
},
computed:{
config() {
return Vue.prototype.$configServer
}
},
};
However the page is not updated. How react to the change?
P.D: I confirm the object is updated with the safari debug tools. Also test in a local html.
Instead of putting the config into the prototype of Vue, you can actually add it as a data option inside the main vue instance which will guarantee you that all your config properties will be reactive. As mentioned in the docs
When you pass a plain JavaScript object to a Vue instance as its data option, Vue will walk through all of its properties and convert them to getter/setters using Object.defineProperty.
Having said that whenever you update your config properties, vue will react to it.
Now let's see how to do it in code:
new Vue(){
data:{ //only place where data is not a function
configServer = {
MODE: "DEBUG",
isMobile: false,
injected: false,
version: -1,
title:"App",
user: null,
host: "http://127.0.0.1:8080"
}
}
}
Now whenever you need to access your config you can directly access it from any component using $root. Like this.$root.configServer.
Well that's it.
I hope it helps.
There are 3 ways to acheive what you want
1- Make sure you import vue in your component
import 'Vue' from vue
...
...
...
computed:{
config() {
return Vue.prototype.$configServer
}
2- If you don't want to import vue the you can directly access prototype using proto from any instance.
computed:{
config() {
return this.__proto__.$configServer
}
3- As you have added the config in the prototype you can actually access is directly from the vue instance using this
computed:{
config() {
return this.$configServer
}
},
Well whatever style matches yours you can choose that.
But I would personally recommend using the 3rd one, because accessing the prototype of instance is sort of an anti-pattern.
I hope it helps.
i have a service to manage all the errors and alerts in my app. and the code looks like this
Service
import Ember from 'ember';
export default Ember.Service.extend({
messages: null,
init() {
this._super(...arguments);
this.set('messages', []);
},
add: function (severity, msg, messageType) {
if (severity === 'error') {severity = 'danger';}
var msgObject ={
severity: severity,
messageType: messageType,
msg: msg,
msgId: new Date()
};
this.get('messages').pushObject(msgObject);
},
remove(msgId) {
this.get('messages').removeObject(msgId);
},
empty() {
this.get('messages').clear();
}
});
Component
import Ember from 'ember';
export default Ember.Component.extend({
messageType:'global',
messageHandler: Ember.inject.service(),
messages: function(){
return this.get('messageHandler.messages').filterBy('messageType',this.get('messageType'));
}.property('messageHandler.messages'),
actions : {
dismissAllAlerts: function(){
this.get('messageHandler').empty();
},
dismissAlert: function(msgId){
this.get('messageHandler').remove(msgId);
}
}
});
Initializer
export function initialize(container, application) {
application.inject('component', 'messageHandler', 'service:message-handler');
}
export default {
name: 'message-handler',
initialize : initialize
};
Template
import Ember from 'ember';
export default Ember.Component.extend({
messageType:'global',
messageHandler: Ember.inject.service(),
messages: function(){
return this.get('messageHandler.messages');
}.property('messageHandler.messages'),
actions : {
dismissAllAlerts: function(){
this.get('messageHandler').empty();
},
dismissAlert: function(msgId){
this.get('messageHandler').remove(msgId);
}
}
});
and whenever there is an error i will add it like this
this.get('messageHandler').add('error',"Unable to get ossoi details","global");
my problem is the filterBy in the component is not working. if i remove the filterBy() it works and i can see the error in the template. am kinda new to ember so if anyone can help me figure out what am missing here or if there is a better way of doing this please let me know
filterBy usage is good and it should be working well. but messages computed property will not be recomputed whenever you add/remove item from messageHandler.messages.
messages: Ember.computed('messageHandler.messages.[]', function() {
return this.get('messageHandler.messages').filterBy('messageType', this.get('messageType'));
}),
In the above code I used messageHandler.messages.[] as dependant key for the messages computed property so that it will be called for add/remove items.
Refer:https://guides.emberjs.com/v2.13.0/object-model/computed-properties-and-aggregate-data/
Computed properties dependent on an array using the [] key will only
update if items are added to or removed from the array, or if the
array property is set to a different array.