Using Moment.js as a plugin inside main.js - vue.js

I registered Moment.js as a plugin, like this:
import Vue from 'vue'
import moment from 'moment'
moment.locale('pt_BR')
Vue.use({
install (Vue) {
Vue.prototype.$moment = moment
}
})
Now, I need to use this in my main.js filters
import './plugins/moment'
Vue.filter('formatDate', value => {
return this.$moment(value, 'YYYY-MM-DD').format('DD/MM/YYYY')
})
Buth this return an error:
Error in render: "TypeError: Cannot read property '$moment' of
undefined"

Looks like you can not access this like in the vue components for filter methods.
By Evan You
This is intentional in 2.x. Filters should be pure functions and should not be dependent on this context. If you need this you should use a computed property or just a method e.g. $translate(foo)
I guess the best way is importing the moment on main.js like this:
import moment from 'moment'
Vue.filter('formatDate', value => {
return moment(value, 'YYYY-MM-DD').format('DD/MM/YYYY')
})

Vue.filter() creates a global filter before the Vue instance is created, a global filter does not have access to the Vue instance therefore you can not access this.$moment. If you need to access the vue instance, you'll need to register a local filter on components.
However, you could rectify this by directly referencing the module:
import moment from 'moment';
Vue.filter('formatDate', value => {
moment(value, 'YYYY-MM-DD').format('DD/MM/YYYY')
});

Related

How to correctly set up Cypress 10, Vue2, Vuetify, Composition API for component testing?

The guide is quite confusing and obviously not correct when trying to set up Cypress 10 for component testing with Vue2 and Vuetify with composition API. There's lots of errors of unknown tags, things returned from setup() aren't accessible, spread operators where there shouldn't be, imports that don't work etc. What's the correct way to set things up so testing works?
You need to set up Vuetify as regular, to the global Vue object. Then in the mount you need to give the Vuetify object to the mount function so it can be found by the components. When using Composition API that also needs to be set up regularly to the global instance (unlike Vuetify it also works in the local instance, if you want).
Then mount the component inside a v-appso it should work properly and pass arugments around.
So component.ts file will include this:
import { mount } from 'cypress/vue2'
import Vuetify from 'vuetify'
import VueCompositionAPI from '#vue/composition-api';
import Vue from 'vue'
import { VApp } from 'vuetify/lib/components/VApp';
Vue.use(Vuetify);
Vue.use(VueCompositionAPI);
Cypress.Commands.add('mount', (component, args) => {
args.vuetify = new Vuetify(yourVuetifyOptions);
return mount({ render: (h) => h(VApp, [h(component, args)]) }, args);
})
When using the mount just do:
cy.mount(myComponent, { props: {someProp: 123 } });
If you need to set up plugins for the local Vue instance in the test they need to be set in args.extensions.plugins, the guide seems to mention globals but that is incorrect.
cy.mount(myComponent, { props: {someProp: 123 }, extensions: { plugins: [MyPlugin] } });
Note that I'm using args for both settings parameters for mount and also for the component, if needed those two can be separated. But there shouldn't be much clashing of properties and attributes so this works.
Also the props/attributes/etc for the component must be given as they're given to createElement, not mount (so props instead of propsData etc).

Vue 3 access to app level provided instances from vue-router

I want to write some complicated guard logics in vue-router in Vue 3 to protect entering some routes according to store and my other provided modules. For example, I want to check if user profile info is present or not:
router.afterEach((to, from) => {
console.log('store: ', useStore());
const puex = usePuex();
puex.isReady().then(() => {
const me = puex.me.compute();
watch(me, (...params) => console.log('router: ', ...params));
});
});
In the above code, useStore and usePuex both try to inject store and puex instances from Vue app which are provided while being used in main.js bootstrap. But both use functions return undefined and I guess that the inject in this scope searches a different place where app-level provided instances do not exist.
So how can I inject them in the router file, or in other words how can I get store and puex instance using useStore and usePuex here?
I have found a way according this question but I still don't know if it is the best available solution. I can export the app instance from main.js file and then use app.$store and app.$puex instead. Although it works, I still think about a better solution to inject the store and puex instance using use functions (inject).
You still can add the navigation guards after that your app has mounted in main.js/ts, the code would look like:
// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
const vm = createApp(App)
.use(puex)
.use(router)
.mount('#app');
router.afterEach((to, from) => {
const me = vm.puex.me.compute();
watch(me, (...params) => console.log('router: ', ...params));
});
You still can export that vm, to import it in the router file and use it the same way, but I really find somehow confusing, as main.js/ts is already importing the router file.

How to include a library to be available in whole Vue.js 3 project?

According to this blog post the correct way of including frequently used libraries (e.g. axios) in Vue.js 2 is to set them as property of Vue prototype object like this:
import axios from 'axios';
Object.defineProperty(Vue.prototype, '$axios', { value: axios });
Unfortunately, this approach is not working in Vue.js 3 anymore. So what is the correct way of importing library to be accesible in whole project? I would prefer not to set them as global variable (i.e. to the window object.)
To use provide/inject as an alternative
import { createApp } from 'vue'
import App from './App.vue'
import axios from 'axios';
const app = createApp(App)
app.provide('axios', axios ) // <-- define here
app.mount('#app')
Then in any component you wanna use axios you would do this
app.component('todo-list-statistics', {
inject: ['axios'],
created() {
this.axios // --> Injected property
}
}
I think the best way to us a library in a vue 3 project is to use the depency injection
https://v3.vuejs.org/guide/component-provide-inject.html
however I simply recommend that you import the library where you really need it, to have a more accurate intellisense, and a better three-shaking

vue.js 2 how use components in ES2015 webpack

I am trying to use vue-components in a webpack Typescript project but it doesn't seem to be working. I don't get any errors during the build and run, but the component HTML is never inserted into the output - I can just see the HTML source of the component instead i.e. .
My project is an ES2015 using Vue2 in VS.Net 2017. My component looks like this:
import Vue from 'vue'
import Component from 'vue-class-component'
// The #Component decorator indicates the class is a Vue component
#Component({
// All component options are allowed in here
template: '<button #click="onClick">Click!</button>'
})
export default class MyHeader extends Vue {
// Initial data can be declared as instance properties
message: string = 'Hello!'
// Component methods can be declared as instance methods
onClick(): void {
window.alert(this.message)
}
}
I have tried the official reference guide to register the component and use it. When I look at the vue-component example, it uses the same format as my project so I added the markup and properties to my Typescript class definition:
import Vue from 'vue';
import Component from 'vue-class-component';
import MyHeader from './MyHeader';
#Component({
components: {
MyHeader
}
})
export default class GetDataComponent extends Vue {
<...rest of class...>
}
but in my project the "components:" section is squiggly-underline-red with the message:
Object literal may only specify known properties, but 'components'
does not exist in type 'VueClass'. Did you mean to write
'component'?
Every example I have seen with vue-component (such as this one) uses the "components:" option in the #Component to register and use their Vue component, but in my project it doesn't seem to like it. I have also tried global registration of the component (such as this one) which includes the line:
// Register the component globally
Vue.component(my-header', MyHeader)`
but in that case I get an error like this:
Type 'typeof MyHeader' is not assignable to type 'AsyncComponent'
The Vue file works (without the Component added) and all content is rendered correctly. It's getting the Component included that doesn't work - I either get Design-time errors per above, or nothing appears in the output at all.
Is my import wrong? Or the format of the #Component? I get the feeling I am doing something that is very basic, very wrong...

Access Vue.js plugin from main.js

I'm creating a plugin and I just wonder why I can't access it in main.js file. Here's how Auth.js looks like:
const Auth = {
install(Vue) {
Vue.prototype.$isGuest = function () {
console.log('This user is a guest.');
}
Vue.prototype.$getAuthToken = function () {
console.log('Auth token will be returned.');
}
}
}
export default Auth
This is main.js:
import Auth from '#/helper/Auth'
Vue.use(Auth)
However, when I execute console.log(this.$isGuest()), it doesn't work. It actually returns the following:
main.js?1c90:25 Uncaught TypeError: this.$isGuest is not a function
The problem is that this method works when I call it in components such as Dashboard.vue and things like that.
I have a way to avoid calling isGuest method within main.js (I can call it in Layout.vue), but I'm more curious why it doesn't work in main.js.
Maybe because Vue hasn't been initialized yet, but even if I put the console.log() line at the end of the file, still doesn't work.
Thanks,
N.
If you are calling this.$isGuest() outside of Vue, you will get the error you describe. That's because this is not a Vue object. What this is depends on how you are building your code, but given you are using import it's probably the module.
Also, you are adding $isGuest to the prototype of Vue. That means that the function is only going to be available on actual instances of Vue objects. That is why it is available in your components.
If you want to use it in the main script, the only place you will be able to get to it is inside the Vue object in a lifecycle handler, method, or computed. For example:
new Vue({
mounted(){
console.log(this.$isGuest()) // this should work
}
})