Navigation:  Tutorials > Creating a GUI Application >

PersonalAccountTransactionDialog: a Presenter for PersonalAccountTransaction

Previous pageReturn to chapter overviewNext page

We now want to create a presenter that handles modal editing of PersonalAccountTransaction instances. The abstract classes Dialog and ValueDialog are suitable superclasses for this kind of modal editing. Both provide the ability to buffer the edit changes until the dialog is confirmed, usually by pressing an OK button.

Tip: choose to subclass from Dialog if the model you are editing is mutable, that is, you are expecting to change the contents of the object in place. Often, however, you will be dealing with an immutable object which should not be modified directly. In such cases create your new class beneath ValueDialog and the standard #value protocol will be used to fetch the initial value for editing and to set the resultant value back into the model.

For our PersonalAccountTransactionDialog we'll choose to edit the contents of an existing instance, ie we'll subclass Dialog to do this.

Dialog subclass: #PersonalAccountTransactionDialog

       instanceVariableNames: 'datePresenter amountPresenter descriptionPresenter isDebitPresenter '

       classVariableNames: ''

       poolDictionaries: ''

 

Write the createComponents method to create the sub-presenters that are needed to edit a transaction model and then the model: method that connects these to the various aspects of the transaction.

createComponents

       "Private - Create the presenters contained by the receiver"

 

       super createComponents.

       datePresenter := self add: DatePresenter new name: 'date'.

       amountPresenter := self add: NumberPresenter new name: 'amount'.        

       descriptionPresenter := self add: TextPresenter new name: 'description'.

       isDebitPresenter := self add: BooleanPresenter new name: 'isDebit'.

 

model: aPersonalAccountTransaction

       "Set the model associated with the receiver."

 

       | aspectBuffer |

       super model: aPersonalAccountTransaction.

       aspectBuffer := self model.

       datePresenter model: (aspectBuffer aspectValue: #date).

       amountPresenter model: (aspectBuffer aspectValue: #amount).

       descriptionPresenter model: (aspectBuffer aspectValue: #description).

       isDebitPresenter model: (aspectBuffer aspectValue: #isDebit).

 

Tip: note that the model being assigned here is not a direct instance of PersonalAccountTransaction as we might expect, but instead it is an instance of the class AspectBuffer. This is created automatically by the Dialog framework to wrap the true subject model so that changes to it are buffered until they are explicitly applied back. For the most part, the aspect buffer can be treated in much the same way as the true subject model would have been, especially when requesting ValueAspectAdaptors onto its various aspects using #aspectValue:.

Add the defaultModel class method to indicate that this presenter should have an instance of PersonalAccountTransaction as its model by default. Once again, there is no need to override the defaultView method providing we ensure that the view is saved under the name "Default view".

defaultModel

       "Answer a default model to be assigned to the receiver when it

       is initialized."

       ^PersonalAccountTransaction new

 

This is all the code we need to write to implement the PersonalAccountTransactionDialog. However, we can now go back to PersonalAccountShell and add the missing methods for newTransaction and editTransaction theat we provided stubs for earlier.

newTransaction

       "Prompt for a new transaction and add it to the receiver's model"

 

       | newTransaction |

       (newTransaction := PersonalAccountTransactionDialog showModal)

               notNil ifTrue: [

                       self model addTransaction: newTransaction.

                       self selectedTransactionOrNil: newTransaction ]

 

Notice that, here, we are relying on the PersonalAccountTransactionDialog creating and editing a default model (that specified by the defaultModel class method). The resultant transaction object will be answered from showModal if OK is pressed, otherwise nil will be answered if Cancel is pressed.

editTransaction

       "Edit the selected transaction"

 

       | transaction |

       transaction := self selectedTransactionOrNil.

       transaction notNil ifTrue: [

               self model removeTransaction: transaction.

               PersonalAccountTransactionDialog showModalOn: transaction.

               self model addTransaction: transaction.

               self selectedTransactionOrNil: transaction ]

 

In this case we are editing an existing selected transaction. The transaction is removed temporarily from the account and a PersonalAccountTransactionDialog is created and shown with this as its model. The dialog displays a copy of the model and any changes made are only written back to it if the dialog is confirmed. Otherwise, the original transaction object is left untouched. The resultant transaction is then added back into the account which causes the account balance to be updated appropriately.

Tip: there are a number of instance creation methods in the Presenter class that help you create presenters in a variety of different ways. You should review these to know what's available; look at the class methods of Presenter in the "instance creation" category.