I'm having a problem trying to build my application. When running in dev mode, it works perfectly, but when I build it it gives window is not defined.
export function socketConnect (): SocketI {
if (typeof window.gqlsocket === 'undefined') {
const socket = initializeSocket()
window.gqlsocket = socket
return socket
} else {
return window.gqlsocket
}
}
export function socketDisconnect (): void {
if (typeof window.gqlsocket !== 'undefined') {
window.gqlsocket.disconnect(() => (window.gqlsocket = undefined))
}
}
const client = createClient({...})
export const install: ModuleInstall = ({ app, isClient }) => {
if (!isClient) return
app.use(urql, client)
}
Add to the index.html :
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<!-- 👇 -->
<script>
var global = global || window;
</script>
Related
Im creating a custom directive as follows in main.ts .
let handleOutsideClick: any;
app.directive("closable", {
mounted: (el, binding, vnode) => {
handleOutsideClick = (e: any) => {
e.stopPropagation();
const payload = binding.value;
console.log(`instance: ${Object.getOwnPropertyNames(binding.instance)}`);
};
document.addEventListener("click", handleOutsideClick);
},
unmounted: (el) => {
document.removeEventListener("click", handleOutsideClick);
},
});
Inside the event handler i want to make a call to a function on the component that triggered this directive.
With Vue 2 you could do it with vnode.context'myfunction' but this does not seem to work with binding.instance.
How can i call the function using the binding instance?
Passing the function to be called as the binding's value and then calling it seems to work:
if (typeof binding.value === 'function') {
binding.value()
}
Working example:
const { createApp } = Vue;
const app = createApp({
setup() {
return { test: () => console.log('here') }
}
})
app.component('demo', {
template: `<div>test</div>`
})
let handleOutsideClick;
app.directive("closable", {
mounted: (el, binding, vnode) => {
handleOutsideClick = (e) => {
e.stopPropagation();
const payload = binding.value;
console.log(`instance: ${Object.getOwnPropertyNames(binding.instance)}`);
if (typeof binding.value === 'function') {
binding.value()
}
};
document.addEventListener("click", handleOutsideClick);
},
beforeUnmount: (el) => {
document.removeEventListener("click", handleOutsideClick);
},
});
app.mount('#app')
<script src="https://unpkg.com/vue#3.2.41/dist/vue.global.prod.js"></script>
<div id="app">
<demo v-closable="test"></demo>
</div>
Notes:
vue internal method names change based on environment. For example, if you remove .prod from the vue link in the example above, you'll get more data out of Object.getOwnPropertyNames(binding.instance).
If your app's business logic relies on vue internals method naming:
 a) you're doing it wrong™;
 b) it won't work in production
if the above is not helpful, please provide more details on your specific use-case via a runnable minimal, reproducible example.
If you need a multi-file node-like online editor, note codesandbox.io allows importing local projects using their CLI utility.
I'm trying to develop microfrontend with Vuejs and Module Federation. So, i have method loadModule, that returns me a an object(maybe component), but what i should do with it i don't know. I tried to import this component like defineAsyncComponent but it's useless.
And i'm tried use the render function in this object and paste this it to markup.
The screenshot shows what the object I get, but I don’t know what to do next with it and how to register this like component.
I'm stumped, any advice would be welcome
<script setup>
import { ref, onMounted, defineAsyncComponent, h } from 'vue'
import TestComp from './TestComp.vue'
let Header
const modules = [
{
protocol: 'http',
host: 'localhost',
port: 8080,
moduleName: 'Company',
fileName: 'remoteEntry.js',
componentNames: ['Header'],
},
]
onMounted(() => {
modules.forEach((uiApplication) => {
const remoteURL = `${uiApplication.protocol}://${uiApplication.host}:${uiApplication.port}/${uiApplication.fileName}`
const { componentNames } = uiApplication
const { moduleName } = uiApplication
const element = document.createElement('script')
element.type = 'text/javascript'
element.async = true
element.src = remoteURL
element.onload = () => {
componentNames?.forEach((componentName) => {
const component = loadComponent(moduleName, `./${componentName}`)
component().then((res) => {
Header = res
console.log(res)
// what should i do next?
// This is not working
// Header = defineAsyncComponent(() => import('Company/Header'))
})
})
}
document.head.appendChild(element)
})
})[enter image description here][1]
function loadComponent(scope, module) {
return async () => {
// Initializes the shared scope. Fills it with known provided modules from this build and all remotes
await __webpack_init_sharing__('default')
const container = window[scope] // or get the container somewhere else
// Initialize the container, it may provide shared modules
await container.init(__webpack_share_scopes__.default)
const factory = await window[scope].get(module)
const Module = factory()
return Module
}
}
const remoteImport = async (location, name, options) => {
const module = await importResource(location, options)
if (name) {
let m = module[name]
if (!m) {
throw new Error(
`No component named ${name} founded in component ${location}`
)
}
return module[name]
} else {
return module
}
}
</script>
<template>
<div>
<Header ref="Header" title="Test shop" />
<h2>Our Shop Page</h2>
</div>
</template>
I am building a project with Nuxt and I need to know the size of the wrapper to adjust the grid setting
(I want a single line, I could still do this in pure CSS probably by hiding the items)
It's my first time using composition API & script setup
<script setup>
const props = defineProps({
name: String
})
const width = ref(0)
const wrapper = ref(null)
const maxColumns = computed(() => {
if (width.value < 800) return 3
if (width.value < 1000) return 4
return 5
})
onMounted(() => {
width.value = wrapper.value.clientWidth
window.onresize = () => {
width.value = wrapper.value.clientWidth
console.log(width.value);
};
})
</script>
<template>
<div class="category-preview" ref="wrapper">
...
</div>
</template>
The console log is working properly, resizing the window and refreshing the page will return 3, 4 or 5 depending on the size, but resizing won't trigger the computed value to change
What am I missing ?
In my test enviroment I had to rename your ref 'width' into something else. After that it did worked for me with a different approach using an event listener for resize events.
You can do something like this:
<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue'
const wrapperWidth = ref(0)
const wrapper = ref(null)
// init component
onMounted(() => {
getDimensions()
window.addEventListener('resize', debounce(() => getDimensions(), 250))
})
// remove event listener after destroying the component
onUnmounted(() => {
window.removeEventListener('resize', debounce)
})
// your computed property
const maxColumns = computed(() => {
if (wrapperWidth.value < 800) {
return 3
} else if (wrapperWidth.value < 1000) {
return 4
} else {
return 5
}
})
// get template ref dimensions
function getDimensions () {
const { width } = wrapper.value.getBoundingClientRect()
wrapperWidth.value = width
}
// wait to call getDimensions()
// it's just a function I have found on the web...
// there is no need to call getDimensions() after every pixel have changed
const debounce = (func, wait) => {
let timeout
return function executedFunction (...args) {
const later = () => {
timeout = null
func(...args)
}
clearTimeout(timeout)
timeout = setTimeout(later, wait)
}
}
</script>
<template>
<div ref="wrapper">
{{ maxColumns }} // will change after resize events
</div>
</template>
Based on this question Detect click outside element and this answer https://stackoverflow.com/a/42389266, I'm trying to migrate the directive from Vue 2 to Vue 3. It seems that binding.expression and vnode.context not exists more. How can I make it work?
app.directive('click-outside', {
beforeMount (el, binding, vnode) {
el.clickOutsideEvent = function (event) {
if (!(el === event.target || el.contains(event.target))) {
vnode.context[binding.expression](event);
}
};
document.body.addEventListener('click', el.clickOutsideEvent);
},
unmounted (el) {
document.body.removeEventListener('click', el.clickOutsideEvent);
}
});
You can use binding.value instead like this:
const { createApp } = Vue;
const highlightEl = (color ) => (event, el) => {
if (el) {
el.style.background = color;
} else {
event.target.style.background = color;
}
}
const clearHighlightEl = (event, el) => {
if (el) {
el.style.background = '';
} else {
event.target.style.background = '';
}
}
const app = Vue.createApp({
setup() {
return {
highlightEl,
clearHighlightEl
}
}
})
app.directive('click-outside', {
mounted(el, binding, vnode) {
el.clickOutsideEvent = function(event) {
if (!(el === event.target || el.contains(event.target))) {
binding.value(event, el);
}
};
document.body.addEventListener('click', el.clickOutsideEvent);
},
unmounted(el) {
document.body.removeEventListener('click', el.clickOutsideEvent);
}
});
app.mount('#app')
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app">
<h1 v-click-outside="highlightEl('yellow')" #click="clearHighlightEl">Element 1</h1>
<p v-click-outside="highlightEl('#FFCC77')" #click="clearHighlightEl">Element 2</p>
</div>
out of the context, there's an easier way in vue3 with composition.
Link to Vueuse ClickOutside (Vue 3)
Link to Vueuse ClickOutside(Vue 2)
<template>
<div ref="target">
Hello world
</div>
<div>
Outside element
</div>
</template>
<script>
import { ref } from 'vue'
import { onClickOutside } from '#vueuse/core'
export default {
setup() {
const target = ref(null)
onClickOutside(target, (event) => console.log(event))
return { target }
}
}
</script>
you can use ref to find out if the element contains the element clicked
<template>
<div ref="myref">
Hello world
</div>
<div>
Outside element
</div>
</template>
<script>
export default {
data() {
return {
show=false
}
},
mounted(){
let self = this;
document.addEventListener('click', (e)=> {
if (self.$refs.myref !==undefined && self.$refs.myref.contains(e.target)===false) {
//click outside!
self.show = false;
}
})
}
}
</script>
vue2 solution:
<script>
export default {
name: 'onClickOutside',
props: ['clickOutside'],
mounted() {
const listener = e => {
if (e.target === this.$el || this.$el.contains(e.target)) {
return
}
this.clickOutside()
}
document.addEventListener('click', listener)
this.$once('hook:beforeDestroy', () => document.removeEventListener('click', listener))
},
render() {
return this.$slots.default[0]
},
}
</script>
vue3:
<script>
import { getCurrentInstance, onMounted, onBeforeUnmount, ref, defineComponent } from 'vue'
export default defineComponent({
name: 'OnClickOutside',
props: ['clickOutside'],
setup(props, { emit, attrs, slots }) {
const vm = getCurrentInstance()
const listener = event => {
const isClickInside = vm.subTree.children.some(element => {
const el = element.el
return event.target === el || el.contains(event.target)
})
if (isClickInside) {
console.log('clickInside')
return
}
props.clickOutside && props.clickOutside()
}
onMounted(() => {
document.addEventListener('click', listener)
})
onBeforeUnmount(() => {
document.removeEventListener('click', listener)
})
return () => slots.default()
},
})
</script>
I'm using the electron-vue boilerplate, and I want to make my mainWindow a fullScreen after clicking a button.
Electron Window API: electron.atom.io/docs/api/browser-window
HTML:
<button #click="setFullScreen">Switch to Full Screen</button>
Vue:
export default {
name: 'mainComponent',
methods: {
setFullScreen: function() {
mainWindow.setFullScreen(true);
}
}
This is not working. How can I use the Electron API in electron-vue?
and the index.js:
'use strict'
import { app, BrowserWindow } from 'electron'
let mainWindow
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:${require('../../../config').port}`
: `file://${__dirname}/index.html`
function createWindow () {
/**
* Initial window options
*/
mainWindow = new BrowserWindow({
height: 728,
width: 1024,
fullscreen: false,
})
mainWindow.loadURL(winURL)
mainWindow.on('closed', () => {
mainWindow = null
})
// eslint-disable-next-line no-console
console.log('mainWindow opened')
}
app.on('ready', createWindow)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (mainWindow === null) {
createWindow()
}
})
you will find it as it is in electron-Vue
the picture shows How the Structure of the folder
enter image description here
mainWindow is not available in your Vue code because it is defined in your main process.
In your single file component, however, you can import remote from electron, where you can get access to the current window. So your code would look something like this.
const {remote} = require("electron")
export default {
name: 'mainComponent',
methods: {
setFullScreen: function() {
remote.getCurrentWindow().setFullScreen(true);
}
}
}