Futurescale, Inc. PureMVC Home

The PureMVC Framework Code at the Speed of Thought


Over 10 years of community discussion and knowledge are maintained here as a read-only archive.

New discussions should be taken up in issues on the appropriate projects at https://github.com/PureMVC

Pages: [1]
Print
Author Topic: How to implement 'Save record? (Yes/No/Cancel)'  (Read 13104 times)
willw
Full Member
***
Posts: 30


View Profile Email
« on: February 13, 2009, 01:37:50 »

Once again I seem to be stuck on what should be a simple thing.

I have a database editor, which allows one to select and edit a record from a grid, or create a new record. I have created suitable commands, mediators etc, so that the various parts of the app live in blissful ignorance.

However, now I would like to handle the following situation. The user has edited a record. Now he does something else - say select another record in the grid (or logs out, or changes the selection set of records - something that is potentially destructive to his current record. Any one of a number of things, where this number will increase as time goes on).

I want to put up a 'Save this record? Yes/No/Cancel' dialog, and then save the record (with the async round trip to the database that implies), sling the record or cancel the action and continue editing. In the first two cases, I wish to carry out the original request afterwards. I would like to do it in a generic way, so that it handles new ways of interrupting the editing process that may occur as the app expands.

If I were doing this in a sinful, tightly-coupled application, I would supply a general routine to test if the record were edited, call my dialog, harvest the answer and take action as appropriate. The logic would be clear, and in one place.

As it is, I am a bit baffled. The logic seems to spread itself over lots of notifications and commands (which I have to create to prevent my mediators from mating), and/or I have to make a state machine which grows states like topsy, and has special pockets where you keep the command you want to execute in the notification handler that is next but one.

I am sure I am being very dense about this, but I really would much appreciate pointers as to how to approach it.
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #1 on: February 14, 2009, 06:49:22 »

Using the StateMachine utility in conjunction with the AsyncCommand Utility is how I would approach it today. Possibly only with AsyncCommand if you're really against the idea of incorporating a state machine.

-=Cliff>
Logged
willw
Full Member
***
Posts: 30


View Profile Email
« Reply #2 on: February 14, 2009, 10:19:07 »

Thanks Cliff, that's reassuring. I thought I might be barking completely up the wrong tree.

I am not against state machines. Indeed, I am very tempted. I now have a state chart sketch to prove it.

From what you said before, do you recommend using an AsyncCommand to run a modal dialog in PureMVC? Is that a better idiom than broadcasting the result from the popup's mediator? The mediator is self contained, and perhaps reusable; the AsyncCommand captures the post-dialog logic?

Please excuse my questions. I am an elderly C++ programmer finding myself on strange and unfamiliar ground. I have yet to reach a comfort level with this framework. I will shut up in a minute, I promise.

Will
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #3 on: February 14, 2009, 05:59:26 »

With the StateMachine, you need to be sure that its safe to change states BEFORE sending the StateMachine.ACTION notification that kicks off the transition.

Let's say you're in the 'edit' state and you click something that will take you to the 'list' state.
That click should lead to a notification that causes a PopUpMediator hears and shows its Popup asking if you want to save. If not, the popup is removed and that's it, you just keep editing.

However if you want to save, then an AsyncCommand asks a Proxy to save the data. (Search for AsyncToken and AsyncCommand in these forums for notions on how to drive the proxy from an AsyncCommand).

Basically the AsyncCommand needs to pass a reference to its completion method that the proxy stashes on the AsyncToken returned when it makes a call. When that result comes back later, the ResultEvent's token property will have this reference whic the proxy calls, causing the AsyncCommand to complete. The AsyncCommand should actually be one of two subcommands of an AsyncMacroCommand. So when the first one completes, the next one runs. That one should send the StateMachine.ACTION notification that triggers the state change.

Hope this helps,
-=Cliff>
Logged
willw
Full Member
***
Posts: 30


View Profile Email
« Reply #4 on: February 15, 2009, 05:03:04 »

Hi,

Thanks for the explanation. However, I am still puzzled. (Fx: Transatlantic groan.)

Let's say you're in the 'edit' state and you click something that will take you to the 'list' state.
That click should lead to a notification that causes a PopUpMediator hears and shows its Popup asking if you want to save.

Suppose, for the sake of argument, the action is clicking on another record in the grid. The grid mediator generates a SELECT_RECORD notification.

My PopUpMediator (you seem to be saying) consumes this. But it needs to know if I am in the edit state. If I am, it pops up and does its thing as you describe. If I am in the list state, it creates an action to change the state back to list state, and somehow forwards the notification so that the mediator that owns the 'Display this record' component loads up the new record.

So now my PopUpMediator has to know what the FSM state is, and test it. Doesn't it? Is that ok? Surely its behaviour should fall out of the transitions that take place, rather than by explicitly querying state? We seem to have moved the logic of the state transitions out of the XML definition of the FSM, and into the code inside the PopUpMediator.

I was thinking of a design something like this:

The SELECT_RECORD notification, issued when the user clicks on a different record in the grid,  is changed into a StateMachine.ACTION called SELECT_RECORD that causes the state machine to try to exit the 'edit' state. This is defined in the FSM.

I set up an AsyncCommand that detects attempts to exit 'edit' state and runs the popup, gathering the result in its callback. When the popup dialog returns, and we are still trying to exit the state, and the following logic gets executed:

- if the user has clicked 'Yes, save my edited record' then the callback triggers a notification for AsyncMacroCommand comprising two parts: 1) save the record, 2) select the clicked-on record. If the 'save the record' part fails, by the way, I most likely issue a StateMachine.CANCEL notification.

- if the user has clicked 'No, ditch my edited record' then just perform the 'select the clicked-on record' part as described above.

- if the user has clicked 'cancel, let's keep on editing' then the callback in the async command generates a StateMachine.CANCEL notification - aborting the state transition

But this design contradicts the very first sentence in your answer, viz:

With the StateMachine, you need to be sure that its safe to change states BEFORE sending the StateMachine.ACTION notification that kicks off the transition.

because I want to be able to abort an ongoing transition. What is CANCEL for if not to abort a transition?

Sorry to be thick, and thanks for your ongoing patience.

Will
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #5 on: February 15, 2009, 06:49:56 »

The problem is that the asyncCommand if triggered on the exiting note and used to popup the dialog will not be able to hold off the thread of execution from completing the StateMachine transition while the popup is awaiting your input. Unless you pass a reference to its completion method to the PopupMediator, which would call that method when it gets the answer back from the popup. That's the tricky part about AsyncCommand is getting it to complete. The author's demo of a t timer that goes away and then comes back is not the best example for showing this kind of stuff.

Its all about keeping that AsyncCommand waiting on the call stack until you've collected data from the user or a service. In those scenarios you need to pass the Proxy or Mediator a reference to the completion function of the AsyncCommand and be sure it gets held onto and called when the data has been collected.

-=Cliff>
Logged
willw
Full Member
***
Posts: 30


View Profile Email
« Reply #6 on: February 15, 2009, 11:11:39 »

The problem is that the asyncCommand if triggered on the exiting note and used to popup the dialog will not be able to hold off the thread of execution from completing the StateMachine transition while the popup is awaiting your input.

Ok, I see that now you say it. But I still have the problem of not wanting to let the popup dialog's mediator query the StateMachine's state.

I feel that the popping up of the dialog must fall out of the design of the state machine.

How about this for a variation? The situation of having a popped-up dialog is really a state. To fix my design, I need to admit this and put it in the model:

From the 'edit' state, clicking something (apart from 'Apply' or 'Cancel' buttons) takes you to the 'abandon dialog' state.

Entering the 'abandon dialog' triggers an async command which
1) Stores the action that caused this transition in its instance
2) Fires off the popup mediator to get the Y/N/Cancel answer from the user - supplying a completion function that gets called when the dialog is dismissed.

The dialog completes, so now we are back in the async command... with the answer. Now I can mung in your previous design with mine:

  • if the answer is 'yes, save my edits' then fire off an AsyncMacroCommand comprising  1) save the record 2) fire the StateMachine.ACTION to go to wherever the original click wanted to go. We know what this action is, because we stored it in the 'Show Dialog' AsyncCommand's instance data above
  • if the answer is 'no, ditch my edits' then just fire off the StateMachine.ACTION to go and do the original action
  • if the answer is cancel, fire off the StateMachine.ACTION to go to the 'edit' state

That works, doesn't it? I have pushed the logic back into the FSM's definition. I think.

Mostly I am worried that I won't be able to find out the Action that brought me here when I fire up the AsyncCommand to show the dialog.

I am also worried about what happens if the first command in the Save AsyncMacroCommand fails (ie the remote save process comes back with an error). Can I abort the rest of the command and do something else?

I am prepared to put off detailed worrying until tomorrow.

Will
Logged
Pages: [1]
Print