I'm trying to use vue.js inside electron app but getting the following error:
Uncaught Exception: ReferenceError: document is not defined
at query (/Users/LM/Documents/mongoui/node_modules/vue/dist/vue.common.js:1070:10)
at Vue._initProps (/Users/LM/Documents/mongoui/node_modules/vue/dist/vue.common.js:7254:23)
at Vue._initState (/Users/LM/Documents/mongoui/node_modules/vue/dist/vue.common.js:7235:10)
at Vue._init (/Users/LM/Documents/mongoui/node_modules/vue/dist/vue.common.js:2394:10)
at new Vue (/Users/LM/Documents/mongoui/node_modules/vue/dist/vue.common.js:9000:8)
at Object. (/Users/LM/Documents/mongoui/main.js:11:1)
at Module._compile (module.js:425:26)
at Object.Module._extensions..js (module.js:432:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:313:12)
This is how I load vue.js inside main.js:
var Vue = require('vue');
new Vue({
el: "#app",
data: {
collections: [
{"name": "test 1"},
{"name": "test 2"},
{"name": "test 3"}
]
}
});
Given your error:
Uncaught Exception: ReferenceError: document is not defined at query
I would assume you're trying to use Vue inside of the Main Process, which unfortunately Vue wont be able to do without something like jsdom, since Vue depends on the document, and the main process doesn't have a document.
But, I assume the issue starts more fundamentally. You're probably wanting to use Vue from a Render Process, since that's where the document can be accessed.
Essentially, the main process in Electron is like the all-mighty controller, it is where you spawn and manage render processes. It doesn't reference to any singular DOM because no DOM exists in the main process. Instead, consider render processes, render processes are things like BrowserWindow, which can have a DOM.
So, with that information, we could try something like this:
main.js:
// import { app, BrowserWindow } from 'electron';
var electron = require('electron'),
app = electron.app,
BrowserWindow = electron.BrowserWindow;
app.on('ready', function() {
var main = new BrowserWindow({ /* ... */ });
main.loadURL('file://' + __dirname + '/index.html');
});
Then, from your render process:
index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example</title>
<script>
var Vue = require('vue');
new Vue({
el: "#app",
data: {
collections: [
{"name": "test 1"},
{"name": "test 2"},
{"name": "test 3"}
]
}
});
</script>
</head>
<body id='app'>
</body>
</html>
Of course you can reorganize the files however you want to, just remember to use Vue inside of the render process instead of the main process.
Edit 11/4/2016
Vue has server side rendering now too which you might want to look at.
Related
I have a JavaScript file with the service worker logic content request URL nested in a directory. I have the ability to and have configured the web server to serve that file with HTTP header Service-Worker-Allowed as "/".
The manifest file is also located in a directory reflecting the request URL. However, the scope and images are set assuming the same request URL is set to the main scope.
The actual request URL I see is set relative to the directory from the manifest, not the root directory I want to start from.
So this is the timeline of the browser:
First, request the website which returns the index file: https://localhost:3000.
index.html
<!DOCTYPE html>
<html lang="en-US">
<head>
<link rel="manifest" href="json/manifest.json" async>
<script id="js-webapp" type="module" src="js/webapp.js" defer async></script>
</head>
<body>
</body>
</html>
Second, it parses that and requests the main app logic: https://localhost:3000/js/webapp.js.
webapp.js
window.addEventListener('load', function (event) {
const path = '/js/sw.js'
if ('serviceWorker' in navigator) {
console.log('Service Worker present')
navigator.serviceWorker.register(path, { scope: './'})
.then((registration) => {
console.log('Service Worker Registered', registration)
})
.catch((err) => {
console.log('Service Worker Failed to Register: ', err)
})
}
})
Third, my app logic determines whether or not to load a service worker file based on the browser configuration: https://localhost:3000/js/sw.js. (This is the HTTP header that has response key-val "service-worker-allowed" and "/".
sw.js
const cacheName = 'v1.0.0'
const cacheFiles = [
'./manifest.json',
'./css/theme.css',
'./js/webapp.js',
'./img/logo.ico'
]
self.addEventListener('install', (event) => {
console.log('Installed')
event.waitUntil(
caches.open(cacheName).then((cache) => {
console.log('Caching cacheFiles')
return cache.addAll(cacheFiles)
})
)
})
Next, the service worker requests the manifest to cache the file: https://localhost:3000/json/manifest.json. (This was loaded on the main index file but nothing is done to this file without the service worker logic being triggered.)
manifest.json
{
"name": "Web App",
"short_name": "",
"start_url": "/",
"scope": "/",
"display": "standalone",
"display": "fullscreen",
"theme_color": "#404040",
"background_color": "#404040",
"icons": [
{
"src": "img/ic_launcher_48.png",
"sizes": "48x48",
"type": "image/png"
},
...
]
}
Following this logic, I would expect the URL request to be https://localhost:3000/img/ic_launcher_48.png. However, the request URL I see being performed by the browser is https://localhost:3000/json/img/ic_launcher_48.png.
What am I doing wrong if I already set to the service worker allowed HTTP in the header when I served the main file that installs, activates, and fetches service worker logic?
All other content that is loaded from other JavaScript, or HTML files load correctly.
the icons src is relative to where the manifest file is, since your manifest is at /json then the browser is looking for the icon at /json/img
The short question I have: based on the below code, why do I have to 'import' the components below twice to get my code to work?
I am working in a pretty locked-down environment, so cannot use Webpack or .vue SFCs at the moment, or npm (for all intents and purposes).
I've cobbled together a working version of a small vue app using typescript files, but am confused why it worked :S.
I have to import the component file, then require it as a component. I'd like to clean this up if I could, as we will be rolling this out as a P.O.C. with developers that are also just learning Vue, so I'd like to avoid bad practices at the start if I could.
index.ts
import * as Vue from "vue";
import * as Apple from "./App"; <-----
Vue.component('apple2', Apple.default); <----- wat?
let v = new Vue({
el: "#app",
components: { Apple}, <-----
template: `
<div>
<apple2/> <-----
</div>`,
data: {
name: "World"
},
});
App.ts
import * as Vue from "vue";
import * as fred from "./Hello"; <----
Vue.component('fred2', fred.default); <----
export default Vue.extend({
name: 'Apple',
template: `
<div>
<fred2 :name="name" :initialEnthusiasm="4"/> <-----
</div>`,
data() {
return { name: "World" }
},
components: { fred } <-----
});
Index.html
<!doctype html>
<html>
<head>
<script src="scripts/vue.min.js"></script>
<script data-main="scripts/build/index" src="scripts/lib/require.min.js">
</script></head>
<body>
<div id="app"></div>
</body>
tsConfig
{"compileOnSave": true,
"compilerOptions": {
"module": "amd",
"moduleResolution": "node",
"noImplicitAny": true,
"noEmitOnError": false,
"outDir": "./scripts/build",
"removeComments": false,
"sourceMap": true,
"target": "es5",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true
},
"exclude": [
"node_modules",
"wwwroot"
],
"include": [
"./scripts/**/*"
]
}
You're mixin up two different concepts, when you do this:
Vue.component('apple2', Apple.default);
You're actually registering the component definition object (Apple.default) with the name apple2 with the global Vue instance, making it available to all the components that are rendered by the previously referred Vue instance. In this case you could remove this part of your code in the index.ts:
components: { Apple}
And in theory your app should still work.
But because you're using typescript you can make your app work as if it was using a module system, allowing you to import the used sub-components in each parent component, allowing you to do something like this:
App.ts
export default const component = {
template: '<div>My component</div>'
}
index.ts
import Vue from 'vue';
import component from './App';
new Vue({
el: '#app',
components: {
'my-imported-component': component
}
});
And in your template:
<div id="app">
<my-imported-component/>
</div>
This would be, in my opinion a better approach because that you won't pollute the global Vue instance with all your components, but it's a matter of taste and what works for your scenario.
For more information take look at this link:
https://v2.vuejs.org/v2/guide/components-registration.html
We have an electron crypto app that signs transactions (among other things).
We want other websites to have the ability to have a button that opens that electron app, pre-filled with some params (the transaction information).
flow is:
user clicks "make transaction" on some-crypto-site.com
electron app opens up with pre-filled params
user clicks "sign transaction" in electron app
electron app does stuff behind the scenes
electron app closes and sends a message to some-crypto-site.com
This could be done at runtime, or install time.
What I tried (linux, chrome)
calling app.setAsDefaultProtocolClient with the code of this gist, which is basically:
app.setAsDefaultProtocolClient("my-app")
But after I put my-app://foo?bar=baz in chrome browser, I get the following popup, and pressing open-xdg does nothing (other than dismissing the popup)
I looked into
Electron protocol api which seems to handle in-app protocols only
webtorrent .desktop file This might be the way to go, I'm just not sure how to go about it.
Maybe there's a way to do so at install time through electron builder?
Thanks in advance for the help, I have no idea how to proceed here!
Resources that might be useful
github repo with mac+window example
github comment for linux
github comment for linux 2
SO answer for all 3 OSs
SO windows answer
npm package for windows registery
SO mac answer
SO linux answer
microsoft docs for windows
windows article
github comment for windows
github comment for mac
info.plst for mac
old repo for mac and win
Since this may be relevant to what I’m doing at work, I decided to give it a go.
I’ve only tested this on OSX though!
I looked at the documentation for app.setAsDefaultProtocolClient and it says this:
Note: On macOS, you can only register protocols that have been added to your app's info.plist, which can not be modified at runtime. You can however change the file with a simple text editor or script during build time. Please refer to Apple's documentation for details.
These protocols can be defined when packaging your app with electron-builder. See build:
{
"name": "foobar",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron .",
"dist": "electron-builder"
},
"devDependencies": {
"electron": "^3.0.7",
"electron-builder": "^20.38.2"
},
"dependencies": {},
"build": {
"appId": "foobar.id",
"mac": {
"category": "foo.bar.category"
},
"protocols": {
"name": "foobar-protocol",
"schemes": [
"foobar"
]
}
}
}
In your main thread:
const {app, BrowserWindow} = require('electron');
let mainWindow;
function createWindow () {
mainWindow = new BrowserWindow({width: 800, height: 600})
mainWindow.loadFile('index.html');
}
app.on('ready', createWindow);
var link;
// This will catch clicks on links such as open in foobar
app.on('open-url', function (event, data) {
event.preventDefault();
link = data;
});
app.setAsDefaultProtocolClient('foobar');
// Export so you can access it from the renderer thread
module.exports.getLink = () => link;
In your renderer thread:
Notice the use of the remote API to access the getLink function exported in the main thread
<!DOCTYPE html>
<html>
<body>
<p>Received this data <input id="data"/></p>
<script>
const {getLink} = require('electron').remote.require('./main.js');
document.querySelector('#data').value = getLink();
</script>
</body>
</html>
Example
open in foobar
This also allows you to launch from the command line:
open "foobar://xyz=1"
How do you get back to the original caller?
I suppose that when you launch the app you could include the caller url:
<a href="foobar://abc=1&caller=example.com”>open in foobar</a>
When your electron app finishes processing data, it would simply ping back that url
Credits
Most of my findings are based on:
From this GitHub issue
And the excellent work from #oikonomopo
All little bit different from above.
open-url fires before the ready event so you can store it in a variable and use within the widow did-finish-load.
let link;
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1280,
height: 720,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
});
mainWindow.openDevTools();
mainWindow.setContentProtection(true);
mainWindow.loadFile('index.html');
mainWindow.webContents.on("did-finish-load", function() {
mainWindow.webContents.send('link', link);
});
}
app.on('ready', createWindow);
// This will catch clicks on links such as open in foobar
app.on('open-url', function(event, url) {
link = url;
if (mainWindow?.webContents) {
mainWindow.webContents.send('link', link);
}
});
app.setAsDefaultProtocolClient('protocols');
You can then use the value in your render html like this.
<!DOCTYPE html>
<html>
<head></head>
<body>
<script>
const ipc = require("electron").ipcRenderer;
ipc.on("link", function (event, url) {
console.log(url);
console.log(parseQuery(decodeURI(url)));
});
function parseQuery(queryString) {
queryString = queryString.substring(queryString.indexOf("://") + 3);
var query = {};
var pairs = (queryString[0] === "?" ? queryString.substr(1) : queryString).split("&");
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split("=");
query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || "");
}
return query;
}
</script>
</body>
</html>
When I install my web app to homescreen on Android without a service worker, everything works as expected. I can click my home screen icon and my app launches with a splash screen and then I see my app in fullscreen mode. When I add in the service worker registration code and install to home screen, my app launches in a browser window and seems to ignore my manifest file.
Something to note is that without the service worker my app only has an icon on the homescreen. with the service worker code it becomes fully installed with the "new improved add to home screen" and there is an icon on my home screen as well as in my installed apps panel.
Here is my code:
web-app.html:
<!DOCTYPE html>
<html>
<head>
<title>Web App</title>
<meta name = "viewport" content = "user-scalable=no, width=device-width">
<meta name = "mobile-web-app-capable" content = "yes">
<link href = "manifest.json" rel = "manifest">
<link href = "web-app.css" rel = "stylesheet" type = "text/css">
<link href = "web-app.png" rel = "icon" type = "image/png">
</head>
<body>
<h1>Android Web App!</h1>
<img src = "/web-app.png">
<p>This page can be viewed in any browser, but it can also work in a web app! If you are viewing this page in a full screened webview on your mobile device, you are looking at a fully functional web app! You can use this technology to better connect with your users or create a full screen mobile experience for your HTML5 games!</p>
<script type = "text/javascript">
navigator.serviceWorker.register("web-app-service.js");
</script>
</body>
</html>
manifest.json:
{
"author": "PoP Vlog",
"background_color": "#ffffff",
"description": "Progressive Web App Example with Offline Mode",
"display": "fullscreen",
"icons": [{
"src": "/web-app.png",
"sizes": "192x192",
"type": "image/png"
}],
"lang":"en",
"manifest_version": 2,
"name": "Web App",
"orientation": "portrait",
"scope":"/",
"short_name": "Web App",
"start_url": "/",
"theme_color": "#ffffff",
"version": "0.2"
}
web-app-service.js:
self.addEventListener("install", function(event) {
event.waitUntil(caches.open("web-app").then(function(cache) {
return cache.addAll([ "/", "/web-app.html", "/web-app.css", "/web-app.png"]).then(function() {
self.skipWaiting();
});
}));
});
self.addEventListener("activate", function(event) {
event.waitUntil(self.clients.claim());
});
self.addEventListener("fetch", function(event) {
event.respondWith(caches.match(event.request).then(function(response) {
return response || fetch(event.request);
}));
});
I couldn't find a lot of documentation on this, but clearly the behavior changes when I remove the service worker registration code from my html document. I suspect that the problem lies in my web-app-service.js file.
EDIT: 11/01/2017
When I completely comment out my fetch event listener, my app works as expected, but add to home screen only installs an icon on my home screen. When I add to home screen with the fetch event listener, I get a full install of my web app into my apps panel and my app only opens in a browser window with full url bar and everything. This problem has something to do with adding the fetch capability to my service worker.
EDIT 11/08/2017
I have discovered that I only have this problem on my Node JS HTTPS test server when accessing my web app through a local IP address. The web app works fine when I run it from my Github Pages site. This leads me to believe it is a problem with scope in the app manifest or perhaps my Node server.
In manifest.json change "display": "fullscreen" to "display": "standalone".
It'll launch your app in App like view.
For more info refer https://developer.mozilla.org/en-US/docs/Web/Manifest
I have Chrome extension that loads jquery-1.8.3.min.js and jquery-ui.js and jquery-ui-base64.css into the content script .
i use them in the content script NOT background script .
i set the configuration ( i think ) right but when i see in the console i getting errors
i can see the icons in the windows just fine , but i still getting the errors in the Chrome window.
is it a bug in chrome im using version 23.0.1271.95 m?
this is the manifist :
{
"name":"Sample communication from content to background",
"description":"This is a sample for Simulating communication from content to background",
"manifest_version":2,
"version":"2",
"background":{
"scripts":["background.js"]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["jquery-1.8.3.min.js","jquery-ui.js","client.js"],
"run_at":"document_end",
"all_frames": true,
"css":["jquery-ui-base64.css"]
}
],
"web_accessible_resources": [
"client.js","jquery-1.8.3.min.js","jquery-ui.js","jquery-ui-base64.css",
"images/ui-bg_flat_0_aaaaaa_40x100.png",
"images/ui-bg_flat_75_ffffff_40x100.png",
"images/ui-bg_glass_55_fbf9ee_1x400.png",
"images/ui-bg_glass_65_ffffff_1x400.png",
"images/ui-bg_glass_75_dadada_1x400.png",
"images/ui-bg_glass_75_e6e6e6_1x400.png",
"images/ui-bg_glass_95_fef1ec_1x400.png",
"images/ui-bg_highlight-soft_75_cccccc_1x100.png",
"images/ui-icons_222222_256x240.png",
"images/ui-icons_2e83ff_256x240.png",
"images/ui-icons_454545_256x240.png",
"images/ui-icons_888888_256x240.png",
"images/ui-icons_cd0a0a_256x240.png"
],
"permissions": [
"unlimitedStorage",
"http://*/",
"<all_urls>",
"tabs"
]
}
in the jquery-ui-base64.css i changed all the imags url load to something like this :
url(chrome-extension://__MSG_##extension_id__/chrome-extension://__MSG_##extension_id__/images/ui-bg_flat_75_ffffff_40x100.png)
url(chrome-extension://__MSG_##extension_id__/chrome-extension://__MSG_##extension_id__/images/ui-bg_highlight-soft_75_cccccc_1x100.png)
but still im getting the errors:
Denying load of chrome-extension://mmoccjinakdjcmhjdjghhjnihbfkkgkp/chrome-extension://mmoccjinakdjcmhjdjghhjnihbfkkgkp/images/ui-bg_flat_75_ffffff_40x100.png. Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension.
Denying load of chrome-extension://mmoccjinakdjcmhjdjghhjnihbfkkgkp/chrome-extension://mmoccjinakdjcmhjdjghhjnihbfkkgkp/images/ui-bg_highlight-soft_75_cccccc_1x100.png. Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension.
the images are there in the images dir and i can see the icons in the JQuery dialog i created.
EDIT 1)
The following code works for all background\extension related DOM and css
manifest.json
Simple json structure with all permissions defined
{
"name": "My extension",
"version": "1.0",
"permissions": [
"http://*/*", "tabs", "https://*/*"
],
"browser_action": {
"default_icon": "icon.jpg",
"default_popup":"popup.html"
},
"manifest_version": 2
}
popup.html
Linked style sheet for Browser action Popup
<html>
<head>
<link rel="stylesheet" href="styles.css"></link>
</head>
<body>
</body>
</html>
styles.css
used url() for image path
body{
width : 500px;
height: 500px;
background-image: url('img/icon.jpg');
}
Let me know if it still fails
EDIT 2)
For Injecting Images through content stuff
Solution a)
Using this converter, you convert your image to base64 strings and you can use them as
{ background-image: url(data:image/png;base64,iVBORw ........ };
Solution b)
The following code will not work because
{
background-image:url(chrome.extension.getURL('img/icon.jpg'));
}
chrome.extension.getURL() is undefined for css.
So, i used js for injection of background-images\any image URL's(Because they have dynamic URL's)
manifest.json
Simple json structure with all permissions defined for content scripts and css
{
"name": "My extension",
"version": "1.0",
"permissions": [
"http://*/*", "tabs", "https://*/*"
],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js":["content.js"],
"css": ["styles.css"]
}
],
"web_accessible_resources": [
"img/icon.jpg"
],
"manifest_version": 2
}
content.js
As a trivial use case prepared a div and added background Image property
var newdiv = document.createElement('div');
newdiv.setAttribute("id", "moot450");
document.body.appendChild(newdiv);
document.getElementById('moot450').style.backgroundImage = "url(" + chrome.extension.getURL('img/icon.jpg') + ")";
styles.css
injected another css for refining injected div
#moot450{
position:absolute;
width:500px;
height:500px;
/*background-image:url(chrome-extension://faaligkhohdchiijdkcokpefpancidoo/img/icon.jpg);*/
}
OUTPUT
Screen shot taken from Google Page after injection
Let me know if you need more information or if it fails.