Sometimes it can be refreshing to look at the implications, but also the opportunities, that come with a certain technique. By doing so I managed to convince (#ALOY-382) Tony to implement RightNavButton and similar features in Alloy in the way they are now :)
Child elements in <Require>
Likewise, I was looking at how we are using <Require> and <Widget> as self-closing tags. What would happen if you would put elements in them like this?
1 2 3 |
<widget src="my.alloy.widget"> <label>Some label</label> </widget> |
Well, this is what happens right now:
1 |
[ERROR] <require> elements may not have child elements.</require> |
Don’t just stop here!
Thank God for Alloy’s pre-compile syntax validation. But that shouldn’t stop us from thinking why we would want <Require> elements to support child elements. Let’s use my pullToRefresh widget and explore two use cases.
Overriding the widget’s (main) View
The first one I think about is using child elements to override the view we want to be shown as the headerPullView. This allows us to have full control over the appearance of the widget without having to change it’s code. This would fulfill the main need for theming widgets (#ALOY-378).
1 2 3 4 5 6 7 |
<widget src="nl.fokkezb.pullToRefresh" id="myWidget"> <view id="headerPullView"> <imageview id="arrow"></imageview> <activityindicator></activityindicator> <label>Pull down to refresh</label> </view> </widget> |
Wrapping the view to act upon
Another use case could be to wrap the TableView we want the widget to add it’s pull-to-refresh magic too. In the current version of my widget you would need to use the init() method of the widget to do so.
1 2 3 4 5 |
<widget src="nl.fokkezb.pullToRefresh" id="myWidget"> <tableview dataCollection="myCollection" id="myTable"> <tableviewrow title="{myTitle}"></tableviewrow> </tableview> </widget> |
Resulting plain Titanium code
So how would this look once it has been compiled by Alloy?
Let’s start with the second use case. The TableView must end up in the controller where we are requiring the widget and not in that of the widget itself. Otherwise, the collection and other table logic will not work. And since we don’t pass a headerPullView like in the first use case, we need the widget controller to include the Ti.UI code for that and not the code of the table it acts upon. So basically what we need is for the requiring controller to pass the table to the widget, so we can attach the event listeners and headerPullView to it.
1 2 3 4 5 6 7 8 9 |
$.__views.myWidget = Alloy.createWidget("nl.fokkezb.pullToRefresh", "widget", { id: "myWidget" }); $.__views.myWidget.setParent($.__views.myWindow); $.__views.myTable = Ti.UI.createTableView({ id: "myTable" }); // Further TableView UI will go here $.__views.myWidget.setChildren([$.__views.myTable]); |
Pretty simple isn’t it? The markup is more descriptive and the configuration of the widget more simple (init-less).
The other use case is a little more challenging. On one hand, we would like the child elements to be constructed in the requiring controller so we can also apply it’s TSS styles to it. On the other hand we should leave the responsibility over the elements to the widget controller. I think the best solution would be to have the Alloy compiler apply the requiring controller’s TSS to the child elements, but put the resulting Ti.UI code in the widget controller, replacing the Ti.UI code of it’s own main view. Does that make sense to you?
All-in-one
In both use case examples we’ve only put a single child element directly under the <Widget>. But of course there could be more on the same level. This might pave the way for an all-in-one solution to both use cases.
Imagine supporting a <RightNavButton> like wrapper. In the example code below <WidgetView> would replace the widget’s default main view by the one wrapped like we did in our second use case. The other view or views next to it, like the TableView, would still be passed to the widget using setChildren().
1 2 3 4 5 6 7 8 9 10 11 12 |
<widget src="nl.fokkezb.pullToRefresh"> <widgetview> <view id="headerPullView"> <imageview id="arrow"></imageview> <activityindicator></activityindicator> <label>Pull down to refresh</label> </view> </widgetview> <tableview dataCollection="myCollection" id="myTable"> <tableviewrow title="{myTitle}"></tableviewrow> </tableview> </widget> |
Feedback
So what do you guys think? I love to here your thoughts on this. Does it make sense to you? Are there more use cases for allowing child elements you could think of?
Let’s discuss this more on the TiAlloy Google Group and we’ll see if we should open a JIRA ticket for this.