After researching this question thoroughly and after implementing the Bus method to communicate between siblings, I am getting a webpack error, so either i'm
not implementing right (using latest CLI as of yesterday), or I need some other method.
I am new to Vue, coming from React and I have built a simple app wrapping 2 components in app.vue:
an input that sends data:
<GoogleInput #searchEvent="getSearchLocation($event)"></GoogleInput>
and a map container that should receive the data:
<GoogleMapsContainer :location="location" name="location-project"></GoogleMapsContainer>
I successfully implemented child (GoogleInput) to parent communication in app.js via:
getSearchLocation(input) {
this.location = input;
}
with a method in GoogleInput:
this.$emit('searchEvent', ev.target.value);
Up to here everything was smooth.
Yet when trying to communicate my input value through to sibling (GoogleMapsContainer) via the bus method:
In my entry index.js: const Bus = new Vue({});
A new emit in my sending component:
Bus.$emit('passLocation', {location: this.location})
and in my receiving component:
Bus.$on('passLocation', (input) => {
this.location = input;
});
I get a webpack error:
Error in created hook: "TypeError: __WEBPACK_IMPORTED_MODULE_1_vue__.$on is not a function"
I am looking for "the shortest distance" to communicate my input to map container, without going into solving webpack issues (if this is a webpack issue at all, or just a fat finger mistake).
BTW: If vuex isn't a time consuming method to implement (as React-Redux is) in here, that
would be a cool route as well, but I must keep this design (already in
Git)
Thanks
You probably have some import errors.
In your index.js, export it like:
export const Bus = new Vue({});
And in the files you do Bus.$emit(...) or Bus.$on(...) import it like:
import { Bus } from './index'; // make sure you use the correct relative path
Related
in my complex Vue project I am getting this console warning:
[Vue warn]: Avoid app logic that relies on enumerating keys on a component instance. The keys will be empty in production mode to avoid performance overhead.
Unfortunately I can not find the reason for this warning just by the above message.
How can I track down the reason for this warning?
Check if your watching an entire route object anywhere in your code. Doing so throws that error (in my case).
Refer this vue documentation on watching routes
Accessing router and current route inside setup
The route object is a reactive object, so any of its properties can be watched and you should avoid watching the whole route object. In most scenarios, you should directly watch the param you are expecting to change.
Was able to fix this with the suggestion done by Glass Cannon.(https://stackoverflow.com/a/70205284/11787139)
To clarify and maybe help someone else: I was trying to send an Axios request to the server of which the data I sent through was composed of a direct component reference emitted by the component function.
Component
saveItem(){
this.saved = true;
setTimeout( this.resetState, 2500);
this.$emit('saveitem', this)
},
Parent
saveitem(e){
const data = {item : e}
axios.post(target, data, {headers . . .).then((response) => {}
})
The error disappeared when I instead fetched the index of the list item by doing so:
saveitem(e){
let item;
this.items.forEach( function(item, index, array) {
if(item.id == e.id) pointer = item
})
data.item = pointer
axios.post(target, data, {headers . . .).then((response) => {}
})
}
So I was also having this issue, but not for the reasons the accepted answer provided. It was occurring due to my Vuex store. After a lot of digging I discovered the cause was the presence of the "CreateLogger" plugin.
So if you're having this issue and it's not due to you watching an entire route, check if you're using the CreateLogger plugin in Vuex. That might be the culprit.
This happens for me when I pass this to a data object
data() {
return {
updateController: new UpdateController({
reportTo: this
})
}
}
This used to work fine with Vue 2 but causes this error in Vue 3.
Making this modification solved the problem for me.
data() {
return {
updateController: new UpdateController({
reportTo: () => this
})
}
}
I know this might be anti-pattern but I needed to inject partial reactivity to a non-reactive part of a JS library and this was the most not complicated way of achieving this that I can think of.
This happens to me when destructuring a ref without .value.
This was happening to me only in Firefox, and when I removed the Vue Dev Tools extension it stopped. After re-installing Vue dev tools it hasn't come back. Make sure you have the latest version of the Vue Dev Tools for your browser.
So I am coding a VueJS and ElectronJS template which can be found here: https://github.com/dev-aethex/electronjstemplate
My code works something like this,
Inside of my Vue component I access a global pre constructed class called MainProcessInterface and when it's constructed it first checks if vue is compiled for running in a development server. If it's in a dev server it will connect to the dev socket which electrons main process will host if electron is in dev mode and not compiled. This method seems to be working great, I had to use a socket because vue dev server is being loaded into electron via loadURL and so vue has no clue what ipcRenderer is. Inside the main process interface, if vue is compiled it will instead use the ipcRenderer.send() method. This is were the problem was born.
As soon as Vue runs thought the TS code, it sees ipcRenderer.send and freaks out while printing an error to the electron window console saying fs.existsSync does not exist or is defined.
I can't find a way around this. I though maybe i'll split MainProcessInterface into 2 peices, one for ipc and the other for websockets. Although it isn't a very good way, so before implementing it, I would like to know if there is a better more proper way of doing such.
I had a similar issue with React. Are you importing the ipcRenderer object somewhere in your build process? You might want to make sure it references the correct variable. I tried to drop this in as a comment but it wouldn't fit:
//index.html (index.ejs) for me... This is in the main HTML entry point
var IPC = null;
try {
IPC = require('electron').ipcRenderer;
console.log('IPC IS: ' + IPC)
} catch (err) {
console.log('CRICITCAL ERROR: IPC NOT ENABLED')
console.log(err)
IPC = null;
}
Then I initialize off that context in React with a startup here:
setTimeout(()=>{
console.log('----------------HACK FIRED POST REHYDRATE')
window.REDUX_STORE.dispatch(
(dispatch, getState) => {
const _state = getState()
if(window.IPC) {
if(_state.osc && _state.osc.on) {
dispatch( reconnectToEos() )
} else {
dispatch( updateStatus('[OSC Startup: DISCONNECTED]', ))
}
console.log('\t------------ELECTRON')
} else {
//Shut off OSC
dispatch( updateOscKey('on', false) )
dispatch( updateStatus('[WebApp, OSC disabled]', ))
console.log('\t------------WEB')
}
}
)
}, 1000)
Basically I'm using a global variable (window.IPC) to initialize my app so I don't import a bad variable in my build process. I have a fair number of Electron APIs where this alleviates the issues with building via Webpack.
I hope this helps!
I recently came across this blog post: Stop using Page Objects and Start using App Actions. It describes an approach where the application exposes its model so that Cypress can access it in order to setup certain states for testing.
Example code from the link:
// app.jsx code
var model = new app.TodoModel('react-todos');
if (window.Cypress) {
window.model = model
}
I'd like to try this approach in my VueJS application but I'm struggling with how to expose "the model".
I'm aware that it's possible to expose the Vuex store as described here: Exposing vuex store to Cypress but I'd need access to the component's data().
So, how could I expose e.g. HelloWorld.data.message for being accessible from Cypress?
Demo application on codesandbox.io
Would it be possible via Options/Data API?
Vue is pretty good at providing it's internals for plugins, etc. Just console.log() to discover where the data sits at runtime.
For example, to read internal Vue data,
either from the app level (main.js)
const Vue = new Vue({...
if (window.Cypress) {
window.Vue = Vue;
}
then in the test
cy.window().then(win => {
const message = win.Vue.$children[0].$children[0].message;
}
or from the component level
mounted() {
if (window.Cypress) {
window.HelloWorld = this;
}
}
then in the test
cy.window().then(win => {
const message = win.HelloWorld.message;
}
But actions in the referenced article implies setting data, and in Vue that means you should use Vue.set() to maintain observability.
Since Vue is exposed on this.$root,
cy.window().then(win => {
const component = win.HelloWorld;
const Vue = component.$root;
Vue.$set(component, 'message', newValue);
}
P.S. The need to use Vue.set() may go away in v3, since they are implementing observability via proxies - you may just be able to assign the value.
Experimental App Action for Vue HelloWorld component.
You could expose a setter within the Vue component in the mounted hook
mounted() {
this.$root.setHelloWorldMessage = this.setMessage;
},
methods: {
setMessage: function (newValue) {
this.message = newValue;
}
}
But now we are looking at a situation where the Cypress test is looking like another component of the app that needs access to state of the HelloWorld.
In this case the Vuex approach you referenced seems the cleaner way to handle things.
How do I access the main electron process fs module from within a renderer side module like a vue component running within the Quasar framework.
I've tried a few variations in a component with the following error:
const { app } = require('electron')
vue-router.esm.js?8c4f:1897 TypeError: fs.existsSync is not a function
const { app } = window.require('electron')
TypeError: window.require is not a function
After looking at what I could find through my friend Google, I am still searching for an answer on how to access the electron main process functions from within a vue component running under the quasar framework. Anyone... anyone? I've seen some github examples of file explorers, but based on the electron documentation it seems the implementation of just simply calling something like fs.readdirSync() should be a lot simpler than what I'm seeing in those implementations.
Your problem is explained in the Quasar docs
https://quasar.dev/quasar-cli/developing-electron-apps/node-integration
Quasar's suggestion is to use a preload script to attach the node APIs that you want in your renderer processes (ie: BrowserWindows) to the global window object.
https://quasar.dev/quasar-cli/developing-electron-apps/electron-preload-script
Attach preload script to BrowserWindow (Main Process)
src-electron/electron-main.js:
import path from 'path'
win = new BrowserWindow({
...
webPreferences: {
preload: path.resolve(__dirname, 'electron-preload.js')
}
})
Attach Node APIs to window global (Preload Script)
src-electron/electron-preload.js:
window.electron = require('electron')
Use Node API through the window global (Renderer Process)
somefile.vue
window.electron.ipcRenderer.sendSync(
'message',
payload
)
The answer was just beyond my understanding of how all these components are working together. Hopefully this will help someone else just coming up to speed on developing a Quasar/Vue/Electron app. If you launch your app/website using
quasar dev
you get a browser (renderer) that communicates with main electron process that cannot handle node main process stuff like:
const electron = require('electron')
const fs = require('fs')
const files = fs.readdirSync('/')
console.log(files)
I couldn't find a clear, concise and simple way. It appears there is a webpack config that can provide the same 'deep' integration, but I was looking for a more out of the box solution.
If you launch your app
quasar dev -m electron
You get deep integration and now can 'require()' or import the above modules within Vue components in your Quasar app.
const electron = require('electron')
I'm trying to run jest-snapshot tests in a Vue app created using the Vue-cli app. When I test a component that have a router-link in it, I receive the following warning.
[Vue warn]: Unknown custom element: <router-link> - did you register
the component correctly? For recursive components, make sure to
provide the "name" option.
Following the documentation here Unit tests with View Router I can stub out the router-link, but that doesn't pass my snapshot test. Has anyone run into this issue before?
Updated: A more consistent solution
tl;dr:
The solution I found that works consistently is to create a localVue (usually outside the scope of the describe() blocks) and add the RoutherLinkStub as a component.
import { mount, RouterLinkStub, createLocalVue } from '#vue/test-utils';
const localVue = createLocalVue();
localVue.component('router-link', RouterLinkStub);
const wrapper = mount(Component, { localVue });
The documentation doesn't make the solution obvious because it seems to be something of a hybrid of the page you linked to and this one about RouterLinkStub.
From that page:
import { mount, RouterLinkStub } from '#vue/test-utils'
const wrapper = mount(Component, {
stubs: {
RouterLink: RouterLinkStub
}
})
That solution works in most cases. If you have a case where you need to use mount and your router-link is a not in the component being tested but the one below it, you will still get that warning. Now, if you are in that situation, it's worth reflecting if you're writing your test properly (are you testing too much rather than a small unit?), but I have a situation or two where if I shallow instead of mount, the snapshot test is worthless because it renders children as <!----> when I callexpect(wrapper.html()).toMatchSnapshot()`.
A Note on Snapshot Tests:
I haven't actually seen examples of people using the Wrapper.html() method for snapshot testing, but it really seems like the best solution in my experimenting. vue-server-renderer is a long walk for what appears to be the same result. Accessing the vm property of Wrapper gives you access to the $el property, without much trouble, but it's just a more whitespace-heavy rendering of html().