<div dir="ltr"><div>Hi,</div><div><br></div><div>this is a simple use-case for the chagnes proposed in the pull request 374 (20551-Improve-way-how-Spec-presenters-are-linked-with-domain-models). </div><div><br></div><div><a href="https://github.com/pharo-project/pharo/pull/374">https://github.com/pharo-project/pharo/pull/374</a><br></div><div><br></div><div>I would like to know your comments on it.</div><div><br></div><div>Situation: You have a presenter that shows a form with information about a person. This form contains a submit button and restore button. Next to it it shows a table with currently saved results. It is only a demo so the domain object is stored directly in this presenter.</div><div><br></div><div><font face="monospace, monospace">ComposablePresenterWithModel subclass: #FormPresenter</font><br></div><div><font face="monospace, monospace"><div>    instanceVariableNames: 'form table'</div><div><br></div></font></div><div>The instance variable "form" is a presenter of a form subcomponent, the "table" is a fast table.</div><div><br></div><div>The ComposablePresenterWithModel stores the model in an instance variable named "specCompatibleModel". This can be a Model or a NewValueHolder (as it is a subclass of Model).<br></div><div><br></div><div>We have our domain model class FormModel which contains information about a person like name, surname etc. It is a subclass of Object.</div><div><br></div><div><font face="monospace, monospace">Object subclass: #FormModel</font><br></div><div><font face="monospace, monospace">    instanceVariableNames: 'name surname ...' <br></font></div><div><br></div><div>At the beginning we create a new instance of this class as a model for our presenter:</div><div><br></div><div><div><font face="monospace, monospace">FormPresenter>>initialize</font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><font face="monospace, monospace">self model: FormModel new.<br></font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><font face="monospace, monospace">super initialize.</font></div></div><div><br></div><div>This will create in the instance variable "specCompatibleModel" a value holder that will contain our domain object and subscribes yourself to announcements of this value holder.</div><div>We need to have this model ready before we will initialize subpresenters because we will provide this model to our form.<br></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">FormPresenter>>initializeWidgets<br></font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><font face="monospace, monospace">form := self instantiate: </font><span style="font-family:monospace,monospace">StandaloneFormPresenter on: self specCompatibleModel</span></div><div><span style="font-family:monospace,monospace">    ...</span></div><div><br></div><div><font face="arial, helvetica, sans-serif">We use here "self specCompatibleModel" and not "self model" because we want to use our value holder directly. If we would use "self model", the form would create a new value holder and then we would need to synchronize the data between the form and parent presenter manually.</font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">When the model of the presenter will change, we will fill the table.</font></div><div><font face="monospace, monospace"><br></font></div><div><div><font face="monospace, monospace">FormPresenter>>imodelChanged</font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><span style="font-family:monospace,monospace">table items: { </span><br></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><span style="font-family:monospace,monospace">self model name.</span></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><span style="font-family:monospace,monospace">self </span><span style="font-family:monospace,monospace">model </span><span style="font-family:monospace,monospace">surname. }</span></div></div><div><br></div><div>The presenter for the form is a subclass of ComposablePresenterWithModel too. It contains input boxes for name and surname. Then it includes buttons for submitting and restoring of the form content. It contains an instance variable "workingModel" to store current state of the form. It is different from the model because we want to be able to restore original data.</div><div><br></div><div><font face="monospace, monospace">ComposablePresenterWithModel subclass: #StandaloneFormPresenter</font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><font face="monospace, monospace">instanceVariableNames: 'workingModel nameTextInput surnameTextInput submitButton restoreButton'</font><br></div><div><br></div><div>To create the form is straightforward and there is nothing special on it:</div><div><br></div><div><div><font face="monospace, monospace">StandaloneFormPresenter>>initializeWidgets</font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><font face="monospace, monospace">nameTextInput := self newTextInput autoAccept: true.<br></font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><font face="monospace, monospace">surnameTextInput := self newTextInput autoAccept: true.<br></font></div><div><font face="monospace, monospace"><br></font></div></div><div><div><font face="monospace, monospace">StandaloneFormPresenter>>initializePresenter</font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><font face="monospace, monospace">self submitButton action: [self submit].<span style="white-space:pre">        </span><br></font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><font face="monospace, monospace">self restoreButton action: [self restore].</font></div></div><div><br></div><div>When we obtain a new model, we will create a new working model as copy of it and we will fill the form with its data</div><div><font face="monospace, monospace"><br></font></div><div><div><font face="monospace, monospace">StandaloneFormPresenter>>modelChanged</font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><font face="monospace, monospace">workingModel := self model copy.</font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><font face="monospace, monospace">self fillFormWithWorkingModel.</font></div></div><div><font face="monospace, monospace"><br></font></div><div><div><font face="monospace, monospace">StandaloneFormPresenter>>fillFormWithWorkingModel</font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><font face="monospace, monospace">self nameTextInput text: workingModel name.<br></font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><font face="monospace, monospace">self surnameTextInput text: workingModel surname.</font><span style="white-space:pre"> </span></div></div><div><br></div><div>When we restore the form, we will only do the same as in case of model change - create a new working copy and update the form </div><div><br></div><div><div><font face="monospace, monospace">StandaloneFormPresenter>>restore</font></div><div><font face="monospace, monospace">    self modelChanged</font><br></div><div><br></div><div>When we submit the form we obtain the current data from the inputs, store them in the working model. Then we will replace the model with it and announce change of the model to other components.</div><div><br></div><div><div><font face="monospace, monospace">submit</font></div><div><span style="font-family:monospace,monospace">    </span><font face="monospace, monospace">workingModel name: self nameTextInput text.<br></font></div><div><span style="font-family:monospace,monospace">    </span><font face="monospace, monospace">workingModel surname: self surnameTextInput text.</font></div><div><span style="font-family:monospace,monospace">    </span><font face="monospace, monospace">self model: workingModel.<br></font></div><div><span style="font-family:monospace,monospace">    </span><font face="monospace, monospace">self specCompatibleModel valueChanged.</font></div><div><span style="white-space:pre">   </span></div></div><div>This operation will force update of the parent presenter (that will update the table). The form itself will be updated too (and a new working copy will be created).</div><div><br></div><div>In this simple case we can make it work without need of a the model working copy. In the simpler approach we wil create parent presenter model as a subclass of Model.</div></div><div><br></div><div><div><font face="monospace, monospace">Model subclass: #FormModel</font><br></div><div><font face="monospace, monospace">    instanceVariableNames: 'name surname ...' </font></div></div><div><br></div><div>The instantiation of StandaloneFormPresenter can be done on the model directly but it is optional because "self model" and "self specCompatibleModel" return the same object here.</div><div><br></div><div><font face="monospace, monospace">self instantiate: StandaloneFormPresenter on: self model</font></div><div><br></div><div>When the model will change, we will simply fill the form</div><div><br></div><div><div><span style="font-family:monospace,monospace">StandaloneFormPresenter>></span><font face="monospace, monospace">modelChanged</font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><font face="monospace, monospace">self fillForm</font><br></div></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace"><div>StandaloneFormPresenter>>fillForm</div></font><font face="monospace, monospace"><div>    self nameTextInput text: self model name.<br></div></font><div style="font-family:monospace,monospace">    self surnameTextInput text: self model surname.</div><div style="font-family:monospace,monospace"><br></div><div><font face="arial, helvetica, sans-serif">When the form will be submitted, we fill the model with new data and announce changes.</font></div><div style="font-family:monospace,monospace"><br></div><div><div><span style="font-family:monospace,monospace">StandaloneFormPresenter>></span><font face="monospace, monospace">submit</font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><span style="font-family:monospace,monospace">self model name: self nameTextInput text.</span><br></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><font face="monospace, monospace">self model surname: self surnameTextInput text.</font></div><div><span style="font-family:monospace,monospace">   </span><span style="font-family:monospace,monospace"> </span><font face="monospace, monospace">self model valueChanged.</font></div></div><div style="font-family:monospace,monospace"><br></div><div><font face="arial, helvetica, sans-serif">So the difference here is in absence of the value holder that will store the domain object. Instead of it we use directly announcer that is provided by the Model class and modify the domain object directly.</font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">The original version with the working copy makes more sense in case when you modify the working copy directly on the fly when the user changes data.</font></div><div><font face="arial, helvetica, sans-serif"><br></font></div><div><div><font face="monospace, monospace">    nameTextInput := self newTextInput</font></div><div><font face="monospace, monospace">        autoAccept: true;</font></div><div><font face="monospace, monospace">        whenTextChanged: [ </font></div><div><font face="monospace, monospace">            self workingModel name: nameTextInput text ].</font></div><div><br></div></div><div>Then during submitting you can use the the working copy directly to replace the model.</div><div><div><br></div><div><font face="monospace, monospace">submit</font></div><div><font face="monospace, monospace">    self model: workingModel.<br></font></div><div><font face="monospace, monospace">    self specCompatibleModel valueChanged.</font></div></div><div><br></div><div>Sometimes for more complex applications it may be handy to use value holders with subinstances of Model inside, when you need to subscribe some presenters directly to models and in the same time to be able to swap then (and be notified about it). This combination is possible too, just use code linke:</div><div><br></div><div>self model: (NewValueHolder value: FormModel new).<br></div><div><br></div><div>...but then you need to take care if you are wokring with the value holder or with the model inside.</div><div><br></div><div><div><font face="monospace, monospace">submit</font></div><div><font face="monospace, monospace">    self specCompatibleModel value: self workingModel.<br></font></div><div><font face="monospace, monospace">    self specCompatibleModel valueChanged.</font></div></div><div><br></div><div>So I hope this proposed changes are quite flexible and will make using of Spec much easier.</div><div><br></div><div>I'm not sure with the naming of "specCompatibleModel".<br></div><div><br></div><div>Cheers,</div><div>-- Pavel</div><div><br></div><div><span style="font-family:monospace,monospace;white-space:pre">       </span><br></div></div></div>