Use vue-resource within vueify component - browserify

I'd like to use the vue-resource $http methods within the script tag of my vueify component but I always get this error:
Uncaught TypeError: Cannot read property 'get' of undefined
My guess would be that the "this" keyword doesn't work (wouldn't know why) or the module isn't installed correctly (although it should be, checked that). My vue-component looks like this:
<template>
<!-- just displaying the data -->
</template>
<script>
module.exports = {
data: function () {
return {
foo: "bar"
}
},
ready: function() {
// the error occurs on the next line
this.$http.get('/api/users', function(data){
this.$set('users', data);
});
}
}
</script>

The answer was quite simple, I had to require('vue') in the component as well. I really did not think about this because I'm quite new to browser-/vueify.
The working code looks like this for anyone wondering:
<script>
var Vue = require('vue');
module.exports = {
data: function () {
return {
message: "Hello World!"
}
},
ready: function() {
var _self = this;
Vue.http.get('/api/users', function(data){
_self.$set('users', data);
});
}
}
</script>
EDIT: here is how I setup the whole dependencies and modules in my main.js file
// require dependencies
var Vue = require('vue');
var VueRouter = require('vue-router');
var VueResource = require('vue-resource');
// use vue-router and vue-resource
Vue.use(VueRouter);
Vue.use(VueResource);
// Laravel CSRF protection
Vue.http.headers.common['X-CSRF-TOKEN'] = document.getElementById('token').getAttribute('value');
// Routing
var router = new VueRouter();
var App = Vue.extend();
// start router in element with id of app
router.start(App, '#app');

FYI, for your .vue file, you don't have to require Vue. Instead you can reference the instance like this:
this.$http.get(...

Related

How fix __dirname not defined when using electron events with Vue?

I used nklayman/vue-cli-plugin-electron-builder to create an electron app prepared with Vue/Vuex. It ships with files main.js, background.js including Vue component starting point. But I can't get the events to work. My attempt below yields Uncaught ReferenceError: __dirname is not defined when rendering (compile is fine).
Component: Splash.vue
<template>
<div #click="open">open</div>
</template>
<script>
const { ipcMain } = require('electron')
export default {
methods: {
open()
{
ipcMain.on('my-open-event', (event, arg) => {
console.log(event, arg)
})
}
}
}
</script>
background.js
import { app, protocol, BrowserWindow } from 'electron'
...
app.on('my-open-event', async () => {
try {
"Will call some executable here";
} catch (e) {
console.error(e)
}
})
main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
store,
render: h => h(App)
}).$mount('#app')
Full error:
Uncaught ReferenceError: __dirname is not defined
at eval (webpack-internal:///./node_modules/electron/index.js:4)
at Object../node_modules/electron/index.js (chunk-vendors.js:1035)
at __webpack_require__ (app.js:849)
at fn (app.js:151)
at eval (webpack-internal:///./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/Splash.vue?vue&type=script&lang=js&:6)
at Module../node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/Splash.vue?vue&type=script&lang=js& (app.js:986)
at __webpack_require__ (app.js:849)
at fn (app.js:151)
at eval (webpack-internal:///./src/components/Splash.vue?vue&type=script&lang=js&:2)
at Module../src/components/Splash.vue?vue&type=script&lang=js& (app.js:1271)
Any ideas what I'm doing wrong?
To solve this I created a file vue.config.js in project root with content
module.exports = {
pluginOptions: {
electronBuilder: {
nodeIntegration: true
}
}
}
There are two processes in electron, the main process and the renderer process. Your Vue component is the renderer process. In order to communicate between these two processes, you need inter-processes communication. So in your case, you'd define a channel perhaps in background.js using ipcMain. You'd write something like:
ipcMain.on("my-custom-channel", (event, args) => handleEvent(event, args));
Then in your Vue component, you'd use the renderer process, ipcRendere, such as:
import { ipcRenderer } from "electron";
export default {
methods: {
open() {
ipcRenderer.send('my-custom-channel', "hello from Vue!")
}
}
}
Point is: you cannot use app.on for your custom events. app.on handles predefined electron events. Use ipcMain.on instead.
reference: https://www.electronjs.org/docs/api/ipc-main
You can apply the solution described on this post
How to import ipcRenderer in vue.js ? __dirname is not defined
In this way you can call window.ipcRenderer.send(channel, args...) from vue files.
Just make sure you configure preload.js on vue.config.js:
// vue.config.js - project root
module.exports = {
pluginOptions: {
electronBuilder: {
preload: 'src/preload.js' //make sure you have this line added
}
}
}
Another solution can be found here https://medium.com/swlh/how-to-safely-set-up-an-electron-app-with-vue-and-webpack-556fb491b83 and it use __static to refer to preload file instead of configure it on vue.config.js. To make it work you can disable preload es-lint warning inside of BrowserWindow constructor:
// eslint-disable-next-line no-undef
preload: path.resolve(__static, 'preload.js')
And make sure you added preload.js file on /public folder

Vue test-utils how to test a router.push()

In my component , I have a method which will execute a router.push()
import router from "#/router";
// ...
export default {
// ...
methods: {
closeAlert: function() {
if (this.msgTypeContactForm == "success") {
router.push("/home");
} else {
return;
}
},
// ....
}
}
I want to test it...
I wrote the following specs..
it("should ... go to home page", async () => {
// given
const $route = {
name: "home"
},
options = {
...
mocks: {
$route
}
};
wrapper = mount(ContactForm, options);
const closeBtn = wrapper.find(".v-alert__dismissible");
closeBtn.trigger("click");
await wrapper.vm.$nextTick();
expect(alert.attributes().style).toBe("display: none;")
// router path '/home' to be called ?
});
1 - I get an error
console.error node_modules/#vue/test-utils/dist/vue-test-utils.js:15
[vue-test-utils]: could not overwrite property $route, this is usually caused by a plugin that has added the property asa read-only value
2 - How I should write the expect() to be sure that this /home route has been called
thanks for feedback
You are doing something that happens to work, but I believe is wrong, and also is causing you problems to test the router. You're importing the router in your component:
import router from "#/router";
Then calling its push right away:
router.push("/home");
I don't know how exactly you're installing the router, but usually you do something like:
new Vue({
router,
store,
i18n,
}).$mount('#app');
To install Vue plugins. I bet you're already doing this (in fact, is this mechanism that expose $route to your component). In the example, a vuex store and a reference to vue-i18n are also being installed.
This will expose a $router member in all your components. Instead of importing the router and calling its push directly, you could call it from this as $router:
this.$router.push("/home");
Now, thise makes testing easier, because you can pass a fake router to your component, when testing, via the mocks property, just as you're doing with $route already:
const push = jest.fn();
const $router = {
push: jest.fn(),
}
...
mocks: {
$route,
$router,
}
And then, in your test, you assert against push having been called:
expect(push).toHaveBeenCalledWith('/the-desired-path');
Assuming that you have setup the pre-requisities correctly and similar to this
Just use
it("should ... go to home page", async () => {
const $route = {
name: "home"
}
...
// router path '/home' to be called ?
expect(wrapper.vm.$route.name).toBe($route.name)
});

unexpected token error importing a vuex store to laravel page

New-ish to Vue nd extremely new to Vuex. Im trying to import a store to my main page from which all my components branch out, but I keep getting an "unexpected token {" error in the browser console. I read through the documentation, but I cant find anything that would address this issue. I have tried changing every bit of syntax I can, and it doesnt seem to make a difference. The brackets around store in the import appear to be the problem, but when I remove them, i just get a new "unexpected identifier", or an "unexpected string" error. Am I importing it incorrectly? This format works fine on all my components, just not on this new vue instance.
vuex-test.blade.php
#extends('core.core_layouts.core_blank')
#section('browsertitle')
#endsection
#section('top-css')
#endsection
#section('breadcrumb')
#endsection
#section('main')
<component :is="currentView" v-bind="currentProperties"></component>
#endsection
#section('bottom-js')
<script>
import { store } from './../stores/store1.js';
var app = new Vue({
el:"#app",
store,
data: {
currentView: 'org-list',
choseOrg: {{ $org }},
}, // end data
computed: {
currentProperties: function() {
if (this.currentView === 'org-list') { return { } }
if (this.currentView === 'add-org') { return { parentOrg: '' } }
}
},
mounted : function() {
}, // end mounted
methods: {
}, // end methods
components: {
},
});
</script>
#endsection
store1.js
export const store = new Vuex.Store({
state: {
safelyStoredNumber: 'ret',
count: 2,
},
mutations: {
setOrgIdentity(state, orgID) {
state.OrgID = orgID
}
}
});
Per comments:
Yes, I get the error at the browser.
import won't work there. It is meant to run on node.js, during the build phase of a vue-cli-based project.
If you are deploying the code directly to browser, you will need to use code that is supported by the browser. In this case, the solution to import other JavaScript files is standard <script> tags.
So, in your code, change:
import { store } from './../stores/store1.js';
to something like (change the path to what is more appropriate):
<script src="./../stores/store1.js"></script>
And in store1.js (because export is too meant for node.js), replace:
export const store = new Vuex.Store({
With:
window.store = new Vuex.Store({

Vuejs testing with Mocha: component not mounting to element

I'm relatively new to developing with VueJS and testing with Mocha (being used headlessly) and I'm intentionally not using vue-cli to get a better understanding of how everything works. I have a suit of unit tests that function fairly well (fetch requests, getting/setting props, etc), up until the point where I need to check something in the DOM. It appears that the components are being successfully created, but they are not being mounted to an element.
I'm using JSDom to create a DOM context for the headless tests, as can be seen in my _setup.js:
global.Vue = require('vue/dist/vue');
require('jsdom-global')('<html><head></head><body><main id="MyApp"></main></body>');
In these cases, $el is undefined, despite the fact the above-defined DOM elements can be found:
app.spec.js
import assert from 'assert';
import app from '../src/js/app.vue';
describe('app.vue', function() {
describe('#created', function() {
it('should initialize the application', function() {
const vEl = document.getElementById('MyApp');
const vm = new Vue({el: vEl}).$mount();
console.log(vEl); // HTMLElement {}
console.log(vm.$el); // undefined
assert(true); // pass anyway for now
});
});
});
myComp.spec.js:
import assert from 'assert';
import header from '../src/js/components/myComp.vue';
describe('myComp.vue', function() {
describe('#created', function() {
it('should initialize the component', function() {
const Constructor = Vue.extend(myComp);
const comp = new Constructor({
propsData: {
someProp: 'hello world'
}
}).$mount();
console.log(document.getElementById('MyApp')); // HTMLElement {}
console.log(comp.$el) // undefined
assert(true);
});
});
});
Note that if I change the element's ID to something that doesn't exist when doing getElementById, I get the following error - which to me it would imply that it has a handle on the element when using the correct ID:
// document.getElementById('IDontExist');
[Vue warn]: Failed to mount component: template or render function not defined.
Note that my component definition is a little different since I'm going for a bare-bones approach and not using vue-cli. Examples (some code omitted for brevity):
app.vue
import myComp from './components/myComp.vue';
let app = new Vue({
el: '#MyApp',
data: {
someProp: 'hello world'
},
created: function() {},
methods: {}
});
export {app};
myComp.vue
export default Vue.component('my-comp', {
props: ['someProp'],
data: function() {},
created: {},
watch: {},
methods: {},
template: `<div>{{someProp}}</div>`
});
I have a feeling I'm overlooking something pretty simple regarding how to wire up the component mounting to the DOM, or accessing the component (but it seems to be in line with the docs). Any ideas?

Vue 2.0 SSR error page

I'm trying to implement ErrorPage vue component for my SSR application. I'm using boilerplate with
// server.js
...
const context = { url: req.url };
const stream = renderer.renderToStream(context);
// If an error occurs while rendering
stream.on('error', (error) => {
var app = new Vue({
template: '<div>Error page</div>'
});
errRenderer.renderToString(app, function (error, html) {
return res
.status(500)
.send(html);
});
});
I'm aware that this is not super pretty, but is there another way of handling that? Ideally I would love to just load an external Vue component and send it to the browser.