How can I open a synchronous dialog in Flex? I need to call a function from an External Interface (JavaScript) that will open a simple dialog in the Flex application and returns an value according to the button the user has clicked (OK/Cancel).
So it should by a synchronous call to a dialog, i.e. the call waits until the user has closed the dialog like this.
//This function is called by JavaScript
function onApplicationUnload():Boolean
{
var result:Boolean;
result = showDialogAndWaitForResult();
return result
}
Does anybody know how I can do this? I could write a loop that waits until the dialog has set a flag and then reads the result to return it, but there must be something that is way more elegant and reusable for waiting of the completion of other asynchronous calls.
EDIT:
Unfortunately a callback does not work as the JavaScript function that calls onApplicationUnload() itself has to return a value (similar to the onApplicationUnload() function in Flex). This JavaScript function has a fixed signature as it is called by a framework and I cannot change it. Or in other words: The call from JavaScript to Flex must also be synchronous.
Flex doesn't work in a synchronous fashion, as it is a single thread application and so needs your code to hand execution back to the "core" in order to handle user input etc.
The way to do it is to make your dialogue's behaviour asynchronous:
function onApplicationUnload():void
{
showDialog(resultMethod);
}
function resultMethod(result:Boolean):void
{
ExternalInterface.call("javaScriptCallback", [result]);
}
You can't do that in Flex. As David mentioned, Flex is single-threaded, so you can't have your function block while the dialog is being processed.
Your best bet might be to use a Javascript popup. You'll have a lot less control over the window, but it should behave the way you want (blocking the function until it's been closed).
Have your Flex code use an event to wait for the dialog. In the main thread, register an event handler that waits for the dialog to close. On OK in the dialog, dispatch the dialog complete event.
With Cairngorm, this is something like:
In the main thread:
CairngormEventDispatcher.getInstance().addEventListener(ClosingDialogCompleteEvent.DIALOG_COMPLETE, onClosingDialogComplete);
(if you want to avoid returning until complete, loop on a timer and global variable.)
In the dialog closing handler:
CairngormEventDispatcher.dispatchEvent(new ClosingDialogCompleteEvent(<parameters>));
The event handler:
public function onClosingDialogComplete (e: ClosingDialogCompleteEvent):void
{
param1 = e.param1;
param2 = e.param2;
// etc.
// Continue processing or set the global variable that signals the main thread to continue.
}
For this to work, the class ClosingDialogCompleteEvent has to be defined. Partial code for the class is:
package com. ... .event // You define where the event lives.
{
import com.adobe.cairngorm.control.CairngormEvent;
public class ClosingDialogCompleteEvent extends CairngormEvent
{
// Event type.
public static const DIALOG_COMPLETE:String = "dialogComplete";
public var param1:String;
public var param2:String;
public function ClosingDialogCompleteEvent(param1:String, param2:String)
{
super(DIALOG_COMPLETE);
this.param1 = param1;
this.param2 = param2;
}
}
}
Waiting on an event is the best way to synchronize in Flex. It works well for startup dialogs too. In a flex-only application it works especially well.
I have explained a workaround to create synchronous alert in flex
http://reallypseudorandom.blogspot.com/2010/05/flash-asynchronous-alert-and-pause.html
OK... after all I found a possible solution. But I guess hardly everybody is going to do that seriously :-(
The solution focuses around using a while loop to check for a result and then return the function that is being called by JavaScript. However we need a way to sleep in the while loop, while we are waiting for the result. However calls to JavaScript are synchronous. Now the trick is to make a sleep in JavaScript, which is also not directly available here, but can be done using a synchronous XML Http Request like described on this blog.
As I said - I won't recommend this only as last resort. For my problem I have resorted to ugly JavaScript popups.
Have your dialog call another function in flex to process the result of the user selection:
private function deleteFileCheck():void
{
Alert.show("Are you sure you want to delete this file?",
"Confirm Delete",
Alert.YES| Alert.NO,
this, deleteFileHandler, null, Alert.NO);
}
private function deleteFileHandler(event:CloseEvent):void
{
if (event.detail == Alert.YES)
{
...do your processing here
}
}
You can fake a synchronous dialog in flex by popping up a dialog then disabling everything in the background. You can see this in action if you do Alert.show("Hello World"); in an application. The background will grey out and the user won't be able to click on any UI in the background. The app will "wait" until the user clicks the OK button.
Related
I have a q-dialog component that has 2 buttons - CANCEL and OK. Cancel button uses v-close-popup but OK can't use that as it is doing some async data fetching, fill some UI element on the dialog and do some processing before it closes automatically. I tried to use v-close-popup and it results in undefined UI element when document.getElementById is called on the existing UI element because the dialog has closed before the processing completes. I need the UI element in the processing part because I use easyqrcodejs which needs a UI element to generate QR code. Any idea and insight is appreciated. Thanks.
Without your code is difficult to know exactly what you need.
TEMPLATE
<q-dialog v-model="isDialogOpen" />
SCRIPT
setup() {
async function fetchData() {
// do some await things here
// done with await update v-model (if using Composition API)
isDialogOpen.value = false;
}
// ...
return { isDialogOpen }
}
If you update your question with some code, I will update mine. 😁
I am using view stack...so when view change like when we move from one page to another hide event is dispatched.So i am saving the information of last page in hide event before i go to next page.but thing is that if i change nothing still change on view hide event is invoked nd call go to backend...i just want do call only if sumthing change in the view..like sum text value...So i have two options
use event listener on each component if sumthing change its make the flag true...nd hide event check, if flag is true send call to backend.
event listener at container level ..if sumthing change in child componenet through bubbling container knows if sum event is dispatched.nd makes the flag true.
I have doubt with container...
Can i use container, and how?
Reason why I can't use container?
What are the pros and cons either way?
I would recommend using a dataProvider with the ability to compare them. For instance, if you are changing things with textinputs, you could basically do something like this:
[Bindable]
private var myDataProvider:Object = new Object();
private function creationCompleteHandler():void {
myDataProvider.updated = false;
myDataProvider.defaultValue = 'default';
myDataProvider.defaultValueTwo = 'default';
}
etc.
Then, in your mxml, you can have something like this:
<mx:TextInput id="myText" text="{myDataProvider.defaultValue}" change="myDataProvider.defaultValue=myText.text; myDataProvider.updated=true;" />
Lastly, in your hide event, you can do the following:
private function hideEventHandler( event:Event ):void {
if( myDataProvider.updated ){
// Call your RemoteServices (or w/e) to update the information
}
}
This way, when anything changes, you can update your dataProvider and have access to the new information each time.
Hope this helps!
I've used an approach similar to your first option in a couple of my past projects. In the change event for each of my form's controls I make a call to a small function that just sets a changesMade flag to true in my model. When the user tries to navigate away from my form, I check the changesMade flag to see if I need to save the info.
Data models are your friend!
If you get in the habit of creating strongly typed data models out of your loaded data, questions like this become very basic.
I always have a key binding set to generate a code snipit similar to this...
private var _foo:String;
public function get foo():String
{
return _foo;
}
public function set foo(value:String):void
{
if(_foo == value)
return;
var oldVal:String = _foo;
_foo = value;
this.invalidateProperty("foo", oldVal, value);
}
If your data used getters/setters like this, it would be very easy to validate a change on the model level, cutting the view out of the process entirely.
I have a child component that dispatches an event in Parent. The event in parent makes a call to our database. Right now, the event gets fired off & the child continues without the results. How do I make it so that the child waits for the results from the database b/f the child continues?
in child:
<fx:Script>
<![CDATA[
dispatchEvent(new Event("getDBcontents")); // dispatch the event in the parent
// do some more stuff here but we need pause until we get the result from the parent
]]>
</fx:Script>
in parent:
public function getDBcontents(event:Event):void {
otherChild.getResult.token = otherChild.childRet.getContents( 'userID.text' );
}
Move the "// do some more stuff here but we need pause until we get the result from the parent" section to a different part. I assume that you are doing a remote call to your database that that has a callback. I'm not sure which mechanism you're using, but let's assume a RemoteObject.
You can pass a function on a custom event that you dispatch. The database section of your code can attach that function pointer to the AsyncToken or just add it to the class instance. Then when it comes back with results you can then call the function that you had passed in as part of the event. The joys of async programming.
I'd recommend looking at the patterns used in Cairngorm and Swiz (Swiz being my preferred framework) as the way they do database calls in those frameworks is exactly what you're trying to do here.
As an example, you could do something like this:
dispatchEvent(new MyCustomEvent("getDBcontents", callBackFunction));
private function callBackFunction(stuffToProcess:Object):void {
//do more stuff here after the stuff is returned
}
//first create MyCustomEvent class extending Event
//Then you need something to handle the event, you can build the event listener yourself, or use something like Swiz to make your life easier
//here is your event handler that you can call yourself, or assign through Swiz Cairngorm
var st:Function;
public myEventHandler(event:MyCustomEvent):void {
st = event.callBackFunction; //your param on your custom function
var token:AsyncToken=this.service.doSomething();
var responder:mx.rpc.Responder=new mx.rpc.Responder(genericResultsHandler, faultHandler);
token.addResponder(responder);
}
genericResultsHandler(result:ResultEvent):void{
if (st != null)
st(result.data);
}
Having tried a number of different solutions I keep coming back to this. I need a Window.ShowDialog, using the ViewModelLocator class as a factory via a UnityContainer.
Basically I have a View(and ViewModel) which on a button press on the the view needs to create a dialog (taking a couple of parameters in its constructor) that will process some logic and eventally return a result to the caller (along with the results of all the logic it computed).
Maybe I'm wrong for stilll looking at this from a Windows Forms perspective, but I know exactly what I want to do and I want to ideally do it using WPF and MVVM. I'm trying to make this work for a project, and ultimately don't want to have to go back to vanilla WPF in order to make it work.
I break the rules to implement a dialogwindow but tried to reduce it to a minimum. I have a method OpenDialog in my BaseViewModel:
public void OpenDialog(DialogViewModel model)
{
this.MessengerInstance.Send<DialogViewModel, MainWindow>(model);
}
And in my MainWindow:
Messenger.Default.Register<DialogViewModel>(this, model =>
{
// Instantiate the dialog box
var dlg = new DialogWindow();
// Configure the dialog box
dlg.Owner = this;
dlg.Content = model;
// Open the dialog box modally
dlg.ShowDialog();
});
That way i only have a loose coupling between my viewmodel and my MainView.
You can do the same for closing, my BaseDialogViewModel has a method:
public void CloseDialog()
{
this.MessengerInstance.Send<PopUpAction, DialogWindow>(PopUpAction.Close);
}
(PopupAction is just an enum) and my DialogWindow registers for that:
Messenger.Default.Register<PopUpAction>(this, action =>
{
switch (action)
{
case PopUpAction.Close:
this.Close();
break;
}
});
You could also leave the receiver away when sending, to keep the view class out of the viewmodel but either way i think it's a acceptable solution :)
You can do that. Just create an instance of a page/usercontrol/window and call instance.ShowDialog().
Here's my T4 templates to generate a view/viewmodel with the messaging for closing a window and other tricks.
I have a quite large Flex application with a large set of views and I ceratain views I'd like to add shortcuts.
And i'm looking for something like:
<mx:Vbox>
<foo:Shortcut keys="ctrl+s" action="{bar();}"/>
....
</mx:VBox>
Is there any framwork or component already done that does something like this? I guess it should be too difficult to build? When building this I only want the shortcuts to be active when the view is visible. How do I detect this? What base class is best to inherit from when working with non visual components?
I don't know of any framework component that does that already, but the examples above should get you started if you try to build your own.
There's no need to inherit from any component for a non-visual component like the one you've described here (your "foo" class needs no parents.) There's nothing in the Flex framework you need to inherit from for this.
However you architect it, your foo class is going to have to take in and parse keyboard codes to listen for and accept one or more methods to call. All you have to do is figure out when to add and remove the event listeners that will call the passed-in methods.
To handle turning your keyboard events on and off based on visibility, just have your foo component bind to the "visible" property of it's parent and add/remove event listeners accordingly.
You might also consider having the listeners added when the component that foo is nested in is on the display list rather than just visible. To do this, simply added and remove your event listeners in one of the component lifecycle methods - probably commitProperties is the most appropriate.
I don't think this solution answer your question directly but anyway, to help solve your problem here is an example.
For instance, I've extended the TextArea component like so. This is the best I can do so far, it can definitely be improved upon. Like, I don't know how to make the cursor go to the end after the next shortcut is pressed.
public class TextArea extends mx.controls.TextArea
{
// the keysmap is an example dictionary holding keycodes
private var keysmap:*={
112 = "some text for F1"
,113 = "the text for F2!"
//etc, etc
}
public var handleKeyDown:Boolean =false;
public function TextArea(){
if(handleKeyDown ==true){
this.addEventListener(KeyboardEvent.KEY_DOWN,this.keydownHandler);
}
}
public function keydownHandler(e:KeyboardEvent):void{
if(e.keyCode >= 112 && e.keyCode <= 123){
e.currentTarget["text"] += String(keysmap[e.keyCode]) +" ";
}//focusManager.setFocus(this);
}
}
I can't give you a solution using MXML, however my first thought would involve a singleton static class with a Dictionary that contains a list of objects as its keys and dynamically created dictionaries as the value pairing that contain keys denoting the desired key press with a function reference as the value.
So, say you had a Sprite and you wanted to capture ctrl+s for save when focus is on that object, I would get the instance of that Singleton, and call a function such as registerKeyBinding passing in the Sprite, the keyCode you want, and your pre-defined callback:
private var registeredObjects:Dictionary = new Dictionary(true);
public function registerKeyBinding(targetObject:Object, keyCode:int, callback:Function) {
if (registeredObjects[targetObject]) {
Dictionary(registeredObjects[targetObject])[keyCode] = callback;
}
else {
registeredObjects[targetObject] = new Dictionary();
Dictionary(registeredObjects[targetObject])[keyCode] = callback;
targetObject.addEventListener(KeyboardEvent.KEY_DOWN, keyDownListener);
}
}
private function keyDownListener(e:KeyboardEvent):void {
if (e.ctrlKey == true) {
//calls the function if that key exists.
Dictionary(registeredObjects[e.target])[e.keyCode];
}
}
Can't say I've tested this, but it was just the first thing that popped into my head. You could then setup functions to deregister and delete keys from the dictionaries, check states of the objects in addition to the keyCodes, remove old listeners, and delete entire dictionaries when there is no longer a need for them. Hopefully this is at least a tiny bit helpful.