I have an Electron app with 2 modules, one of them being the standard Menu. When I click a menu item, I want it to call an instantiated module's function.
The only solution I've found is to have my instantiated object being a property of the main electron.app object, which is globally available.
Here's my example:
main.js
const electron = require('electron');
const app = electron.app;
const WindowManager = require('components/WindowManager');
let windowManager = new WindowManager(); // <- I want my menu item to call a function from this object
const MainMenu = require('components/MainMenu');
let mainMenu = new MainMenu();
function initApp() {
let menuTemplate = mainMenu.getTemplate();
let menuBuilt = electron.Menu.buildFromTemplate(menuTemplate);
electron.Menu.setApplicationMenu(menuBuilt);
}
function mainTestFileOpen() {
console.log('File open test function in main.js');
}
// I'm trying to avoid doing this
app.testFileOpen = function() {
console.log('Function is part of "app" so globally accessible...');
}
// I'm trying to avoid doing this too
app.appWindowManager = new WindowManager();
// Start the app
app.on('ready', initApp);
components/WindowManager.js
class WindowManager {
constructor() {
this.doFileOpen = this.doFileOpen.bind(this);
}
doFileOpen() {
console.log('File open from WinwdowManager module');
}
}
module.exports = WindowManager;
components/MainMenu.js
const electron = require('electron');
class MainMenu {
constructor() {
this.template = [];
this.init = this.init.bind(this);
this.getTemplate = this.getTemplate.bind(this);
// Initialize
this.init();
}
getTemplate() {
return this.template;
}
init() {
this.template = [{
label: 'File',
submenu: [{
label: "Open File",
click() {
/** Calling a function in main.js does NOT work **/
mainTestFileOpen();
/** Calling an object in main.js doe NOT work **/
windowManager.doFileOpen();
/** If the function is part of "app" then it works **/
electron.app.testFileOpen();
/** If the instantiated object is part of "app" then it works **/
electron.app.appWindowManager.doFileOpen();
}
}]
}]
}
}
module.exports = MainMenu;
I think what I am not getting is the scope of click() in an Electron Menu template.
You're trying to call a function from a different module (every file in Node is its own module, which is different from the typical JS environment) without first importing the module.
It's not enough to just write a function in the main.js module:
function mainTestFileOpen() {
console.log('File open test function in main.js');
}
And expect to call it from the MainMenu.js module. You must first export it:
export function mainTestFileOpen() { ... }
Then, in MainMenu.js, you can import it at the top:
import { mainTestFileOpen } from "../main";
Same thing with windowManager. It doesn't look like you're doing anything with WindowManager from main.js, so just move the import and instantiation to MainMenu.js:
import { WindowManager } from "./WindowManager";
let windowManager = new WindowManager();
And then you'll be able to do:
windowManager.doFileOpen();
Side Note:
You do stuff like this in your constructor: this.doFileOpen = this.doFileOpen.bind(this);
There is no need for this as the only way somebody could call doFileOpen is by calling it on the windowManager instance like so: windowManager.doFileOpen(...).
The same applies to:
this.init = this.init.bind(this);
this.getTemplate = this.getTemplate.bind(this);
Related
I'm quite new to Javascript. I had to switch my project to type "Module" because an import required it. However it totally broke my code.
I am using an AutoLoader to load in all my created classes:
const modules = new Autoloader(path.resolve('./modules'), {
ignore: [
'command.js',
'service.js',
'event.js'
]
});
Normally when I printed out modules it would look like:
{ commands: { invite: class Invite extends Command } }
Now it doesnt recognize the class any more:
{ commands: { invite: undefined } }
The autoloader loads all the classes out (for example) my commands directory.
const api = require('../../api');
const Command = require('./command');
class Invite extends Command {
prefix = '/invite';
handle(message) {
let args = this.stripWithoutLower(message);
if (message.isGroupMsg !== this.groupOnly) {
return;
}
if (typeof args[1] !== 'undefined') {
let inviteCode = args[1].split('/');
if (typeof inviteCode[3] !== 'undefined') {
this.client.joinGroup(inviteCode[3]);
}
}
}
}
module.exports = Invite;
Normally I would use module.exports to export the class. Inside my main.js file I would them loop through the classes I've autoloaded to initiate them. For example:
const commandList = [];
Object.keys(modules['commands']).forEach(key => {
commandList.push(new modules['commands'][key](client));
});
The above coded was working until I had to switch everything to type "Module". Im very new to javascript forgive me about any mis typings.
I've read online that I had to switch module.exports to exports.default = Invite; or to export Class. Unfortunately both didn't worked.
What error am I getting:
TypeError: modules.commands[key] is not a constructor
I used to implement singleton this way:
class MySomething {
constructor(props) {}
}
let myInstance = null;
module.exports = (props) => {
//first time call
if(props) {
myInstance = new MySomething (props);
return myInstance;
} else {
return myInstance;
}
this assumes that at app.js (entry file) I will call first:
require('./MySomething')(props)
then everywhere in the project I use:
const instanceOfSomething = require('./MySomething')();
I discovered that every time I got a new instance!
What's wrong in my code?
I tried also this way:
class MySomething {...}
const mySomething = (function() {
let myInstance = null;
return {
init: function() {
myInstance = new MySomething();
},
getInstance: function() {
return myInstance ;
}
}
})();
module.exports = mySomething;
and I got the some problem when importing this module from different files, anyone can explain to me?
every require of file create new instance of mySomething
UPDATE
I tried this example now:
class MySomething {...}
const mySomething = {
myInstance: null,
init: function() {
myInstance = new MySomething();
},
getInstance: function() {
return myInstance ;
}
}
};
module.exports = mySomething;
The same problem happened, maybe it is related to my project structure, I will explain it here:
the code below belongs to module "facts"
facts folder contains a folder named "dao" this folder contains MySomething.js (the singleton)
in the facts/index.js I have:
const Localstorage = require('./dao/MySomething');
exports.init = (path) => {
Localstorage.init(path)
}
exports.Localstorage = Localstorage;
Now in a folder named "core" which contains the "facts" folder I re-exported the Localstorage again in "index.js" like this:
const facstModule = require('./facts');
exports.Localstorage = facstModule.Localstorage;
Then in "schedule" folder which contains "Runtime.js" within I write:
const { Localstorage } = require('../core');
setTimeout(() => {
const localstorageIns = Localstorage.getInstance(); //this is always null!
}, 5000)
In app.js file (entry point) I did:
const facts = require('./src/facts');
facts.init(__dirname);
Normally instance will be created before the timeout execute the callaback,
But I noticed that there two instance of Localstorage which is singleton
the cleanest way to do a singleton is
class MyClass () { ... }
module.exports = new MyClass()
if you need a singleton that gets instantiated once, I would do:
class MyClass () { ... }
let myClass
const MyClassSingleton = (...args) => {
if (myClass) {
return myClass
}
myClass = new MyClass(...args)
return myClass
}
module.exports = MyClassSingleton
Every require of file create new instance of mySomething because every time you return new object with init method and getInstance method.
If you need singleton you need do like this:
class MySomething {
constructor() {
if (!MySomething.instance) {
MySomething.instance = this;
}
}
getInstance: function() {
return MySomething.instance;
}
}
module.exports = MySomething;
class Singleton {
constructor() {
this.my_obj;
}
static makeObject() {
if (!this.my_obj) {
this.my_obj = new Singleton();
}
return this.my_obj;
}
add() {
return 1
}
}
// so to get the object we need to call the makeobject method
const obj = Singleton.makeObject()
console.log(obj.add());
Are there any advantages/disadvantages to instantiate a module object on the module's file instead of instantiaing the module object on the file that uses it?
Example of Instantiating module on the module's file:
module.js
const someModule = (function() {
const doSomething = function(id) {
console.log("doing something");
};
return {
doSomething: doSomething
};
})();
export someModule;
main.js
import { someModule } from "./module";
someModule.doSomething();
Example of instantiating module on the file that uses it:
module.js
const someModule = function() {
const doSomething = function(id) {
console.log("doing something");
};
return {
doSomething: doSomething
};
};
export someModule;
main.js
import { someModule } from "./module";
someModuleInstance = new someModule();
someMosomeModuleInstance.doSomething();
The first example only has "someModule"(from the import statement) on the main file's global scope while the second example has both "someModule" and its instance on the global scope. So is the first example better?
I am working on a VR project which is has 2 user roles, a leader (who sets up and configures a VR session) and clients (who connect to this session).
I am using a Native module to perform a DOM overlay in which several buttons related to session configuration are displayed for the leader. I was wondering if it is possible to call a function within the React360 code directly from a Native Module (i.e. not as a callback as the event would originate from the Native Module)?
This could be a complete anti-pattern, I can't seem to see a way of doing it...
I actually got this working with the following:
In client.js I passed the context to the DOM overlay native module:
const r360 = new ReactInstance(bundle, parent, {
// Add custom options here
fullScreen: true,
cursorVisibility: "visible",
nativeModules: [
// Create an instance of the DOM overlay module and pass the context
ctx => new DashboardModule(ctx, domDashboardContainer)
],
...options,
});
In the dashboard native module :
const eventToOb = (event) => {
const eventOb = {};
for (let key in event) {
const val = event[key];
if (!(lodash.isFunction(val) || lodash.isObject(val))) {
eventOb[key] = val;
}
}
return eventOb;
};
....
constructor(ctx, overlayContainer) {
super('DashboardModule');
...
this._rnctx = ctx;
this._bridgeName = 'BrowserBridge';
}
onButtonClick() {
....
this._emit('nativeButtonClicked', event);
}
_emit(name, event) {
if (!this._rnctx) {
return;
}
const eventOb = eventToOb(event);
this._rnctx.callFunction(this._bridgeName, 'notifyEvent', [name, eventOb]);
}
...
and in my index.js
...
import BatchedBridge from 'react-native/Libraries/BatchedBridge/BatchedBridge';
import lodash from 'lodash';
class BrowserBridge {
constructor() {
this._subscribers = {};
}
subscribe(handler) {
const key = String(Math.random());
this._subscribers[key] = handler;
return () => {
delete this._subscribers[key];
};
}
notifyEvent(name, event) {
lodash.forEach(this._subscribers, handler => {
handler(name, event);
});
}
}
const browserBridge = new BrowserBridge();
BatchedBridge.registerCallableModule(BrowserBridge.name, browserBridge);
....
constructor(props) {
super(props);
this.onBrowserEvent = this.onBrowserEvent.bind(this);
...
}
componentWillMount() {
this.unsubscribe = browserBridge.subscribe(this.onBrowserEvent);
}
onBrowserEvent(name, event) {
// Do action on event here
}
componentWillUnmount() {
if (this.unsubscribe) {
this.unsubscribe();
delete this.unsubscribe;
}
}
If there is a better way of doing this please let me know.
I am using ES6, and I want to start testing using mocha & chai.
My current test file code is :
const assert = require('chai').assert;
var app = require('../../../../src/app/login/loginController').default;
describe('login Controller tests', function(){
it('no idea ', function(){
let result = app();
assert.equal(result, 'hello');
})
})
and my loginController.js is :
class LoginController {
checkout(){
return 'hello';
}
}
export default LoginController
I want to import the 'checkout' function into a variable inside my test file, but so far I am able to import only the class.
Will appreciate any help, thanks !
You cannot import methods directly from classes. If you want to import a function without a class as intermediary, then you need to define the function outside the class. Or if you really meant checkout to be an instance method, then you need to call it on an instance.
Here's an example file derived from yours:
export class LoginController {
// Satic function
static moo() {
return "I'm mooing";
}
// Instance method
checkout() {
return "hello";
}
}
// A standalone function.
export function something() {
return "This is something!";
}
And a test file that exercises all functions, adapted from the file you show in your question:
const assert = require('chai').assert;
// Short of using something to preprocess import statements during
// testing... use destructuring.
const { LoginController, something } = require('./loginController');
describe('login Controller tests', function(){
it('checkout', function(){
// It not make sense to call it without ``new``.
let result = new LoginController();
// You get an instance method from an instance.
assert.equal(result.checkout(), 'hello');
});
it('moo', function(){
// You get the static function from the class.
assert.equal(LoginController.moo(), 'I\'m mooing');
});
it('something', function(){
// Something is exported directly by the module
assert.equal(something(), 'This is something!');
});
});