More thoughts…
—peter
On 8/17/16, 7:10 PM, "Alex Harui" <[email protected]> wrote:
>OK, I think I get it now. The key thing is that the "View" isn't really a
>view like a ButtonView or CheckBoxView is to Button or Checkbox. Instead,
>a View is a top-level component.
>
>Application is a top-level component. It has a "model", "controller", and
>"initialView" property. You were able to eliminate circularities by
>having the 3 rules below because the controller is referencing the
>"initialView" and "model" and not the Application (as it should).
>
>The equivalent for StackedViewManager may require terms borrowed from the
>Flex SDK: "navigator" and "navigatorContent". If StackedViewManager is
>actually a StackNavigator and there was a NavigatorContent tag with view
>and controller, that would be more similar to the Application pattern:
Right now in the Mobile project framework, the equivalent of
NavigatorContent is the StackedViewManager's StackedViewManagerView bead.
The view bead is responsible for the layout and creation of the stack, so
it manages the content. If the intent is to have NavigatorContent have its
own view and controller, then in the pay-as-you-go system, we'd need
StackedNavigatorContent or maybe just SavedNavigatorContentView (or
NavigatorContentStackedView and NavigatorContentTabbedView) so it would
know how to do that layout. In other words, push the responsibility of the
Stack and Tab view managers down to a subordinate class. I'm not sure this
would make a difference as the StackedViewManager already does this and
has its own view; it does not have a controller because the pushing and
popping of the views are taken care of via function calls. The
TabbedViewManager does have a controller tho.
This is a tricky problem.
>
><StackedNavigator>
> <NavigatorContent title="Watch List" dataModel="{applicationModel}">
> <view>
> <View>
> UI Widgets
> </View>
> </view>
> <controller>
> <WatchListController />
> </controller>
> </NavigatorContent>
></StackedNavigator>
>
>And I think that would also eliminate circularities because the controller
>would be referencing the View and not the NavigatorContent.
>
>Thoughts?
>-Alex
>
>On 8/17/16, 2:29 PM, "Peter Ent" <[email protected]> wrote:
>
>>
>>
>>On 8/17/16, 5:09 PM, "Alex Harui" <[email protected]> wrote:
>>
>>>Peter,
>>>
>>>I like the idea of declaring the controller as a bead in the view.
>>>
>>>But shouldn't the controller be able to know about (have references to)
>>>the View as long as the View doesn't know about the controller (which it
>>>shouldn't, IMO).
>>As soon as I added the controller has a bead, the circular dependency was
>>formed. I had started with the previous changes where I had removed the
>>circular dependency - the controller was just referenced in the CSS.
>>>
>>>The view should dispatch events (for example, converting a "click" on a
>>>checkbox to "LicenseAcceptanceCheckboxSelectionChange" but it should be
>>>ok
>>>for the controller to have a reference to the view to access data from
>>>the widgets, validate them, and then apply them to the model. Passing
>>>data via events means copying data and references and the controller
>>>code
>>>might need other values from other widgets in the view. It also means
>>>fewer custom event classes.
>>
>>I thought using custom events was a nice way to separate the concerns.
>>What do other people think - do you use a lot of custom events in your
>>application code or do you get some simple event and then go grab the
>>data
>>out of the controls?
>>
>>>
>>>IMO:
>>>Models should not reference views or controllers
>>>Views should reference models but not controllers
>>>Controllers should reference models and views.
>>
>>I don't see how you can remove the dependency when the View MXML explicit
>>references the Controller MXML in the <js:beads> list. Perhaps we have
>>something wrong in the fundamentals then.
>>
>>>
>>>I think there aren't circularities in that set.
>>>
>>>Thoughts?
>>>-Alex
>>>
>>>On 8/17/16, 1:46 PM, "Peter Ent" <[email protected]> wrote:
>>>
>>>>This is what I'm concluding in MobileTrader:
>>>>
>>>>MobileTrader has "sub-views" for WatchList and Alerts. Each of these
>>>>sub-views has its own controller (WatchListController,
>>>>AlertViewController) which are responsible for taking events from the
>>>>view
>>>>and updating the model.
>>>>
>>>>The first task is to get the controller to know about the model. We are
>>>>talking about the model holding the data, not the FlexJS "model" which
>>>>most components have that hold values for the component's properties.
>>>>So
>>>>I'm calling this the "dataModel". The circular dependencies are arising
>>>>because the controllers are directly references their views and
>>>>grabbing
>>>>data out of the view.
>>>>
>>>>I removed the attachment of the controller to the sub-view from the
>>>>CSS.
>>>>In the view (eg, WatchListView), I explicitly name the controller bead
>>>>and
>>>>use data binding to pass in the model the controller needs to use.
>>>>
>>>><js:beads>
>>>> <js:ViewDataBinding>
>>>> <controller:WatchListController dataModel="{dataModel}" />
>>>></js:beads>
>>>>
>>>>(Note: The WatchListView's dataModel is set using data binding from the
>>>>outer MyInitialView).
>>>>
>>>>Then I removed all references to WatchListView from
>>>>WatchListController.
>>>>This caused several errors because WatchListController was referencing
>>>>the
>>>>view to get to the data on the screen which needed. For example, it
>>>>needed
>>>>the symbol to watch:
>>>>
>>>>view.symbolName.text
>>>>
>>>>Since WatchListController now knows nothing about the view, I created
>>>>custom events for the WatchListView to dispatch that contained the data
>>>>the controller needs. So when the Set button is picked, the button's
>>>>event
>>>>handler in WatchListView now does:
>>>>
>>>>var event: new AddSymbolEvent(symbolName.text);
>>>>dispatchEvent(event);
>>>>
>>>>The WatchListController is looking for this event and can use the
>>>>symbol
>>>>value from the event without referring to the view at all.
>>>>
>>>>I applied this technique to the AlertsView and AlertsViewController.
>>>>All
>>>>in all, the code got cleaner and more readable. The view takes care of
>>>>updating the view UI components and the controller takes care of
>>>>updating
>>>>the model.
>>>>
>>>>The pattern now works something like this:
>>>>
>>>>1. Give the sub-view controller bead bindable get/set methods for the
>>>>dataModel it needs. Don't forget to add the ViewDataBinding bead to the
>>>>sub-view class.
>>>>2. Create custom events to pass data from the sub-view controls over to
>>>>the controller.
>>>>3. Have the controller set up event listeners for these custom events
>>>>in
>>>>its strand-setter; the "strand" is really the sub-view.
>>>>4. When the view receives some user interaction, have it create a
>>>>custom
>>>>event with the data and dispatch it.
>>>>5. The controller will receive the custom event and modify the model.
>>>>6. The view can update the UI controls (eg, textField.text = "") after
>>>>sending the event.
>>>>
>>>>For simpler projects, like the TodoListSample or DataBindingExample,
>>>>the
>>>>controller can implement IDocument and, when the "document" (aka,
>>>>Application) is set, it can get the reference to the application data
>>>>model and set up listeners for it.
>>>>
>>>>I will be checking these changes in shortly today or tomorrow (USA,
>>>>EDT)
>>>>morning.
>>>>
>>>>‹peter
>>>>
>>>>On 8/16/16, 8:33 PM, "Alex Harui" <[email protected]> wrote:
>>>>
>>>>>
>>>>>
>>>>>On 8/16/16, 2:20 PM, "Peter Ent" <[email protected]> wrote:
>>>>>
>>>>>>
>>>>>>Hi,
>>>>>>
>>>>>>I wanted to post what I've done before I commit the code to get an
>>>>>>idea
>>>>>>if
>>>>>>this is the right approach.
>>>>>>
>>>>>>Background: a circular dependency is when class A references class B
>>>>>>which
>>>>>>references class B. Pretty simple.
>>>>>>
>>>>>>In many of the FlexJS examples, there are circular dependencies
>>>>>>between
>>>>>>the application class and the application's controller. For example,
>>>>>>DataBindingExampleApp references its controller, MyController, which
>>>>>>has
>>>>>>a
>>>>>>reference back to the application. Likewise, the TodoListSampleApp
>>>>>>references its controller which holds a reference to the app class.
>>>>>>The
>>>>>>objective in both examples is provide the controller with a reference
>>>>>>to
>>>>>>the model.
>>>>>>
>>>>>>In both of these cases, my solution is to remove the explicit
>>>>>>reference
>>>>>>to
>>>>>>the application (eg, DataBindingExample) in the controller and
>>>>>>replace
>>>>>>it
>>>>>>with the Application class and then extract what it needs into local
>>>>>>variables.
>>>>>
>>>>>The above sounds like a good plan.
>>>>>
>>>>>>However, MobileTrader is bit trickier. MobileTrader has several
>>>>>>mobile
>>>>>>"views" and each has its own controller. The circular dependency in
>>>>>>this
>>>>>>case is between these secondary views and their respective
>>>>>>controllers.
>>>>>>The way I approached this was to make a new interface in the
>>>>>>MobileTrader
>>>>>>example src, called "IBeadControllerWithModel" which extends
>>>>>>IBeadController and adds a getter/setter for a model. Now the view
>>>>>>can
>>>>>>simple reference its controller using this new interface and set the
>>>>>>model
>>>>>>so the controller can now modify it.
>>>>>
>>>>>I don't know this code very well, but I poked around a bit and it
>>>>>appears
>>>>>the view knows about the controller only because it is assigning the
>>>>>model
>>>>>to the controller. IMO, the controller should pick up the model from
>>>>>the
>>>>>strand. If it is a timing issue where the model isn't set when the
>>>>>controller is put on the strand, the controller should be able to look
>>>>>for
>>>>>a modelChange event or maybe even initComplete.
>>>>>
>>>>>Thoughts?
>>>>>-Alex
>>>>>
>>>>
>>>
>>
>