Getting "simple" remote objects in electron - javascript

When I call a remote function in electron, the results are always littered with getters and setters. I think I understand why this is, but I'd like to be able to get simple objects.
My current solution is this:
import {remote} from 'electron'
const bridge = remote.require('bridge') // This is a little script I create for talking to a python process. Over stdin/stdout i.e. pipes.
bridge.on('fileTreeUpdate', (data) => {
myDataStore.update(JSON.parse(JSON.stringify(data.tree))
})
Is there a more elegant way to do this?

As mentioned in the comments, I would suggest to require 'bridge' directly in the Renderer; this way you don't need to pass its data between the Main and Renderer processes at all.
Having said that, if another requirement made it necessary to load the 'bridge' module in the Main process only, while still accessing its data from the Renderer, I'd suggest to use Electron's 'ipc' modules.
For instance, in your main process you could do something like this (assuming the variable win is a reference to the window of the Renderer process you wish to communicate with):
import bridge from 'bridge'
bridge.on('fileTreeUpdate', data => {
// assuming win {BrowserWindow} has already been initialized
win.webContents.send('bridge:fileTreeUpdate', data)
})
And in your Renderer processes (associated with win):
import ipcRenderer from 'electron'
ipcRenderer.on('bridge:fileTreeUpdate', (event, data) => {
myDataStore.update(data.tree)
})

Related

How do I import local json file as a starter as my in-memory resource in Express

I am working on a simple todo app with node.js express and I wanted to manipulate some resource in memory, instead of connecting to a database.
I have a local json file todo.json with some predefined data set and I wanted to use that as a starter and the CRUD operations are built on top of it.
So I have a function initializeTodos and a function getTodos
import { readFile } from 'fs/promises'
const initializeTodos = async () =>
JSON.parse(
await readFile(process.cwd() + '/src/resources/todo/todo.json', 'utf-8')
)
export const getTodos = async () => {
return initializeTodos()
}
then in each route handler I would call getTodos to get the todo list and perform crud operations on it. But now the issue is, every time I call getTodos it in turn calls initializeTodos and that gives me the json from the json file, which is static. That means any operations I perform after getTodos is not saved in memory and it is going to get reset every time I call getTodos
I guess I could write back to the disk for each crud operation but I really wanted to keep it simple here to just do it in memory. Is there a way I can achieve that?
But now the issue is, every time I call getTodos it in turn calls initializeTodos
Then don't call initializeTodos
You should load the file once at the start of your app and assign the data to a global variable that will be shared throughout your application. That will be your 'database' - all in memory
Then the updates and reads will be going to the same place so you will see updated results everytime i.e the routes will read write from that global variable
Then once you have this working - refactor the global variable out to its own class and call it ToDoInMemoryDb and hide all the access behind it to keep things clean. Global shared vars can lead you to learn bad habits
On app shutdown you can persist the latest value of the variable back to disk so the next time you have all the edits made

Use custom JavaScript code in a Vue.js app

I'm trying to insert JavaScript code in a Vue.js router app. I need to load data from the CMS the app is served from. In order to get the data from the CMS I have to use a JavaScript library from the CMS which is not made for Vue and is not exporting it's class/functions like modern JS. So I import the JS library from in the index.html by a script tag. This works as intended.
But now I have to use the class from this CMS JavaScript library.
Before writing this as a Vue-Router app I just have used Vue for templating purposes.
So I had some code packed in the window.onload event handler.
I have to create an instance for the CMS data access class.
But this leads to a build error (using vue-cli build). Since there
are no understandable error messages from the build process
I have to use trial and error. Even simple variable assignments like var a = 1 seem not to be allowed.
A console.log('something') works. But nothing else seemes to be allowed (except defining the onload-event handler)
I have added this code in a <script> Tag inside App.vue (which was created by vue-cli create)
window.onload = function() {
try {
// Instantiate class obj for CMS data access
cmsDataAccessObj = new CMSAccessData();
waitForPlayerData = true;
}
catch (e) {
console.error(e);
}
}
UPDATE
After testing the different solutions from the answers I got aware that using non-instance variables seems to cause the build errors.
This gives an error:
waitForPlayerData = true;
This works:
this.waitForPlayerData = true;
I wouldn't recommend using window.load to run your code. There are more native approaches to do this in Vue.js.
What you should do in the case you want to run it in the main component of the app before it's been loaded is to put the code inside the beforeCreate lifecycle hook of the main component.
...
beforeCreate () {
this.cmsDataLoader()
},
methods: {
cmsDataLoader () {
try {
// Instantiate class obj for CMS data access
cmsDataAccessObj = new CMSAccessData();
waitForPlayerData = true;
}
catch (e) {
console.error(e);
}
}
}
...
This will run the code everytime a component is created before the creation. You could also use the created lifecycle hook if you want to run it after the creation of the component.
Check the following link for more information about lifecycle hooks.
The best way to place JavaScript in Vue.js App is mounted function, it is called when the component is loaded:
export default {
name: "component_name",
mounted() {
let array = document.querySelectorAll('.list_item');
},
}
You don't need window.onload, you can just put whatever you want there. I'm not entirely certain when precisely in the lifecycle it renders and maybe someone can hop in and let us know but it for sure renders when page starts. (though it makes sense that it does before the lifecycle hooks even start and that it'll solve your needs)
Better & easier solution if you want to load it before Vue loads is to add it to the main.js file. You have full control there and you can load it before Vue initializes.
No need for window.onload there either, just put it before or import a JS file before you initialize Vue, because it's going to be initialized by order.

Write Vue plugin with custom options

I'm trying to write a vue plugin with custom options. I followed the official vue guidelines (https://v2.vuejs.org/v2/guide/plugins.html) on doing so but can't find a way to define custom options. These options should be read by normal javascript which then exports an object that is used by a vue component.
My folder structure is like this:
/src
factory.js
CustomComponent.vue
factory.js
import Vue from "vue";
import ImgixClient from "imgix-core-js";
var imgixClient = new ImgixClient({
domain: CUSTOM_OPTION_URL <-- important bit
domain: Vue.prototype.$imgixBaseUrl //tried it like this
});
export { imgixClient };
I already tried to set this custom bit by utilizing Vue.prototype in the install method like this but can't seem to get it working
export function install(Vue, options) {
if (install.installed) return;
install.installed = true;
Vue.prototype.$imgixBaseUrl = options.baseUrl;
Vue.component("CustomComponent", component);
}
I'm afraid this isn't going to be the simple answer you might have been hoping for... there's a lot to unpick here.
Let's start with factory.js. That is not a factory. It's a singleton. Singletons have problems around dependencies, configuration and the timing of instantiation and that's precisely the problem you're hitting. More on that later.
Now let's take a look at the plugin. First up, these two lines:
if (install.installed) return;
install.installed = true;
That shouldn't be necessary. Vue already does this automatically and should ensure your plugin is only installed once. Perhaps this came from an old tutorial? Take a look at the source code for Vue.use, there's not a lot to it:
https://github.com/vuejs/vue/blob/4821149b8bbd4650b1d9c9c3cfbb539ac1e24589/src/core/global-api/use.js
Digging into the Vue source code is a really good habit to get into. Sometimes it will melt your mind but there are some bits, like this, that aren't particularly difficult to follow. Once you get used to it even the more opaque sections start to become a little clearer.
Back to the the plugin.
Vue.prototype.$imgixBaseUrl = options.baseUrl;
It is not clear why you are adding this to the prototype.
I'm going to assume you are already familiar with how JavaScript function prototypes work.
Component instances are actually instances of Vue. So any properties added to Vue.prototype will be inherited by your components with almost no overhead. Consider the following simple component:
<template>
<div #click="onClick">
{{ $imgixBaseUrl }}
</div>
</template>
<script>
export default {
methods: {
onClick () {
const url = this.$imgixBaseUrl
// ...
}
}
}
</script>
As $imgixBaseUrl is an inherited property it is available within onClick via this.$imgixBaseUrl. Further, templates resolve identifiers as properties of the current Vue instance, so {{ $imgixBaseUrl }} will also access this.$imgixBaseUrl.
However, if you don't need to access $imgixBaseUrl within a component then there is no need to put it on the Vue prototype. You might as well just dump it directly on Vue:
Vue.imgixBaseUrl = options.baseUrl;
In the code above I've ditched the $ as there's no longer a risk of colliding with component instance properties, which is what motivates the $ when using the prototype.
So, back to the core problem.
As I've already mentioned, singletons have major problems around creation timing and configuration. Vue has its own built-in solution for these 'do it once at the start' scenarios. That's what plugins are. However, the key feature is that plugins don't do anything until you call install, allowing you to control the timing.
The problem with your original code is that the contents of factory.js will run as soon as the file is imported. That will be before your plugin is installed, so Vue.prototype.$imgixBaseUrl won't have been set yet. The ImgixClient instance will be created immediately. It won't wait until something tries to use it. When Vue.prototype.$imgixBaseUrl is subsequently set to the correct value that won't have any effect, it's too late.
One way (though not necessarily the best way) to fix this would be to lazily instantiate ImgixClient. That might look something like this:
import Vue from "vue";
import ImgixClient from "imgix-core-js";
var imgixClient = null;
export function getClient () {
if (!imgixClient) {
imgixClient = new ImgixClient({
domain: Vue.prototype.$imgixBaseUrl
});
}
return imgixClient;
}
So long as nothing calls getClient() before the plugin is installed this should work. However, that's a big condition. It'd be easy to make the mistake of calling it too soon. Besides the temporal coupling that this creates there's also the much more direct coupling created by sharing the configuration via Vue. While the idea of having the ImgixClient instantiation code in its own little file makes perfect sense it only really stands up to scrutiny if it is independent of Vue.
Instead I'd probably just move the instantiation to within the plugin, something like this:
import ImgixClient from "imgix-core-js";
export default {
install (Vue, options) {
Vue.imgixClient = Vue.prototype.$imgixClient = new ImgixClient({
domain: options.baseUrl
});
Vue.component("CustomComponent", component);
}
}
I've made a few superficial changes, using a default export and wrapping the function in an object, but you can ignore those if you prefer the way you had it in the original code.
If the client is needed within a component it can be accessed via the property $imgixClient, inherited from the prototype. For any other code that needs access to the client it can either be passed from the component or (more likely) grabbed directly from Vue.imgixClient. If either of these use cases doesn't apply then you can remove the relevant section of the plugin.

ES6 : optimizing the Command pattern

I'm building a kind of in-browser CLI for doing tests on a Game Engine.
The app looks like a console, with an input test and a submit button below a results sequence container DIV.
Behind the scene, there is an Invoker and numerous Commands objects :
// imports
import Invoker from '../cli/Invoker.js'
import Command1 from '../commands/Command1.js'
import Command2 from '../commands/Command2.js'
// ...
import Command20 from '../commands/Command20.js'
// instanciatiing
const invoker = new Invoker(context)
const commands = [
new Command1(),
new Command2(),
// ...
new Command20(),
]
// attach in invoker
commands.forEach(command => invoker.install(command))
the usage is pretty simple :
// later...
invoker.run('command20', { ...withArgs })
QUESTION 1/2:
Since this creates a bunch of imports, and initialization code, which could over time pollute the namespace of the client app code. How to fix it ? Builder pattern ? Facade ? Flyweight ? or something other ?
QUESTION 2/2
Rendering results : couldn't figure exactly where to put the code which take the effects of running a command, render (either with Mustache or JSX) and append it to the resulting DIV. Some comments ?
Help is welcome. Regards.
OK! the good answer is the Module Pattern. Not really one of the 23 GoF patterns, mostly a ES 2015 pattern. But no matters ! I still develop this way. Thanks !
For question 2/2 I intend to attach the render method to the resulting object -or- a render method per command bound to context after each command invocation...
[Considering this as SOLVED]
You can use a webpack's feature called context to do a bunch of imports
const files = require.context('.', false, /\.js$/);
const commands = {};
files.keys().forEach((key) => {
if (key === './index.js') return;
commands[key.replace(/(\.\/|\.js)/g, '')] = files(key).default;
});
export default commands;
and you can use namespaced as vuex
command1.js
export { actions: { get: {} }, namespaced: true }
app.js
context.dispatch('command1/get', param);
Concerning 2/2 you should go ahead and use Observer or PubSub.
Simply follow the modern days way and add a store to your project using redux/mobx (I prefer mobx for small/medium projects) or any other technology of your choice and push the changes to the store after the command execution. Handle the store changes as a separate process. If you're using React in can be done in no time.

how to make global variable and functions which can be accessible in all the components in angular 4

I am struggling with global variables as I want some variables which I need to access in all the components so what should i do in angular 4.
I've tried something like this:
Created one file with the name of global.ts and made a class with the name GlobalComponent something like this
export class GlobalComponent {
globalVar:string = "My Global Value";
}
and am using it on HeaderComponent by importing and making instance and it's working fine but this is very long process to import in each and every files to get it available.
So I want it to be available on all the components without importing it.
Is there any way or trick to achieve this? Any suggestion would be appreciated :)
As #bgraham is suggesting, it is definitely better to let angular injector to instantiate the service.
But you could also export just a simple object with key-value pairs, including functions. Then you can simply import it and use it (without the instantiation part).
globals.ts file:
export const GLOBALS = {
globalVar: 'My Global Value',
globalFunc: () => alert('123')
};
your.component.ts file:
import { GLOBALS } from './globals.ts';
console.log(GLOBALS.globalVar);
GLOBALS.globalFunc();
Btw I don't think there is a way how to get rid of the import statement - that is part of how typescript works...
I don't think what you want to do is possible and it's probably not a good idea.
The import statements are how webpack (or other bundlers) are able to build a tree to figure out which files to include. So it might be tricky to get global files built into all your bundles.
Also I would add, I'm not sure just importing the static file is the way to go either. It's kind of quick and dirty, which maybe is what you want I guess, but for production apps I would recommend making an angular service and injecting it.
export class GlobalVariablesService {
globalVar:string = "My Global Value";
}
This way you can mock these for unit tests or potentially pass in different variables depending on your changing needs in the future.
If you need these to update and push that into lots of components throughout your app, you might look into Redux. Its pretty handy for that kind of thing.
Sorry, perhaps not the answer you were hoping for
Simply create constant file - constant.ts under src folder.
Now import constant.ts file whenever you require parameter to be called
It will look like so
export const constant = {
COSNT_STRING : 'My Global Value',
}
how to use constant:
1) Import file.
2) constant.CONST_STRING
Also this is a good practice from future prospective, if you want to modify response just made change in one file not in 800 files.

Categories

Resources