Rui::View::Base - view abstract base
# using views
$view = $parentWidget->MyView; # use default model $view = $parentWidget->MyView(model => $model); $view->model($model); $model = $view->model;
# subclassing: an on/off view
package MyViews::OnOff; use Rui::Model::Value; use base 'Rui::View::Base';
sub sendModelToClient { my $self = shift; $self->attribute(value => $self->model->value? 'On': 'Off'); }
sub getDefaultModel { Rui::Model::Value->new } sub getDefaultWidgetClass { 'Label' } sub getModelEventNames { qw(Change) } sub onUndefModel { shift->clear }
A view is just a widget, but has some model. If the model is undefined, then the view is disabled. The view listens to model events, and modifies its display accordingly. It listens to events from the user, and modifies its model accordingly.
You can use model()
to get/set the model of the view, and you can set
it as an init key to the constructor. If created without a model, the
view will create its own default model.
The view uses a the Rui::Model::Value manpage as a holder for the model. By
setting the adapt
key in the init hash, you can configure the view so
that its model changes automatically whenever some event propagates
through the value net. This saves you typing as there is no need to
write the event handler that replaces the model, and the attach/detach
mechanism for that event handler.
You may implement the following template methods:
populate
- Will be called when the view is created. Composite views
want to create their children here. Non-composite views will leave this
empty.
sendModelToClient
- Will be called when the model is replaced, or
when the model fires a Change
event. Composite views will do nothing
here, as the value net will propagate to the sub views pieces of the
parent view model automatically. Non-composite views will want to clear
the contents of their client-side widget, and render the model to it.
Non-composites will want to call sync()
when there is a large change
in their model that requires a complete refresh. It calls
sendModelToClient()
if the view model is defined, or disables the
view if not. You should not call sendModelToClient()
.
getDefaultModel
- Should return an object that can be used as a
default model for the view, if none was specified in the constructor.
If you do not write this method the model will be set to undef.
getModelEventNames
- Should return an array of strings. These are
event names on the model, that are deemed interesting by this view. The
framework will add/remove these listeners on the model, automatically
whenever there is a new model. You just need to say what events you need
from the model, and provide event handlers. If your view is a composite,
and its children listen to parts of its model, then there is probably no
need for the composite to implement this method, as it does not need to
listen to its model.
onUndefModel
- Non-composites will want to clear()
themselves
here. Default implementation does nothing.
You must implement the following template methods:
handleEvent_modelHolder_SomeModelEventName
- for every event name you
put in getModelEventNames
, you must add the corresponding event
handler for that model event. Here you want to render any changes from
the model. You do not need to write add a handler for one event:
Change
. The framework will automatically call sendModelToClient()
when this event is called.
Many times a composite view will want to give its children some part of its own model: in MVC there are parallel hierarchies of views and models. This means composite views need to replace the models of their child views when their own model is replaced. The framework does this for you if you do two things:
holder
init()
key, not the model
key. You set the
value to the name of the child model of the model of the composite view,
that you want to give to the child view. Example:
the Rui::View::DoubleList manpage creates a child list view on a part of its
model so:
$self->List(holder => 'left');
Instead of:
$self->List(model => $self->model->left);
Now that the framework knows how to get the holder for the child model from the composite model, it can replace the models of the children views automatically when the composite view model is replaced.
If the view you are creating is not a direct child of this view, you can
use makeChildModelAdapter()
to get an adapter on the child model.
getChildModel_left()
that
returns the selection in list that models the left list. The framework
will call this method to get the new model for the child view when the
model of the composite view is replaced.