Last active
October 9, 2019 18:23
-
-
Save uriannrima/d131db13256255d2f07d9e33e7adca63 to your computer and use it in GitHub Desktop.
Mixin factory to create components with it's own vuex store module.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div> | |
<div class="black-box rounded h-md-up"> | |
<div> | |
<label>Description</label> | |
<minimize-button :minimize.sync="minimize"></minimize-button> | |
</div> | |
</div> | |
<div class="description-component" v-show="!minimize"> | |
<div class="horizontal-container"> | |
<input type="text" class="full-width-input" :value="name"> | |
<label>Character Name</label> | |
<button @click="saveCharacter()">Save</button> | |
<button @click="newCharacter()">New</button> | |
</div> | |
<div class="horizontal-container"> | |
<input type="text" class="full-width-input" :value="playerName"> | |
<label>Player Name</label> | |
</div> | |
<div class="horizontal-container"> | |
<input type="text" class="full-width-input" :value="classes"> | |
<label>Class and Level</label> | |
</div> | |
<div class="three-part-area"> | |
<div class="horizontal-container"> | |
<input type="text" class="full-width-input" :value="race"> | |
<label>Race</label> | |
</div> | |
<div class="horizontal-container"> | |
<input type="text" class="full-width-input" :value="alignment"> | |
<label>Alignment</label> | |
</div> | |
<div class="horizontal-container"> | |
<input type="text" class="full-width-input" :value="deity"> | |
<label>Deity</label> | |
</div> | |
</div> | |
<div class="four-part-area"> | |
<div class="horizontal-container"> | |
<select :value="size.name" class="full-width-input"> | |
<option v-for="(size, index) in allSizes" :key="index" :value="size.name">{{size.name}}</option> | |
</select> | |
<label>Size</label> | |
</div> | |
<div class="horizontal-container"> | |
<input type="text" class="full-width-input" :value="age"> | |
<label>Age</label> | |
</div> | |
<div class="horizontal-container"> | |
<input type="text" class="full-width-input" :value="gender"> | |
<label>Gender</label> | |
</div> | |
<div class="horizontal-container"> | |
<input type="text" class="full-width-input" :value="height"> | |
<label>Height</label> | |
</div> | |
</div> | |
<div class="four-part-area"> | |
<div class="horizontal-container"> | |
<input type="text" class="full-width-input" :value="weight"> | |
<label>Weight</label> | |
</div> | |
<div class="horizontal-container"> | |
<input type="text" class="full-width-input" :value="eyes"> | |
<label>Eyes</label> | |
</div> | |
<div class="horizontal-container"> | |
<input type="text" class="full-width-input" :value="hair"> | |
<label>Hair</label> | |
</div> | |
<div class="horizontal-container"> | |
<input type="text" class="full-width-input" :value="skin"> | |
<label>Skin</label> | |
</div> | |
</div> | |
</div> | |
</div> | |
</template> | |
<script> | |
import SizeService from 'Services/size.service'; | |
import StoredComponentMixinFactory from 'Store/stored.component.mixin.factory'; | |
import DescriptionStore from './DescriptionStore'; | |
export default { | |
mixins: [ | |
MinimizableMixin, | |
StoredComponentMixinFactory({ | |
// Namespacing definition | |
moduleConfiguration: ['Character', 'Description'], | |
// The component store. | |
store: DescriptionStore, | |
// Map all state properties. | |
stateMapping: true, | |
// Map all getters. | |
gettersMapping: true, | |
// Map all mutations. | |
mutationsMapping: true, | |
// Map all actions. | |
actionsMapping: true, | |
// Make a commit informing the module registration. | |
commitRegistration: true | |
}) | |
], | |
data() { | |
return { | |
allSizes: [] | |
} | |
}, | |
created: async function () { | |
this.allSizes = await SizeService.getAll(); | |
}, | |
methods: { | |
saveCharacter: async function () { | |
console.log('Save it'); | |
}, | |
newCharacter: async function () { | |
console.log('Create new one'); | |
} | |
} | |
} | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default { | |
namespaced: true, | |
state: { | |
name: "Test Name", | |
playerName: "", | |
classes: [], | |
race: "", | |
alignment: "", | |
deity: "", | |
size: {}, | |
age: "", | |
gender: "", | |
height: "", | |
weight: "", | |
eyes: "", | |
hair: "", | |
skin: "" | |
}, | |
getters: { | |
classes: (state, getters) => { | |
return state.classes.map(classe => { | |
if (!classe.name || !classe.level) return ""; | |
return classe.name + " (" + classe.level + ")"; | |
}); | |
} | |
}, | |
mutations: { | |
updateClasses: (state, { classes }) => { | |
state.classes = []; | |
const classesValues = classes.split(","); | |
classesValues.forEach(classesValue => { | |
// If empty character | |
if (classesValue.trim() === "") return; | |
// Clear whitespaces | |
var classLevel = classesValue.replace(/ /g, ""); | |
// Regex to get level | |
var levelRegex = /\(([^)]+)\)/; | |
// Get level data, | |
var levelData = levelRegex.exec(classLevel); | |
// Extract level from data | |
var level = levelData && levelData.length > 1 ? levelData[1] : 1; | |
// Remove level from classes. | |
var classOnly = classLevel.replace(levelRegex, ""); | |
state.classes.push({ | |
name: classOnly.trim(), | |
level: level | |
}); | |
}); | |
} | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createNamespacedHelpers } from 'vuex'; | |
/** | |
* An mixin factory to configure a component with it's own vuex store module. | |
* @param {string[]} moduleNamespace - Array containing the "path" to the component. | |
* @param {Object} module - Component module object. | |
* @param {string[]|boolean} stateMapping - Array of state properties that you want to mix from the store to the component computed properties. True if you want all state's properties. | |
* @param {string[]|boolean} gettersMapping - Array of getters methods that you want to mix from the store to the component computed properties. True if you want all methods. | |
* @param {string[]|boolean} mutationsMapping - Array of mutations methods that you want to mix from the store to the component methods. True if you want all methods. | |
* @param {string[]|boolean} actionsMapping - Array of actions methods that you want to mix from the store to the component methods. True if you want all methods. | |
* @param {string|boolean} commitRegistration - Commit name to be executed to inform Vuex that a module has been registered. Helps to make Vue DevTools to know that a new module exists. String containing the commit name, or a boolean to use default 'ModuleRegistered'. | |
*/ | |
export default function ({ moduleNamespace, module, stateMapping, gettersMapping, mutationsMapping, actionsMapping, commitRegistration } = {}) { | |
const mixin = { | |
computed: {}, | |
methods: {}, | |
created() { | |
// Avoid register module more than once. | |
// Caveat: If using hot-reload, if you make changes on the script that contains the store | |
// Or if you create the store in the same script that you define the component, everytime you change it | |
// The component will register again the store, and it will have it's alreadyRegistered cleared out. | |
if (module.alreadyRegistered) return; | |
this.$store.registerModule(moduleNamespace, module); | |
module.alreadyRegistered = true; | |
// Since the Vuex Store won't update on Vue DevTools Store Modules until there is a commit... | |
// After the module has been registered we do a commit (could be a empty commit, but let's be a bit informative) | |
// After this, Time Travel works. | |
// Caveat: Again, if you use hot-reload, and the store loses it's information, it will register again and do a new commit. | |
if (commitRegistration) this.$store.commit((typeof commitRegistration === 'string') ? commitRegistration : 'ModuleRegistered', { moduleNamespace, module, stateMapping, gettersMapping, mutationsMapping, actionsMapping }); | |
} | |
}; | |
const namespace = (typeof moduleNamespace === 'object') ? moduleNamespace.join('/') : moduleNamespace; | |
function checkAndMerge(mappingOptions, mappingFunction, fromStore, toComponent) { | |
if (!fromStore || !toComponent) return; | |
if (mappingOptions) { | |
if (typeof mappingOptions === 'boolean') { | |
toComponent = Object.assign(toComponent, { ...mappingFunction(Object.keys(fromStore)) }); | |
} else { | |
toComponent = Object.assign(toComponent, { ...mappingFunction(mappingOptions) }); | |
} | |
} | |
}; | |
const { mapState, mapGetters, mapMutations, mapActions } = createNamespacedHelpers(namespace); | |
checkAndMerge(stateMapping, mapState, module.state, mixin.computed); | |
checkAndMerge(gettersMapping, mapGetters, module.getters, mixin.computed); | |
checkAndMerge(mutationsMapping, mapMutations, module.mutations, mixin.methods); | |
checkAndMerge(actionsMapping, mapActions, module.actions, mixin.methods); | |
return mixin; | |
}; |
- Namespaced is required for map* helper functions to work.
- Flaging the module to avoid duplicate registration.
- Checking moduleNamespace type as object (array is object and constructor array, too lazy, I'll fix it later).
- Some renaming.
- Now using createNamespacedHelpers().
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
An small mixin factory to create a mixin that dynamically register a module and map it's state/getters/mutations/actions.
Description.vue (added .js just for better visualization on gist) and DescriptionStore.js are just for reference.