Passing props to root instances in Vue.js - javascript

The problem
I am trying to pass a prop to my root constructor. I understand that propsData is the way to go:
var appComponent = Vue.component('app', require('./components/app/app.vue.html'));
new Vue({
el: '#app-root',
router: new VueRouter({ mode: 'history', routes: routes }),
propsData: { test: 'hi!' },
components: { appComponent },
render: h => h('app')
});
This is the AppComponent that receives the prop:
export default class AppComponent extends Vue {
#Prop()
test: string;
mounted() {
console.log('test = ' + this.test);
// result: test = undefined
// expected: test = 'hi!'
}
}
Things I have tried
The only way to make it work in development (not in production) was:
test: string;
beforeCreate() {
this.test = this.$root.test;
}
It work on development (windows), but in deployment (linux) I got this typescript error:
ERROR in [at-loader] ClientApp/components/app/app.ts:14:34
TS2339: Property 'test' does not exist on type 'Vue'.
Here is another post talking about this.

You can pass props in the data object which is the 2nd argument the createElement alias h function takes in the render option
render: h => h('app', { props: { test: 'hi!' }})

Related

Base class observable(variable) remains undefined in spec file - Angular unit testing

I have a componentA that inherits(extends) another one i.e componentB. The problem is unit tests of componentA are breaking because an observable defined in componentB remains undefined in spec.ts.
Relevant part of ComponentB(Base class)
this.storeObservable = this.store.select(selectCandidateState);
Accessing this in ComponentA,
this.storeObservable.subscribe((candidateState) => {
this.candidateSkills = candidateState.skills;
this.unsavedChanges = false;
});
Now the unit test of componentA are breaking with this message:
TypeError: Cannot read property 'subscribe' of undefined,
while pointing to this.storeObservable.subscribe
ComponentA
export class ComponentA extends ComponentB implements OnInit, OnDestroy
ngOnInit() {
super.ngOnInit();
this.storeObservable.subscribe((candidateState) => {
this.candidateSkills = candidateState.skills;
this.unsavedChanges = false;
});
The unit tests
let shallow: Shallow<ComponentA>;
beforeEach(() => {
const actions$ = new Observable();
const services = [Store];
const mockedServices = [ConfigCzarServiceMock({ test: 'property' })];
shallow = createShallowComponent(
ComponentA,
ProfileModule,
[componentB],
[],
services,
mockedServices,
);
> All the tests break with above error. TypeError: Cannot read property
> 'subscribe' of undefined,

Can't pass data from main.js to App.vue in Vue js?

In my main.js I do authentication and after that I populate a data property. I'm trying to pass that data property to the App component but seems not possible?
main.js
import Vue from 'vue'
import App from './App.vue'
new Vue({
data: {
test: 'test'
},
//render: h => h(App)
render: h => h(App, { props: { 'test': this.test }})
}).$mount('#app')
App.vue
<template>
<div id="app" :test="test">
<h1>{{test}}</h1>
</div>
</template>
<script>
export default {
name: 'app',
props: ['test']
}
</script>
Just gives error as test and/or this.test is undefined. Passing a hard-coded value works. Without using props on the render line has no errors but App doesn't receive data. Am I just doing it wrong?
You can set render to a normal function in order for this to refer to the Vue instance:
render: function (h) {
return h(App, { props: { test: this.test } });
}
Your problem is that you're defining render as an anonymous arrow function. which means that it's this context isn't bind to you Vue instance. if you want a function's context to be the "hosting" object, you must define it with the function syntax.
new Vue({
data: {
test: 'test'
},
render(h) {
return h(App, { props: { 'test': this.test }})
}
}).$mount('#app')
you can read about arrow function vs regular functions here

Unit testing vuejs methods using Jest

I'm trying to test the methods in jest framework. For javascript methods i was able to import the methods from file using require() function and test it (expect(addition(1,2).toBe(3))).
But unable to replicate the same in VueJS methods.
// VueJS
export default defineComponent({
name: "App",
components: {
HelloWorld,
},
methods: {
addition(a: number, b: number) {
return a + b;
},
subtraction(a: number, b: number) {
return a - b;
},
multiplication(a: number, b: number) {
return a * b;
},
},
});
test file
import addition from "./App.vue"
describe("HelloWorld.vue", () => {
it("testing vue method", () => {
expect(addition(1,2).toBe(3));
});
});
//error on line 4 states "Value of type 'DefineComponent<{}, {}, any, ComputedOptions, MethodOptions, ComponentOptionsMixin,
// ComponentOptionsMixin, ... 4 more ..., {}>' is not callable. Did you mean to include 'new'?"
The App.vue file is compiled into a component definition, which is used to instantiate the App component with Vue Test Util's mount or shallowMount, which results in a test wrapper. The wrapper has a vm property that provides direct access to the component's methods and props.
So your test should look similar to this:
import { shallowMount } from "#vue/test-utils";
import App from "./App.vue";
describe("App.vue", () => {
it("testing vue method", () => {
const wrapper = shallowMount(App);
expect(wrapper.vm.addition(1,2)).toBe(3);
});
});

Vue JS - custom properties on vue component gives TS errors

I have created some Vue middleware and I am trying to add a custom property to one of my components in Vue like so:
middleware.js:
import { VueConstructor } from 'vue/types';
function eventPlugin(vue: VueConstructor): void {
const Socket = new someClass();
Object.defineProperties(vue.prototype, {
$socket: {
get: function get() {
return Socket;
},
},
});
vue.$socket = Socket;
}
myComponent.js
const MyComponent = Vue.extend({
name: 'MyComponent',
$socket: {
event(data: any) {
}
},
methods: {
MyMethod() {
}
}
})
app.js
import Vue from 'vue';
import eventPlugin from './middleware.js';
import MyComponent from './myComponent.js'
Vue.use(eventPlugin);
export default new Vue({
render: (h) => h(MyComponent),
}).$mount('#app');
The custom property I am trying to add here is obviously socket. The problem is when I add it I get typescript errors:
Object literal may only specify known properties, and 'socket' does
not exist in type 'ComponentOptions<Vue, DefaultData,
DefaultMethods, DefaultComputed, PropsDefinition<Record<string,
any>>, Record<...>>'.
As you can see in middleware.js I have tried defining the property there so I am not sure why I am receiving the error?
When adding instance properties or component options, you also need to augment the existing type declarations.
Based on Augmenting Types for Use with Plugins (Vue 2):
To type-hint the $socket instance property:
declare module 'vue/types/vue' {
interface VueConstructor {
$socket: string
}
}
export {}
To type-hint the $socket component option:
import Vue from 'vue'
declare module 'vue/types/options' {
interface ComponentOptions<V extends Vue> {
$socket?: string
}
}
export {}
The type declarations above should go in a .d.ts file in your src directory. If using VS Code, any new .d.ts files might require restarting VS Code to load.

Passing props via router doesn't work

I'm using VueJS 2.3.3 and coffescript and I'm trying to pass a prop to a component from the router, but I'm having no success. Code is not mine, so I'm having some trouble figuring out what am I doing wrong. Here's my router:
App = require './views/App'
Shared = {
header: require('./views/shared/header'),
global_loader: require('./views/shared/global_loader.vue')
}
view = (view_name) ->
require("./views/#{view_name}")
componentize = (view_name, options = {}) ->
options.include_header ?= true
component = options.component || {}
component.app ?= view(view_name)
component.header = Shared.header if options.include_header
component
exports.routes = [
{
path: '/',
component: App
children: [
{
path: '/component',
components: componentize('path/to/component'),
props: { has_groups: true }
},
...
]
}
...
}
Here's my App.vue code:
<template lang="pug">
#app-wrapper
transition(name="fade")
router-view(name="global_loader")
#header-wrapper
router-view(name="header")
#current-view-wrapper.container
transition(name="enter")
router-view(name="app")
</template>
On my component, I'm receiving the prop as usual:
props:
has_groups:
default: false
Everything works fine, except that has_groups doesn't receive the correct prop value from the router. It doesn't change to true.
Can anyone help me finding out what I'm missing?
I found the solution. As I'm using named routes, I have to configure the props like:
props:
global_loader: false
header: false
app: (route) -> ({ has_groups: true })

Categories