View not updated, even though presenter is updated

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

View not updated, even though presenter is updated

Fernando Rodríguez
Hi,

I have a simple Person class with 2 instance variables (firstname and
surname) and its 2 accessors. It also has a #name method (and that's
where I got in trouble) that simply return the concatenation of both:

name
        ^firstname, ' ', surname.

I created a presenter (descending from Shell) with 3 sub presenters:
firstnamePresenter, surnamePresenter and namePresenter.

I want the view that displays the name to update whenever the user
changes the text in the surname or firstname views. So, I added the
following code PersonShell

createSchematicWiring
        firstnamePresenter
                when: #valueChanged
                send: #onNameChanged
                to: self.
        surnamePresenter
                when: #valueChanged
                send: #onNameChanged
                to: self.

onNameChanged
        "namePresenter should raise #valueChanged and update its view
(hopefylly)..."
        namePresenter value: model name.

If I try this, by showing it in a view with 3 textboxes linked to the
3 textpresenters, and change the surname in its textbox, the model is
updated, but the textbox displaying name remains unchanged...

What am I doing wrong???

This is the full pac:
------------------------------------------------------------------------------------
| package |
package := Package name: 'test'.
package paxVersion: 0;
        basicComment: ''.


package classNames
        add: #Person;
        add: #PersonShell;
        yourself.

package binaryGlobalNames: (Set new
        yourself).

package globalAliases: (Set new
        yourself).

package allResourceNames: (Set new
        add: #PersonShell -> 'Default view';
        yourself).

package setPrerequisites: (IdentitySet new
        add: '..\..\Documents and Settings\Fernando\Mis
documentos\Dolphin Smalltalk 5.1\Object Arts\Dolphin\Base\Dolphin';
        add: '..\..\Documents and Settings\Fernando\Mis
documentos\Dolphin Smalltalk 5.1\Object Arts\Dolphin\MVP\Base\Dolphin
MVP Base';
        add: '..\..\Documents and Settings\Fernando\Mis
documentos\Dolphin Smalltalk 5.1\Object Arts\Dolphin\MVP\Type
Converters\Dolphin Type Converters';
        yourself).

package!

"Class Definitions"!

Model subclass: #Person
        instanceVariableNames: 'firstname surname'
        classVariableNames: ''
        poolDictionaries: ''
        classInstanceVariableNames: ''!
Shell subclass: #PersonShell
        instanceVariableNames: 'namePresenter firstnamePresenter
surnamePresenter'
        classVariableNames: ''
        poolDictionaries: ''
        classInstanceVariableNames: ''!

"Global Aliases"!


"Loose Methods"!

"End of package definition"!

"Source Globals"!

"Classes"!

Person guid: (GUID fromString:
'{AF6E4143-DF83-4BF7-9A02-FAB8CAA0F9D3}')!
Person comment: 'Clase de prueba'!
!Person categoriesForClass!Kernel-Objects! !
!Person methodsFor!

firstname
        ^firstname!

firstname: anObject
        firstname := anObject!

name
        ^firstname, ' ', surname.!

surname
        ^surname!

surname: anObject
        surname := anObject! !
!Person categoriesFor: #firstname!accessing!private! !
!Person categoriesFor: #firstname:!accessing!private! !
!Person categoriesFor: #name!public! !
!Person categoriesFor: #surname!accessing!private! !
!Person categoriesFor: #surname:!accessing!private! !

!Person class methodsFor!

new
        ^super new initialize.! !
!Person class categoriesFor: #new!public! !

PersonShell guid: (GUID fromString:
'{1C3E56A4-8CE7-43AD-8EA2-95E7701FEE5C}')!
PersonShell comment: ''!
!PersonShell categoriesForClass!MVP-Presenters! !
!PersonShell methodsFor!

createComponents
        super createComponents.
        namePresenter := self add: TextPresenter new name: 'name'.
        firstnamePresenter := self add: TextPresenter new name:
'firstname'.
        surnamePresenter := self add: TextPresenter new name:
'surname'.!

createSchematicWiring
        firstnamePresenter when: #valueChanged send: #onNameChanged
to: self.
        surnamePresenter when: #valueChanged send: #onNameChanged to:
self.
        !

model: aPerson
        super model: aPerson.
        namePresenter model: (self model aspectValue: #name).
        firstnamePresenter model: (self model aspectValue:
#firstname).
        surnamePresenter model: (self model aspectValue: #surname).!

onNameChanged
        namePresenter value: model name.! !
!PersonShell categoriesFor: #createComponents!public! !
!PersonShell categoriesFor: #createSchematicWiring!public! !
!PersonShell categoriesFor: #model:!public! !
!PersonShell categoriesFor: #onNameChanged!public! !

!PersonShell class methodsFor!

defaultModel
        ^Person new.! !
!PersonShell class categoriesFor: #defaultModel!public! !

"Binary Globals"!

"Resources"!

(ResourceIdentifier class: PersonShell name: 'Default view') assign:
(Object fromBinaryStoreBytes:
(ByteArray fromHexString:
'2153544220312046020C0001000000566965775265736F75726365000000000E0124005354425265736F757263655354424279746541727261794163636573736F7250726F7879000000007200000024070000215354422031204E080C000A0000005354425669657750726F7879000000009A000000000000005200000010000000446F6C7068696E204D5650204261736552000000090000005368656C6C56696577620000001B0000000000000000000000620000000200000001009E0101000200A00100000000000006010B0053797374656D436F6C6F72000000001F0000000000000007020000000000000000000000000000A001000000000000EA000000000000000001000062000000060000009A010000000000009A00000000000000C001000052000000080000005465787445646974620000001000000000000000A0010000620000000200000082000000040000008000014401040000400200000000000000000000000000000700000000000000000000000000000040020000000000008200000008000000C706FFFF0000000006020D004E756C6C436F6E7665727465720000000000000000000000000100000006010F004D65737361676553657175656E636500000000CA00000000000000D0000000620000000300000006030B004D65737361676553656E64000
00000BA00000000000000520000001000000063726561746541743A657874656E743A620000000200000006020500506F696E7400000000330000002900000062030000000000002D01000033000000400200001203000000000000BA00000000000000520000000F00000073656C656374696F6E52616E67653A620000000100000006030800496E74657276616C00000000030000000100000003000000400200001203000000000000BA00000000000000520000000F0000006973546578744D6F6469666965643A6200000001000000200000004002000006010F0057494E444F57504C4143454D454E5400000000720000002C0000002C0000000000000001000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1900000014000000AF0000002D000000CA00000000000000D000000062000000000000006203000000000000C1000000C10000000000000013000000520000000900000066697273746E616D659A0100000000000050020000620000001000000000000000A0010000620000000200000082000000040000008000014401040000A004000000000000000000000000000007000000000000000000000000000000A0040000000000008200000008000000C706FFFF00000000B202000000000000000000000000000001000000D202000000000000CA00000000000000D00
000006200000003000000120300000000000030030000620000000200000062030000000000007D0100002900000062030000000000000F01000033000000A00400001203000000000000A00300006200000001000000D203000000000000030000000100000003000000A0040000120300000000000000040000620000000100000020000000A00400003204000000000000720000002C0000002C0000000000000001000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE00000014000000450100002D000000CA00000000000000D00000007004000080040000000000001300000052000000070000007375726E616D659A0100000000000050020000620000001000000000000000A0010000620000000200000082000000040000008000014401040000000600000000000000000000000000000700000000000000000000000000000000060000000000008200000008000000C706FFFF00000000B202000000000000000000000000000001000000D202000000000000CA00000000000000D0000000620000000300000012030000000000003003000062000000020000006203000000000000330000008D00000062030000000000005902000033000000000600001203000000000000A00300006200000001000000D20300000000000003000000010000000300000000060000120
300000000000000040000620000000100000020000000000600003204000000000000720000002C0000002C0000000000000001000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1900000046000000450100005F000000CA00000000000000D00000007004000080040000000000001300000052000000040000006E616D6500000000000000000000000000000000000000000100000000000000000000000000000000000000010000000000000000000000D202000000000000CA00000000000000D00000006200000002000000120300000000000030030000620000000200000062030000000000000B0000000B0000006203000000000000A5040000C7010000A00100001203000000000000BA0000000000000052000000080000006D656E754261723A620000000100000000000000A00100003204000000000000720000002C0000002C0000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF050000000500000057020000E8000000CA00000000000000D0000000620000000300000040020000A004000000060000800400000000000015000000460504000300000049636F6E0000000000000000100000000E02110053544253696E676C65746F6E50726F7879000000009A000000000000005200000007000000446F6C7068696E5200000018000000496D61676
552656C617469766546696C654C6F6361746F72BA00000000000000520000000700000063757272656E74520000000D0000005368656C6C566965772E69636F0E021F0053544245787465726E616C5265736F757263654C69627261727950726F7879000000005200000010000000646F6C7068696E64723030352E646C6C00000000'))!


Reply | Threaded
Open this post in threaded view
|

Re: View not updated, even though presenter is updated

Ian Bartholomew-19
Fernando,

> What am I doing wrong???

You're quite close.  There are three problems.

1) The #createSchematicWiring event should do a supersend first - it's
important.

2) Your model needs an #initialize to set up the instance variables to empty
Strings.  It will prevent walkbacks.

3) The one that's difficult to explain :-)

The solution is easy - remove the following line from the shell's #model
method.

 namePresenter model: (self model aspectValue: #name).

The reason why this was causing the problem.  It's easiest to explain by
putting a breakpoint in the #onNameChanged method and then make a few single
steps.  The breakpoint/single step sequence (after entering a string in the
firstname field and tabbing) is ....

#onNameChanged
step over "model name"
step into "namePresenter value: model name"
step over "self model"
step into "self model value: anObject"

now, look at the first test in the next method (and yes, we've been here
before in connection with ListPresenters).  It tests to see if the current
value being displayed is the same as the new value and, if so, doesn't do
anything more.

OK, step into "self value"
step into "self getValue"

We are now at the point where we are getting the current value.  Note that
it gets the current value by sending #name to the TextEdits model (i.e. a
Person instance).  It does this because we changed the 'name' TextEdit's
model to be a wrapper around your model's #name method (see the line above
that I suggest removing).

So go back to the comparison a couple of steps ago.  This is therefore
comparing the value returned by sending #name to the model with the value
returned by sending #name to the model.  Not surprisingly they are identical
so no update of the 'name' view takes place.


If you remove the line from the #model method that links the #name method to
the namePresenter (it can't work anyway as there is no #name: accessor in
the model) then all will work as the #getValue method above will get the
value from the TextPresenters default model, and as that will contain a
different (empty) string the view update will work.

--
Ian

Use the Reply-To address to contact me.
Mail sent to the From address is ignored.


Reply | Threaded
Open this post in threaded view
|

Re: View not updated, even though presenter is updated

Fernando Rodríguez
On Mon, 21 Mar 2005 18:18:16 -0000, "Ian Bartholomew"
<[hidden email]> wrote:

[snip]

Thank you very much, it was driving me crazy. Looks like it's gonna
take some time to get used to this MVP thing...

>If you remove the line from the #model method that links the #name method to
>the namePresenter (it can't work anyway as there is no #name: accessor in
>the model) then all will work as the #getValue method above will get the

I just ran into this issue too. :-) I did it by making the PersonShell
a subclass of Dialog. As soon as I clicked on the 'ok' button I got a
DNU complaining about the lack of a #name: method. Yuck...

What's the best way to present read-only aspects such as #name?
Wouldn't it make sense to have read-only presenters for this sort of
thing?


Reply | Threaded
Open this post in threaded view
|

Re: View not updated, even though presenter is updated

Chris Uppal-3
Fernando.

> Looks like it's gonna
> take some time to get used to this MVP thing...

If it's any comfort, the behaviour you found surprised me too, and I had to
follow Ian's explanation pretty closely to understand it (the behaviour, not
Ian's explanation ;-)

Another way of "fixing" the problem would be to leave PersonShell>>model:
unchanged, but to modify #onNameChanged so that it just notified the
sub-component that the 'virtual' #name aspect should be re-computed.

    onNameChanged
        namePresenter model trigger: #valueChanged.

which you might find more intuitive if you like using value aspect adaptors.

Of, course that would still suffer from the Dialog problem since there is no
#name:.  On that subject, I rather think that there's a bug in the Dialog
framework there.  The class comment for AspectBuffer states (my emphasis):

    Only the aspects that have been explicitly requested AND THEN MODIFIED
    will be updated back to the subject when #apply is received.

I never use Dialogs myself (I don't like user interfaces with modal dialogs --
except the obvious File-open and so on), but if I'm reading the code correctly,
the AspectBuffer will attempt to write back the value of any aspect that has
been requested regardless of whether it has also been modified.


> What's the best way to present read-only aspects such as #name?
> Wouldn't it make sense to have read-only presenters for this sort of
> thing?

As I say, I never use Dialogs, but it looks as if overriding Dialog>>apply
might be the easiest thing to do.

A generic (and untested) implementation:

    apply
        aspects do: [:each |
            each canSet ifTrue:
                [self subject
                    perform: each putSelector
                    with: each value]].

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: View not updated, even though presenter is updated

Ian Bartholomew-19
In reply to this post by Fernando Rodríguez
Fernando,

> I just ran into this issue too. :-) I did it by making the PersonShell
> a subclass of Dialog. As soon as I clicked on the 'ok' button I got a
> DNU complaining about the lack of a #name: method. Yuck...

It's correct though.  A Dialog will update it's model when the OK button is
pressed and you told it, by linking the #namePresenter model with the #name
accessor in the Person, to do this by sending #name: to the model.

> What's the best way to present read-only aspects such as #name?
> Wouldn't it make sense to have read-only presenters for this sort of
> thing?

There is.  In the ViewComposer you can set the #isReadOnly aspect for a
presenter to #true or use a StaticText rather than a TextEdit.  In both
cases user input is not allowed so the presenter (normally) never tries to
update the model.

The easiest way to prevent the linkage is to just not connect the
TextPresenters model to your model and control any update yourself, via
events triggered off of your model or the presenters (the way you were
trying to do).

--
Ian

Use the Reply-To address to contact me.
Mail sent to the From address is ignored.