I'd like to be able to switch between 2 different apps (app1 and app2) using and transition animation. Ideally with the following capabilities
1) App2 is able to recognize that was invoked by App1
2) App1 able to received a callback parameter from App2
Unfortunately, no magic for this case. To implement this scenario you need to:
1 Create separate model (AppSettings for example) in both apps and store there App1Url and App2Url correspondingly for each app.
2 To navigate user from App1 to App2 you can use this binding for Link widgets:
#datasources.AppSettings.item.App2Url + '?paramName=paramValue' + '#PageName'
3 In the onAttach event of the 'PageName' page invoke function like this
function loadPageName() {
google.script.url.getLocation(function(location) {
var paramName = location.parameter.paramName;
var datasource = app.datasources.SomeDatasource;
datasource.filters.SomeField._equals = paramName;
datasource.load();
});
}
Please, keep in mind, that to avoid double datasource loading you need to switch it to manual loading mode.
This scenario will cause full page reload.
Related
(Brand new learning Electron here, so I'm sure this is a basic question and I am missing something fundamental...)
How does one interact with a local database (I am using Sqlite) from an Electron application front end? I have a very basic database manager class and have no problem using it from the index.js file in my Electron application. But from the front-end (I am using Svelte, but I can probably translate solutions from other front-end frameworks), how does one interact with the database? This seems fundamental but I'm striking out trying to find a basic example.
Since everything is local it would seem it shouldn't be necessary to set up a whole API just to marshal data back and forth, but maybe it is? But if so, how does one go about telling the Electron "backend" (if that's the right term) to do something and return the results to the front end? I'm seeing something about IPC but that's not making a lot of sense right now and seems like overkill.
Here's my simple database manager class:
const sqlite3 = require("sqlite3").verbose();
class DbManager {
#db;
open() {
this.#db = new sqlite3.Database("testing.db", sqlite3.OPEN_READWRITE);
}
close() {
this.#db.close();
}
run(sql, param) {
this.#db.run(sql, param);
return this;
}
}
const manager = new DbManager();
module.exports = manager;
And I can call this and do whatever no problem from the Electron entry point index.js:
const { app, BrowserWindow, screen } = require("electron");
require("electron-reload")(__dirname);
const db = require("./src/repository/db");
const createWindow = () => {
...
};
let window = null;
app.whenReady().then(() => {
db.open();
createWindow();
});
app.on("window-all-closed", () => {
db.close();
app.quit();
});
But what to do from my component?
<script>
// this won't work, and I wouldn't expect it to, but not sure what the alternative is
const db = require("./repository/db");
let accountName;
function addAccount() {
db.run("INSERT INTO accounts (name) VALUES ($name);", { $name: accountName });
}
</script>
<main>
<form>
<label for="account_name">Account name</label>
<input id="account_name" bind:value={accountName} />
<button on:click={addAccount}>Add account</button>
</form>
</main>
If anyone is aware of a boilerplate implementation that does something similar, that would be super helpful. Obviously this is like application 101 here; I'm just not sure how to go about this yet in Electron and would appreciate someone pointing me in the right direction.
If you're absolutely 100% sure that your app won't be accessing any remote resources then you could just expose require and whatever else you may need through the preload script, just write const nodeRequire = require; window.require = nodeRequire;.
This is a fairly broad topic and requires some reading. I'll try to give you a primer and link some resources.
Electron runs on two (or more if you open multiple windows) processes - the main process and the renderer process. The main process handles things like opening new windows, starting and closing the entire app, tray icons, window visibility etc., while the renderer process is basically like your JS code in a browser. More on Electron processes.
By default, the renderer process does not have access to a Node runtime, but it is possible to let it. You can do that in two ways, with many caveats.
One way is by setting webPreferences.nodeIntegration = true when creating the BrowserWindow (note: nodeIntegration is deprecated and weird. This allows you to use all Node APIs from your frontend code, and your snippet would work. But you probably shouldn't do that because a BrowserWindow is capable of loading external URLs, and any code included on those pages would be able to execute arbitrary code on your or your users' machines.
Another way is using the preload script. The preload script runs in the renderer process but has access to a Node runtime as well as the browser's window object (the Node globals get removed from the scope before the actual front end code runs unless nodeIntegration is true). You can simply set window.require = require and essentially work with Node code in your frontend files. But you probably shouldn't do that either, even if you're careful about what you're exposing, because it's very easy to still leave a hole and allow a potential attacker to leverage some exposed API into full access, as demonstrated here. More on Electron security.
So how to do this securely? Set webPreferences.contextIsolation to true. This definitively separates the preload script context from the renderer context, as opposed to the unreliable stripping of Node APIs that nodeIntegration: false causes, so you can be almost sure that no malicious code has full access to Node.
You can then expose specific function to the frontend from the preload, through contextBridge.exposeInMainWorld. For example:
contextBridge.exposeInMainWorld('Accounts', {
addAccount: async (accountData) => {
// validate & sanitize...
const success = await db.run('...');
return success;
}
}
This securely exposes an Accounts object with the specified methods in window on the frontend. So in your component you can write:
const accountAdded = await Accounts.addAccount({id: 123, username: 'foo'});
Note that you could still expose a method like runDbCommand(command) { db.run(command) } or even evalInNode(code) { eval(code) }, which is why I said almost sure.
You don't really need to use IPC for things like working with files or databases because those APIs are available in the preload. IPC is only required if you want to manipulate windows or trigger anything else on the main process from the renderer process.
Hello I'm trying to learn Meteor Js and i started building a simple task manager app.
I want to add a notification (using the notifications api), when a user adds a task from an input. My problem is that I cant find a way to send the notifications between users from the server side.
What i have so far :
Template.listPage.events({
'submit #newItem':function(e){
e.preventDefault();
//variables to insert here here. i dont post for space issues.
// Call server method for insert into db
Meteor.call('addItem',item,d,user,datestring,currentList,listName,assign);
// This is what i m trying to implement
var items = todos.find({name:item}).observe({
added:function() {
var n = new Notification("hello new task added")
}
})
},
The above works as :
The notification is shown only to the user that adds the task (not really useful).
How can I use this in server side, In order to:
Show the notification to all users using the app? Is this possible or I have to use a push notification package?
PS: I ask for users permition to receive notifications on Meteor.startup.
The problem is that you specify the observe within the click event. Therefore, only users who have triggered the event (i.e. clicked the button) will get the notification.
In order to activate the observer for everyone do the following:
Template.listPage.rendered = function(){
todos.find().observe({
added: function() {
... do whatever you want to do here
}
})
};
To add notifications, you could use a package such as yogiben:notifications:
https://atmospherejs.com/yogiben/notifications
(There might be better packages for this nowadays. Search on Atmosphere.)
Alternatively, you can roll your own solution. The basic idea would be add create a collection called Notifications. Then add notifications whenever you feel like it and publish the relevant notifications to whichever user(s) you'd like.
The code in your question will also work as long as you're publishing the relevant todos to different users.
I have a Google Apps Script application written using the UI service (not the HTML service), and the app is embedded in a Google Site.
I have a grid of 15 values (3x5). I am able to use a clientHandler to validate that the the values in each textBox are integers.
I want to ensure that all 15 of the values are correctly set before enabling the Submit button.
What is the best way to do this?
Obviously, just toggling the the button .setEnabled property onChange is no good, as if one widget disables the button, but the next is a valid integer, it would re-enable the button.
I thought about using a serverHandler, but figured that could be slow and unreliable. I'd like to keep it client side if I can. It feels like it should be possible, but I just can't work it out. What am I missing?
All advice welcomed. Thanks.
This will enable submit once all fields are integers, but it'll need more added to it to handle cases where the values could get changed back to non-integer after first passing the validation.
function doGet() {
var app = UiApp.createApplication();
var field1 = app.createTextBox();
var field2 = app.createTextBox();
var field3 = app.createTextBox();
var submit = app.createButton('SUBMIT').setEnabled(false);
var handler = app.createClientHandler().validateInteger(field1)
.validateInteger(field2).validateInteger(field3)
.forTargets(submit).setEnabled(true);
field1.addValueChangeHandler(handler);
field2.addValueChangeHandler(handler);
field3.addValueChangeHandler(handler);
app.add(field1).add(field2).add(field3).add(submit);
return app;
}
Deployed app.
Corey's UiApp posts are helpful in this area.
PURE MVC -
In my application there are multiple screens which are minimized like in windows desktop.
Now each has different instances of mediator which have same notification.
So if I do changes in one screen say press some button >> which sends some command >> to proxy >>then proxy sends notifications to mediator.
But as multiple screens are active there it is showing changes in all screens as they are notified too by proxies .
How to make sure that proxy calls to particular view component (mediator ) and not for all live instances of mediators ?
In the PureMVC structure the Proxy notifications are send to all instances of the mediators
though you can restrict the action performed to all other components apart from the selected one.like below
assign the selected component to singleton class variable during button click
check the id in handleNotification function in mediator like below
Example:
public static const ADD_BOLD_STYLE:String = "AddBoldStyle";
public function TextElementMediator(objViewComponent:Object)
{
super(NAME, objViewComponent);
_objTextData = objTextData;
}
public function get textElement():TextElement
{
return viewComponent as TextElement;
}
override public function listNotificationInterests():Array
{
return [ADD_BOLD_STYLE];
}
override public function handleNotification(notification:INotification):void
{
switch(notification.getName())
{
case ADD_BOLD_STYLE:
if(textelement == singleton.selectedObject)
{
//Process your data
}
break;
}
}
here singleton refers to selectedObject
The way you have phrased the question is that you are pressing a button in one of the screens and want to update that view. What should happen is that the view should dispatch an event to the mediator. Only if you need business logic or data from the proxy should you send a notification to the command tier. You say that you are notifying the proxy which leads me to believe that you need FRESH data from the proxy i.e. you are requesting new data from the backend. When the new data is retrieved it would send a notification to the mediator to update the view with the fresh data. The rest of the application should update if the data has changed.
If not all mediators care about this data then you should probably subclass the mediator for your different windows and override the 'execute' method.
So I'm trying to build a tool that will allow me and other users to all open the same .swf, and then I as the Admin user am able to interact with mine while they all see my mouse movements and button clicks etc on theirs.
I'm using BlazeDS to manage this and I'm getting data sent back and forth etc - no difficulties there. The issue I'm running into is this:
In an "Admin" instance, I click a button. I capture that X and Y, then tell Blaze to tell my clients to dispatch a Click event at that X and Y. On my client side, I get that data and dispatch a Click event at that X and Y - but the click is actually caught at the stage level. The click on my client side takes place UNDER all of my buttons and other content - so the whole thing fails.
Does this make sense? Is there a way to tell it to start the click event at the top?
If you are unable to architect the loaded swf's to use a better architecture you could try something a little more hackish to get buttons working.
Have a look at the methods getObjectsUnderPoint and areInaccessibleObjectsUnderPoint of the DisplayObjectContainer. Combined with hasEventListener you should be able to emulate what you want.
Here is some untested pseudo-code:
function detectClick(pt:Point):void
{
var objsUnderPoint:Array = containerStage.getObjectsUnderPoint(pt);
var clickable:Array = [];
for each(dispObj:DisplayObject in objsUnderPoint)
{
if(dispObj.hasEventListener(MouseEvent.CLICK))
{
clickable.push(dispObj);
}
}
if(clickable.length)
{
// sort on depth here
// that might be tricky since you'll be looking at grandchildren
// and not just children but it is doable.
var topMostClickable:DisplayObject = ???
topMostClickable.dispatchEvent(new MouseEvent(MouseEvent.CLICK, true, false));
}
}
areInaccessibleObjectsUnderPoint is important if you think their might be security restrictions (e.g. cross-domain issues) so you can debug if things go wrong.
Also note that you may want to (or need to) fill in more details of the MouseEvent (like the proper target, localX, localyY etc.)
Sounds like you need to set focus to the top most component of the x and y positions before dispatching the click event.
That said, I wonder what the use case is for something like this; as opposed to using a screen sharing tool such as Connect.
This seems like a poor way to implement what you are trying to do. If you "click" in the admin tool you are probably actually triggering some event. Why not trigger that event instead of sending the mouse click?
I'd just keep a map of actions and then when something happens in the Admin interface send the key to the action.
e.g.
In the admin:
myButton.addEventListener(MouseEvent.CLICK, handleClickButton);
function handleClickButton(event:MouseEvent):void
{
doSomeAction();
sendTriggerToClient(MyActions.SOME_TRIGGER);
}
In the client:
var actionMap:Object = {};
actionMap[MyActions.SOME_TRIGGER] = doSomeAction;
function receiveTriggerFromAdmin(trigger:String):void
{
var responseFunc:Function = actionMap[trigger];
responseFunc();
}
I hope that pseudo-code makes sense. Just abstract what happens as a result of a click into a separate function (doSomeAction) and then have the admin send a message before calling that function. In the client wait for any trigger that comes through and map it to the same function.