Storing Objects in Vue Templates - vue.js

I create an object from a class in the mounted event. I need to use this object throughout my component.
I've been using a data field to store the object (its not a JSON object, its a full on instantiated class).
Where is the best place to store this object? So I can use it throughout my component?

Based on your comment, it sounds like you're looking for ways to declare non-reactive data, scoped to each component instance.
Option 1: Use an attached property
Assign this.VARNAME = VALUE
Typically done in the created() hook, but can be just about anywhere in the component context
IntelliSense in IDEs may have trouble discovering this property (TypeScript will require type assertions/declarations)
Example:
export default {
created() {
this.myNonReactiveProp = new MyClass()
}
}
Option 2: Use a data property with Object.freeze()
Object.freeze() prevents the property from being reactive, but also make its completely readonly
Can be useful for static data
IntelliSense can detect this property (as it does for all data() properties)
Example:
export default {
data() {
return {
myNonReactiveProp: Object.freeze(new MyClass())
}
}
}

You can pass class between vue components via plugin:
https://v2.vuejs.org/v2/guide/plugins.html
Install your plugin globally, you should call the class like this, for example:
class myClass {
// ...
}
Vue.prototype.$myClass = new myClass;
OR in the component only:
<script>
class test {
}
export default {
data: () => ({
instance: new test
}),
mounted() {
console.log(this.instance)
}
}
</script>

Related

How to achieve clean interface for a Vue mixin with methods that dependent on a field that can be in data, props or computed

I have this mixin:
export default {
computed: {
headerValues: function () {
return this.headers.map(header => header.value);
}
},
methods: {
getTextAlignment(headerValue) {
const header = this.headers.find(header => header.value === headerValue);
return header.align
? `text-${header.align}`
: '';
},
}
};
The method and the computed property depend on a field called headers. Now I have two components that use that mixin, but in one of them, headers is a prop and in the other, a computed property.
I would like to, somehow, make this mixin self-contained, so its methods doesn't depend on a field that the user may forget to declare. This is somewhat a diffuse question, but what I want to achieve is a mixin with a well-defined interface, that doesn't have it's functionality splitted between those components that import it and itself. Any ideas?

Call original VueJS mixin method from overridden implementation

OK, i have a web page with some steps for the user and a mixin that handle those steps properties and logic, like current/next step value, checks to enable user advancing from each step to the next, etc.
Now i have to add a new functionality to be executed only after a certain step, so what i would like to be able to do is to call the original mixin method that is called everytime the user advance to the next step to add this functionality.
To be more clear, the mixin is (obviously) used in many pages of the webapp, so i would like to override the culprit mixin method in the component extending it, call the original mixin method to reuse its logic and then call oher methods for the new functionality.
Is it possibile?
sure you can, try it so:
// MyMixin.js
export default {
methods: {
myMethod() {
//...
}
}
}
// Component.vue
<script>
import MyMixin from 'path-to-mixins/MyMixin'
export default {
//...
mixins: [MyMixin],
methods: {
// there is overrinding you mixin method called myMethod
myMethod() {
// then some logic before to use your mixin method
// and call your mixin method as below
MyMixin.methods.myMethod()
}
}
//...
}
</script>
The previous answer does not work, in many cases...
you can directly access the function from the import but you need to bind the scope to this so the function uses your component data and/or other overriden functions
// Mixin.js
export default {
methods: {
aMethod() {
//...
}
}
}
// Component.vue
import Mixin from './Mixin'
export default {
//...
mixins: [Mixin],
methods: {
aMethod() {
// you need to bind the method to `this` when running so that the mixin uses your component data.
Mixin.methods.aMethod.call(this);
}
}
//...
}
if the mixin you are importing is already a vue constructor, this can happend if you used Vue.extend to create the mixin, in this case you need to use the options to access your method
You can access the mixin function using the options of the mixin
// Mixin.js
export default Vue.extend({
extends: OtherComponent
methods: {
aMethod() {
//...
}
}
})
// Component.vue
import Mixin from './Mixin'
export default {
//...
mixins: [Mixin],
methods: {
aMethod() {
// you need to bind the method to `this` when running so that the mixin uses your component data.
Mixin.options.methods.aMethod.call(this);
}
}
//...
}

Vue props with #emit

I am not using a prop with #emit in the correct way but I don't know how to fix it. I need to know a non-global registration way of doing this correctly (I am completely new to Vue by the way)..
Here is my html in file parent.vue:
<deleteLayoutDialog v-if"showDeleteLayoutDialog"
:layout-name="dialogNameToDelete
#confirm="removeLayout(layout-name)"
#cancel="setShowDeleteLayoutDialog(false)"/>
Here is a file child.vue where deleteLayoutDialog's prop and #emit are defined:
// html
// <script>
// import { //sth } from 'files'
// #Component({ // components })
export default class DeleteLayoutDialog extends Vue {
#Prop({required: true, type: String})
public readonly layoutName: string;
#Emit()
public confirm(layoutName: string) {
// do something
}
}
</script>
Here is my javascript (inside parent.vue) where columnLayoutName appears to have a NaN value (in chrome dev tool)
public removeLayout(columnLayoutName: string) {
if (this.columnLayout.name === columnLayoutName) {
// (this.columnLayout is defined somewhere in this code)
// do something...
}
}
How should I change my html and removeLayout so that I am using prop properly? Thank you.
You are calling removeLayout instead of passing it as an argument.
#confirm="removeLayout(layout-name)"
You are trying to do a subtraction between layout and name, which are probably undefined, so you get NaN.
Just pass the reference
#confirm="removeLayout"

Vue make ES6 class object reactive

I have a ES6 class that I use to hold and manage datas:
import DataManager from "./components/data-manager.js";
export default {
...
data() {
return {
dataModel: null
}
},
beforeMount() {
this.dataModel = new DataManager();
},
computed: {
datas() {
return this.dataModel.myProperty
}
},
...
}
But it appears that there is a reactivity issue and any changes made into that dataModel does not trigger the re-rendering of the view.
How can I make the properties of my class reactive ?
Thanks,
Edit: #acdcjunior The Class looks like this, but I realize that some mutation occurs... would that be the issue ? Anyway if thats not good practice to make a whole ES6+ class reactive, I'll get ride of that and go for a plain Object, or even Vuex store. Any simple suggestion ?
class DataManager {
constructor() {
this.professionalsList = [];
this.eventsList = [];
this.attendeesList = [];
}
// import datas
importDatas({
professionalsList,
eventsList,
attendeesList
}) {
this.professionalsList = this.parsePros(professionalsList)
...
}
parsePros(list) {
return list.map( item => { ... })
}
...
}
Edit #2: So the issue was indeed some mutations that occures in one of the objects I was trying to bind.
You need to store the same data structure in data().dataModel as returned from DataManager() and change every property in methods. When you setting new dynamic property in data object they can't be reactive. You can try Vue.set(), but it's a bad practice.

Decentralizing functions in vuejs

Am from Angular2 whereby i was used to services and injection of services hence reusing functions how do i achieve the same in vuejs
eg:
I would like to create only one function to set and retrieve localstorage data.
so am doing it this way:
In my Login Component
this.$axios.post('login')
.then((res)=>{
localstorage.setItem('access-token', res.data.access_token);
})
Now in another component when sending a post request
export default{
methods:{
getvals(){
localstorage.getItem('access-token') //do stuff after retrieve
}
}
}
Thats just one example, Imagine what could happen when setting multiple localstorage items when retrieving one can type the wrong key.
How can i centralize functionality eg: setting token(in angular2 would be services)
There are a few different ways to share functionality between components in Vue, but I believe the most commonly used are either mixins or custom modules.
Mixins
Mixins are a way to define reusable functionality that can be injected into the component utilizing the mixin. Below is a simple example from the official Vue documentation:
// define a mixin object
var myMixin = {
created: function () {
this.hello()
},
methods: {
hello: function () {
console.log('hello from mixin!')
}
}
}
// define a component that uses this mixin
var Component = Vue.extend({
mixins: [myMixin]
})
var component = new Component() // => "hello from mixin!"
Custom module
If there are a lot of shared functionality with a logical grouping it might make sense to instead create a custom module, and import that where you need it (like how you inject a service in angular).
// localStorageHandler.js
const localStorageHandler = {
setToken (token) {
localStorage.setItem('access-token', token)
},
getToken () {
localstorage.getItem('access-token')
}
}
export default localStorageHandler
And then in your component:
// yourcomponent.vue
import localStorageHandler from 'localStorageHandler'
export default{
methods:{
getvals(){
const token = localStorageHandler.getToken()
}
}
}
Modules are using the more modern syntax of JavaScript, which is not supported in all browsers, hence require you to preprocess your code. If you are using the vue-cli webpack template it should work out of the box.