I'm trying to figure out how to call an Electron method from my frontend application javascript. Either the main or renderer process would do fine for starters, presumably I can work through the rest from there.
In all the examples I can find, the renderer code attaches to the frontend element and adds an event listener:
document.querySelector('#btn').addEventListener(() => { // doElectronStuff });
This is not quite what I'm after though... it seems like a rather severe coupling to have this "server side" code reaching into my DOM.
Using an Angular2 frontend, I found what appears to be a nice package called ngx-electron which exposes the electron interface as an injectable complete with typescript mappings, etc.
So now I have an Angular service that I want to call an Electron method for (to grab some database stuff, or whatever else):
constructor(private _electron:ElectronService) {}
getAll(): Entity[] {
var results = this._electron.ipcRenderer.?????
}
I really have no idea how to make the angular service make that call to the electron method. I've tried running emit() and have tried using send() and such against the ipcRenderer, the remote.ipcMain, but receive various errors and all and all can't seem to make the connection.
Hopefully I missing something simple? What's the combination of electron-side syntax and angular-side syntax that's required to match these up? Thanks
(I'm not particularly stuck on ngx-electron, but it did seem a nice lib and I'm assuming it works well, once I get past my own block...)
Found it. As usual, an oversight on my end.
// in the angular service
this._electron.ipcRenderer.send('event-aka-channel-name1', args);
// in the electron main.js
ipc.on('event-aka-channel-name1', (event, args) => { // doStuff });
My issue was apparently a misspelling of an import which I caught via various logs. Once that was fixed the rest works as intended (or at least enough for me to move forward)
Related
I am asking this question because I am a bit confused. I just started to discover meteor ( better late then never ) and I am reading/hearing a lot of discussions why I should use Flow-router instead of Iron router.
I started my project with Iron router, but the more I read the more I think I should switch to Flow-router for many performances, rendering reasons ...
What pro's and con's make them different?
Sank U !
The official documentation recommends FlowRouter: https://guide.meteor.com/routing.html
But you can use iron router too. I have already used them both in different projects, but I decided to follow meteor official recommendation.
I use IronRouter, because the description states that
"FlowRouter only deals with registration of subscriptions. It does not
wait until subscription becomes ready."
(https://github.com/kadirahq/flow-router)
Because of this, when you subscribe to large data, you get extra page updates or failures. So I chosed IronRouter, which in the documentation describes how to make the waiting for the subscription ready (WaitOn). It works without problems 2 years for me. From the point of view of the update, both routers have not been updated for a long time, more than a year, so it is unclear whether the revision is coming as new versions of Meteor are released.
Press F12 in browser, watch for any errors. In my case I removed a package, but did not remove the code calling that package.
It errored, and looked like Iron Router was to blame.
Removed the offending code loading some other lib I removed and hey, Iron Router works.
The problem with FlowRotuer is you have to jump through hoops to load data into a template.
It makes code complex, fragmented and hard to follow (My opinion).
Iron Router allows you to pass data in as a second argument to the render function and access it directly from the template.
With Flow Router you have to first write data to a session, then write a Template helper to pull the session data or wrap your template in a "with" element.
This is an example how FlowRouter would have you get some example into a template
Template.templateName.onCreated(function() {
Meteor.call('thirdPartyAPI', function(error, result) {
Session.set('result', result);
});
});
Then on the template side you could have:
{{#with result}}
Content that requires a context
{{/with}}
And you would have a template helper that returned the Session/ReactiveVar, e.g.
Template.templateName.helpers({
result: function() {
return Session.get('result');
}
});
A similar example with Iron Router
Router.route('/post/:_id', function () {
this.render('Post', {
data: function () {
return Posts.findOne({_id: this.params._id});
}
});
});
Is there a way to instantiate sails.io inside the zone of a service provider in Angular2 so that websocket events trigger change detection?
A sub-question: how to RXJS to subscribe to sails.io data streams.
I'm using Angular2 RC4, and the latest version of SailsJS.
Related question: Angular2 View Not Changing After Data Is Updated
UPDATE
It annoyed me that I couldn't give a full example of how to use Sails with Angular 2 with just plunker, so I created a github repo, that provides the full picture on how this can work.
I can see how the related question you linked, would lead you down the zone path. However, you can plug all of this together without having to manually tinker with zones. Here is an example of how you could implement sails.io with Angular2 in a plunker. This leverages using RxJS to create a type of observable. You also have to implement a different version of Angular2 change detection(all of this is implemented in the plunker).
I go into a little more detail in my answer to your linked question.
As for your sub question I'm not sure if there is a way to integrate the stream, I believe one of the intentions of RxJS was to reduce the use of callbacks, which sails.io appears to still do.
Excerpt of implementing sails.io in a service from the plunker.
constructor() {
this._ioMessage$ = <Subject<{}>>new Subject();
//self is the window object in the browser, the 'io' object is actually on global scope
self.io.sails.connect('https://localhost:1337');//This fails as no sails server is listening and plunker requires https
this.listenForIOSubmission();
}
get ioMessage$(){
return this._ioMessage$.asObservable();
}
private listenForIOSubmission():void{
if(self.io.socket){//since the connect method failed in the constructor the socket object doesn't exist
//if there is a need to call emit or any other steps to prep Sails on node.js, do it here.
self.io.socket.on('success', (data) => {//guessing 'success' would be the eventIdentity
//note - you data object coming back from node.js, won't look like what I am using in this example, you should adjust your code to reflect that.
this._ioMessage$.next(data);//now IO is setup to submit data to the subscribbables of the observer
});
}
}
In my add-on, I'm using XUL to display dialog windows because I can customize their appearance to suit the add-on's general style (like a custom titlebar).
Using the migration guide, I'm able to do this easily. The thing is, I would like to call certain functions in the add-on's main module from the XUL dialog.
After a bit of searching I found the loader module, which seems to be able to do exactly what I want. But, I'm experiencing trouble in using it to access the main module.
First, I tried the obvious approach as mentioned in the documentation;
xul_dialog.js:
let {Loader} = Components.utils.import('resource://gre/modules/commonjs/toolkit/loader.js');
let loader = Loader.Loader({
paths: {
'toolkit/': 'resource://gre/modules/commonjs/toolkit/',
'': 'resource:///modules/',
'./': 'resource://<my-addon-name>/root/'
}
});
let main = Loader.main(loader, './main');
I got an error that './main' was not found at resource://<my-addon-name>/root/. Figuring that I was using the incorrect paths, I experimented a bit until I could remove all path associated errors.
xul_dialog.js:
let {Loader} = Components.utils.import('resource://gre/modules/commonjs/toolkit/loader.js');
let loader = Loader.Loader({
paths: {
'toolkit/': 'resource://gre/modules/commonjs/toolkit/',
'': 'resource://gre/modules/commonjs/',
'./': 'resource://<my-addon-id>-at-jetpack/<my-addon-name>/lib/'
}
});
let main = Loader.main(loader, './main');
This time I got a rather confusing error at loader.js, line 279.
Components is not available in this context.
Functionality provided by Components may be available in an SDK
module: https://jetpack.mozillalabs.com/sdk/latest/docs/
However, if you still need to import Components, you may use the
`chrome` module's properties for shortcuts to Component properties:
Shortcuts:
Cc = Components.classes
Ci = Components.interfaces
Cu = Components.utils
CC = Components.Constructor
Example:
let { Cc, Ci } = require('chrome');
I get the same error when I use Loader.Require(loader, {id: './main'}) instead of Loader.main. I even tried passing Components as globals when instantiating the loader, but without much luck.
I'm fairly certain that I'm doing a lot of things wrong. I don't understand why I'm getting the error, even after spending quite a bit of time in loader.js. Plus, I also think that there would be a better alternative than having to use the add-on id for the path to main.js; hard-coding it like that doesn't seem right.
Any help would be really appreciated.
What you have to do is to find a specific instance of Loader, not create a new one.
At main.js
const { id, name, prefixURI } = require("#loader/options");
//pass these to the XUL dialog
At xul.js (or whatever is the name of the xul dialog script)
Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
var extensionscope = XPIProvider.bootstrapScopes[id];
var mainjssandbox = extensionscope.loader.sandboxes[prefixURI + name + "/lib/main.js"];
Assuming there is a foo function at main.js, you can call it like
mainjssandbox.foo();
Of course don't expect things to work as if XUL and Add-on SDK actually blended into one thing.
If it is your XUL dialog that should interact with your add-on, then please don't use the Loader stuff and in particular do not go the XPIProvider.bootstrapScopes #paa suggested. While this might work (for now), it should be noted that it relies on tons of implementation details that are subject to change at any point making this solution extremely fragile.
Instead there are a couple of other options (not an exhaustive list):
If the SDK part opens the windows, you may use .openDialog which supports passing arguments to the created window, and these arguments can even be objects and functions. Also, you can have the window dispatch (custom) events, and which your SDK part can listen to by calling addEventListener on the window the .openDialog call returns.
If the window is created from somewhere else (e.g. from the AddonManager because of em:optionsURL) then the nsIObserverService is another way to communicate. The window could e.g. .notifyObservers on DOMContentLoaded containing a reference to itself. The SDK parts would just have to observe such notifications by addObserver.
Another way, a bit hacky but working, is the SDK part listening to new windows via nsIWindowWatcher.registerNotification and then injecting some API into e.g. browser.xul windows by XPCNativeWrapper.unwrap(subject.QueryInterface(Ci.nsIDOMWindow)).myAddonAPI = something.
Be sure to handle unloading or your add-on well - it is still restartless -, i.e. reverse any changes you've done and remove any observers and so on again.
If you want to interact with SDK addons not created by you, then the XPIProvider route might be the only feasible. But still, it might be worth contacting the add-on author first asking for the addition of some public API instead of hacking deep into the AddonManager and SDK loader internals.
PS:
Considering passing require or the global scope to your window via openDialog if you want to. Get the global scope by placing this into your main.js:
const globalScope = this;
I'm having trouble integrating bitovi syn (link), Rails 3 (asset pipeline), Ember and qunit. I want to use syn for browser simulation for testing purposes. Has anyone done this, if so, how?
I'm using the version of syn that was released 11 Mar 2014. When I load it into my app, two things happen:
I get a global failure in qunit that says "TypeError: 'undefined' is not an object (evaluating 'Syn.schedule')", (around this line: syn.js?body=1:1084)
and
A div with a form is added to my application.
I'm using qunit for the most part, and I dabbled with using YUI to do browser simulation but it isn't working quite the way I had expected it to. I'd really like to use Syn, but I don't understand why it's not working.
In attempting to get it work, I tried adding this line to the top of the syn.js file:
window.Syn = { schedule: function (fn, ms) { Ember.run.later(window, fn, ms); } };
but it didn't do anything much at all.
I'd read on this pull request: https://github.com/bitovi/syn/pull/28 that I could add that piece of code to mount it in a fashion to work with Ember.
Any help would be greatly appreciated!
So the thing here was simply to load Syn at the bottom of the page. It's potentially a bit broken at the moment in the case where you want to load it in the head (which I probably shouldn't have been doing anyway, but still!) :)
I use TypeScript to code my javascript file with Object Oriented Programing.
I want to use the node module https://npmjs.org/package/typescript-require to require my .ts files from other files.
I want to share my files in both server and client side. (Browser) And that's very important. Note that the folder /shared/ doesn't mean shared between client and server but between Game server and Web server. I use pomelo.js as framework, that's why.
For the moment I'm not using (successfully) the typescript-require library.
I do like that:
shared/lib/message.js
var Message = require('./../classes/Message');
module.exports = {
getNewInstance: function(message, data, status){
console.log(requireTs);// Global typescript-require instance
console.log(Message);
return new Message(message, data, status);
}
};
This file need the Message.js to create new instances.
shared/classes/Message.ts
class Message{
// Big stuff
}
try{
module.exports = Message;
}catch(e){}
At the end of the fil I add this try/catch to add the class to the module.exports if it exists. (It works, but it's not really a good way to do it, I would like to do better)
If I load the file from the browser, the module.export won't exists.
So, what I did above is working. Now if I try to use the typescript-require module, I'll change some things:
shared/lib/message.js
var Message = requireTs('./../classes/Message.ts');
I use requireTs instead of require, it's a global var. I precise I'm using .ts file.
shared/classes/Message.ts
export class Message{
// Big stuff
}
// remove the compatibility script at the end
Now, if I try like this and if I take a look to the console server, I get requireTs is object and Message is undefined in shared/lib/message.js.
I get the same if I don't use the export keyword in Message.ts. Even if I use my little script at the end I get always an error.
But there is more, I have another class name ValidatorMessage.ts which extends Message.ts, it's not working if I use the export keyword...
Did I did something wrong? I tried several other things but nothing is working, looks like the typescript-require is not able to require .ts files.
Thank you for your help.
Looking at the typescript-require library, I see it hasn't been updated for 9 months. As it includes the lib.d.ts typing central to TypeScript (and the node.d.ts typing), and as these have progressed greatly in the past 9 months (along with needed changes due to language updates), it's probably not compatible with the latest TypeScript releases (just my assumption, I may be wrong).
Sharing modules between Node and the browser is not easy with TypeScript, as they both use very different module systems (CommonJS in Node, and typically something like RequireJS in the browser). TypeScript emits code for one or the other, depending on the --module switch given. (Note: There is a Universal Module Definition (UMD) pattern some folks use, but TypeScript doesn't support this directly).
What goals exactly are you trying to achieve, and I may be able to offer some guidance.
I am doing the same and keep having issues whichever way I try to do things... The main problems for me are:
I write my typescript as namespaces and components, so there is no export module with multiple file compilation you have to do a hack to add some _exporter.ts at the end to add the export for your library-output.js to be importable as a module, this would require something like:
module.exports.MyRootNamespace = MyRootNamespace
If you do the above it works, however then you get the issue of when you need to reference classes from other modules (such as MyRootNamespace1.SomeClass being referenced by MyRootNamespace2.SomeOtherClass) you can reference it but then it will compile it into your library-output2.js file so you end up having duplicates of classes if you are trying to re-use typescript across multiple compiled targets (like how you would have 1 solution in VS and multiple projects which have their own dll outputs)
Assuming you are not happy with hacking the exports and/or duplicating your references then you can just import them into the global scope, which is a hack but works... however then when you decide you want to test your code (using whatever nodejs testing framework) you will need to mock out certain things, and as the dependencies for your components may not be included via a require() call (and your module may depend upon node_modules which are not really usable with global scope hacking) and this then makes it difficult to satisfy dependencies and mock certain ones, its like an all or nothing sort of approach.
Finally you can try to mitigate all these problems by using a typescript framework such as appex which allows you to run your typescript directly rather than the compile into js first, and while it seems very good up front it is VERY hard to debug compilation errors, this is currently my preferred way but I have an issue where my typescript compiles fine via tsc, but just blows up with a max stack size exception on appex, and I am at the mercy of the project maintainer to fix this (I was not able to find the underlying issue). There are also not many of these sort of projects out there however they make the issue of compiling at module level/file level etc a moot point.
Ultimately I have had nothing but problems trying to wrestle with Typescript to get it to work in a way which is maintainable and testable. I also am trying to re-use some of the typescript components on the clientside however if you go down the npm hack route to get your modules included you then have to make sure your client side uses a require compatible resource/package loader. As much as I would love to just use typescript on my client and my server projects, it just does not seem to want to work in a nice way.
Solution here:
Inheritance TypeScript with exported class and modules
Finally I don't use require-typescript but typescript.api instead, it works well. (You have to load lib.d.ts if you use it, else you'll get some errors on the console.
I don't have a solution to have the script on the browser yet. (Because of export keyword I have some errors client side) I think add a exports global var to avoid errors like this.
Thank you for your help Bill.