A vue application I am working on currently has lots of code redundancies relating to date functions. In an effort to reduce these redundancies, I'd like to create a utility class as shown below, import it and set it to a Vue data property within the component, so I can call the date functions within it.
I am not certain on the best way to implement this. The current implementation results in an error saying TypeError: this.dates is undefined and my goal is not only to resolve this error but create/utilize the class in the Vue environment using best standards.
Importing utility class
import Dates from "./utility/Dates";
...
Component
const contactEditView = Vue.component('contact-edit-view', {
data() {
return {
contact: this.myContact
dates: Dates
}
},
...
Dates.js
export default {
dateSmall(date) {
return moment(date).format('L');
},
dateMedium(date) {
return moment(date).format('lll');
},
dateLarge(date) {
return moment(date).format('LLL');
}
};
View
Date of Birth: {{ dates.dateMedium(contact.dob) }}
My suggestion for this is to use a plugin option in Vue. About Vue plugin
So you will crate a new folder called services, add file yourCustomDateFormater.js:
const dateFormater = {}
dateFormater.install = function (Vue, options) {
Vue.prototype.$dateSmall = (value) => {
return moment(date).format('L')
}
Vue.prototype.$dateMedium = (value) => {
return moment(date).format('lll')
}
}
In main.js:
import YourCustomDateFormater from './services/yourCustomDateFormater'
Vue.use(YourCustomDateFormater)
And you can use it anywhere, like this:
this.$dateSmall(yourValue)
Or, if you want to use mixin. Read more about mixin
Create a new file dateFormater.js
export default {
methods: {
callMethod () {
console.log('my method')
}
}
}
Your component:
import dateFormater from '../services/dateFormater'
export default {
mixins: [dateFormater],
mounted () {
this.callMethod() // Call your function
}
}
Note: "Use global mixins sparsely and carefully, because it affects every single Vue instance created, including third party components. In most cases, you should only use it for custom option handling like demonstrated in the example above. It’s also a good idea to ship them as Plugins to avoid duplicate application." - Vue documentation
dateUtilsjs
import moment from 'moment-timezone'
function formatDateTime(date) {
return moment.utc(date).format("M/D/yyyy h:mm A")
}
export { formatDateTime }
Component JS
...
import { formatDateTime } from '../utils/dateUtils'
...
methods: {
formatDateTime,
}
Used within component
{{ formatDateTime(date) }}
Related
I'm trying to extract a function for use across multiple components, but "this" is undefined and I'm unsure of the best practice approach of how to attach the scope so my function knows what "this" is. Can I just pass it as an argument?
Component:-
import goToEvent from "#/common";
export default {
name: "update",
methods: {
goToEvent
common function:-
let goToEvent = (event, upcoming=false) => {
this.$store.dispatch({
type: 'setEventsDay',
day: event.start_date
})
}
export default goToEvent
When I call goToEvent in my component, I get TypeError: Cannot read property '$store' of undefined. How do I avoid this?
In this situation I recommend to define eventable as a mixin :
const eventable= {
methods: {
goToEvent(event, upcoming=false) {
this.$store.dispatch({
type: 'setEventsDay',
day: event.start_date
})
}
}
}
export default eventable;
in your vue file :
import eventable from "#/eventable";
export default {
name: "update",
mixins:[eventable],
....
second solution :
export an object with the function as nested method then import it and spread it inside the methods option :
export default {
goToEvent(event, upcoming=false){
this.$store.dispatch({
type: 'setEventsDay',
day: event.start_date
})
}
}
then :
import goToEvent from "#/common";
export default {
name: "update",
methods: {
...goToEvent,
otherMethod(){},
}
//....
}
You're tagged with Typescript, so you need to tell TS that this actually has a value (note, I do not know VueJS, am using the generic Event types here, there is likely a more valid and correct type!)
First option, manually tell it what there is -
let goToEvent = (this:Event, event, upcoming=false) => {
Other option - tell it what type it is -
let goToEvent: EventHandler = (event, upcoming=false) => {
Of the two I personally prefer the second style for readability.
There are numerous ways to achieve this, here are some that I like to use in my projects:
Method 1: Mixins
Mixins are great for sharing a bunch of methods across components and also easy to implement, although one big con is that you will not be able to import specific methods that you need. Within the mixin, this follows the rules as in components.
File: #/mixins/eventable
import { mapActions } from 'vuex'
export default {
methods: {
...mapActions([])
goToEvent (event, upcoming = false) {
store.dispatch({
type: 'setEventsDay',
day: event.start_date
})
}
}
}
Usage in component:
import eventable from '#/mixins/eventable'
export default {
name: 'ComponentName',
mixins: [eventable],
methods: {
componentMethod () {
this.goToEvent()
}
}
...
Method 2: Static JavaScript files
In some cases, you might have a collection of helper functions kept in a file and want the ability to import as you need.
In your case, you seem to be using a store actions (assumed from the dispatch), hence I'll be including importing and using the store within the static JS file.
File: #/static/js/eventable.js
import store from 'path_to_store_file'
const goToEvent = () => {
store.dispatch('actionName', payload)
}
export default {
goToEvent
}
Note:
Although this is not entirely necessary, but only by declaring the imported function as a method within the component will it be bound to the component instance. This will allow you to access the function in the HTML portion.
Usage in component:
import { goToEvent } from '#/static/js/eventable.js'
export default {
name: 'ComponentName',
methods: {
// Read note before this code block
goToEvent,
componentMethod () {
// When declared as a method
this.goToEvent()
// When not declared, it can still be accessed in the js portion like this
goToEvent()
}
}
...
I currently writing a financial application using Vue.js and Vuetify. I have a few component files and javascript files like
Dashboard.vue
Cashflow.vue
NetWorth.vue
stores.js <- Vue Vuex
I have some functions which I need to use across all the Vue.js and javascript files. Would it be possible for me to perhaps write a function library which can be used across all
the component and js files.
function moneyFormat(num)
function IRRCalc(Cashflow)
function TimeValueMoneyCalc(I,N,PV,FV,PMT)
function PerpetualAnnuityCalc(I,PV)
function CarLoanInstallment(V,N)
function HouseLoanInstallment(V,N)
I know in C it is very simple just #include<financial.h> was wondering is there something similar in javascript.
Thanks.
There are 3 ways to do this:
1/You can create a helper.js file and import it to .vue files
// helper.js
export default {
function moneyFormat(num) { // some logic}
}
// Dashboard.vue
<script>
import helper from "helper.js" //the path may change depends on where you put the js file
methods: {
useHelper(value) {
helper.moneyFormat(value)
}
}
</script>
2/Another way is bind the function to Vue prototype
in main.js
Vue.prototype.$moneyFormat= function moneyFormat(num) {}
then in Dashboard.vue just call this.$moneyFormat(num). No need to import anything
3/ Use mixins. You can search online on how to use this https://v2.vuejs.org/v2/guide/mixins.html
You can create a single JS file that holds all the helper/util methods, and then export them individually:
export function moneyFormat(num) { ... }
export function IRRCalc(Cashflow) { ... }
export function TimeValueMoneyCalc(I,N,PV,FV,PMT) { ... }
export function PerpetualAnnuityCalc(I,PV) { ... }
export function CarLoanInstallment(V,N) { ... }
export function HouseLoanInstallment(V,N) { ... }
Then, you can simply import individual methods as of when needed, i.e.:
import { CarLoanInstallment, HouseLoanInstallment } from '/path/to/helper/file';
This can be quite usefuly for tree-shaking when you're bundling with webpack, for example, so that you don't bundle unnecessary functions that are never used in your project.
You can use Mixin
In your main.js, add Vue.mixin:
import Vue from "vue";
import App from "./App.vue";
Vue.mixin({
methods: {
helloWorld() {
alert("Hello world");
}
}
});
new Vue({
render: h => h(App)
}).$mount("#app");
and then you can call helloWorld() method from your component script with this.helloWorld() or just helloWorld() from the template.
You also can use filters if the method is to apply common text formatting
In your main.js, add Vue.filter:
import Vue from "vue";
import App from "./App.vue";
Vue.filter("capitalize", function(value) {
if (!value) return "";
value = value.toString();
return value.charAt(0).toUpperCase() + value.slice(1);
});
new Vue({
render: h => h(App)
}).$mount("#app");
and then you can do {{ "some text" | capitalize }} to apply capitalize filter on "some text"
Example here: https://codesandbox.io/s/heuristic-dirac-esb45?file=/src/main.js:0-226
I have to display dynamic meta descriptions for my articles and I am kind of struggling to achieve that with the async function for my head object. This is what I have so far:
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
#Component
export default class ArticleContent extends Vue {
article: any | null = null;
articlelist = example.data;
async asyncData({params: any }) { <----- Not sure how I could put in my articlelist here
return this.articlelist;
}
head(): object {
return {
title: this.articlelist.productId.productNames['en'],
meta: [
{
hid: this.articlelist._id,
name: this.articlelist.productNames['en'],
content: this.articlelist.metaDescription['en'],
},
],
};
}
}
</script>
articlelist is what I am using in the head() object for my meta description. Would appreciate some help!
Both the head() and asyncData() properties are not part of the core of vue,
to use head() you need to install this as a plugin
to use asyncData() you have to use nuxt
If your spa has a strong need for seo I suggest you use nuxt, which natively includes seo and the conversion from vue to nuxt is very easy
If you already using nuxt this is the correct way to get
async asyncData({params: any }) {
const articlelist = await axios.get('some.url'); //get the data
return { articlelist }; //this object is merged with the data of the istance
}
I'm a newbie in javascript and vue.js and I'm facing somme issue when trying to add a new function in an existing programme.
I have put my new function (with others) in a separate file:
export const MyFunctions = {
MyFunction: function(param) {
// Doing stuff
}
}
Then I import the file in the component file and calling my function :
<script>
import {MyFunctions} from "#/components/MyFunctions.js";
export default {
name:"Miniature",
computed: {
useMyFunction() {
MyFunction("Please do some stuff !");
}
}
}
</script>
When the component is used, I get an error message
[Vue warn]: Property or method "MyFunction" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
I've read a lot of documentation and fail to understand why it doesn't work. Can anyone help me with this ??
You're exporting an object then in order to use the MyFunction you need to access to that function using dot notation, like this: MyFunctions.MyFunction("Please do some stuff !")
I made a working example for this use-case: https://codesandbox.io/s/62l1j19rvw
MyFunctions.js
export const MyFunctions = {
MyFunction: function(param) {
alert(param);
}
};
Component
<template>
<div class="hello">
{{msg}}
<button #click="handleClick">Click me</button>
</div>
</template>
<script>
import {MyFunctions} from "../MyFunctions.js";
export default {
name: "HelloWorld",
data() {
return {
msg: "Welcome to Your Vue.js App"
};
},
methods:{
handleClick: function(){
MyFunctions.MyFunction("Please do some stuff !");
}
}
};
</script>
You can just import your javascript files into .vue files as long as they are inside <script> tags. Since Vue.js is after all javascript, the first part where you should look at while debugging is if you have some kind of mistake in your syntax. From what I see, there is some confusion with the import and export statements, which could be quite complex at first!
Check MDN's Documentation specially under named exports:
In the module, we could use the following
// module "my-module.js"
function cube(x) {
return x * x * x;
}
const foo = Math.PI + Math.SQRT2;
var graph = { /* nice big object */ }
export { cube, foo, graph };
This way, in another script, we could have:
import { cube, foo, graph } from 'my-module';
// Use your functions wisely
what you export is an object, and what you use is a field/method inside this object, so you need to use your function this way:
MyFunctions.MyFunction("Please do some stuff !");
I am refactoring some code in my app and turns out,the below logic it is repeated in many many components.
import component1 from '...'
import component2 from '...'
import component3 from '...'
//...many others
export default {
//other data
components: {
component1,
component2,
component3
//...
}
}
Does exists a shorter approach in order to clean my code?
Thanks for your time
Below are 3 ways.I prefer method 3 by the way.
Method 1
Create a js file in my case dynamic_imports.js:
export default function (config) {
let registered_components = {}
for (let component of config.components) {
registered_components[component.name] = () => System.import(`../${config.path}/${component.file_name}.vue`)
}
return registered_components
}
In the component in which you have many component imports and registrations
import dynamic_import from '#/services/dynamic_imports' //importing the above file
let components = dynamic_import({
path: 'components/servers',
components: [
{ name: 'server-one', file_name: 'serverOne' },
{ name: 'server-two', file_name: 'serverTwo' },
]
})
export default {
//...other code
components: components
}
As a result you will import and register your components with "clean code".
But note that this worked for me,maybe it has to modified a lit bit to fit your needs,to understand:
The property path means that will look at this path for the names specified in file_name.The name property is the name you register the component
Method 2
If you don't like the above look below to another way:
function import_component(cmp_name){
return System.import(`#/components/${cmp_name}.vue`);
}
export default{
components: {
'component1': () => import_component('componentOne'),
'component2': () => import_component('componentTwo'),
'component3': () => import_component('componentThree')
}
}
Method 3
If again you are saying: This is not a cleaner way,take a look below but keep in mind that if you are working in team and skills differ,then some programmers will be a little bit confused.
dynamic_imports.js
export default function ({path, file_names, component_names}) {
let registered_components = {}
for (let [index, file_name] of file_names.entries()) {
registered_components[component_names[index]] = () => System.import(`../${path}/${file_name}.vue`)
}
return registered_components
}
In your component
import dynamic_import from '#/services/dynamic_imports'
let components = dynamic_import({
path: 'components/servers',
file_names: ['serverOne', 'serverTwo'],
component_names: ['server-one', 'server-two']
})
export default {
components: components
}
You can automatically register such repeated base components globally using the pattern described in the official docs
https://v2.vuejs.org/v2/guide/components-registration.html#Automatic-Global-Registration-of-Base-Components
Chris Fritz also talks about this pattern in his awesome video where he mentions 7 secret patterns for cleaner code and productivity boost while working with Vue.js
The disadvantage of this approach, however, is that the components that you autoregister this way always end up in the main bundle and therefore cannot be lazy loaded/code-splitted. So make sure you do this only for the base components that are very generic.