Component Suppliers in Willow

I n this fourth post of our series we continue learning how to use Willow to create your web applications. This time it’s all about creating web components regardless of your chosen front-end framework.


How do I create standard components?

When creating our web application, we learned in previous posts that we just need to implement #renderContentOn: in a subclass of WAPainter, which in turn can reference other objects to be rendered. But eventually, we will reach the moment when we will need a standard component, like an input.

For that, a Willow application makes use of its component supplier. Remember that your Willow application (in our To Do example we named it ToDoApplication) must define a #componentSupplierForApplication message. We can then call #componentSupplier inside the #renderContentOn: of any subclass of WAPainter and ask it for objects like anchors, labels, fields, dropdowns, and more.

For example, to add a text input field on our application, we can write the following:

renderInputOn: aCanvas
| field |
field := self componentSupplier
singleLineTextFieldApplying: [ :theField | ].
aCanvas render: field

For now we will ignore the empty block argument, since that is for advanced customization. The important lesson here is that the resulting component is a correct text field for our chosen front-end framework (in this case plain HTML5).

To make sure that the field is rendered as part of the application, let’s update the implementation of ToDoApplication>>#contentView:

contentView
^ [ :canvas |
self renderTasksOn: canvas;
renderInputOn: canvas ]

If we wanted to add a button next to the field, the code required is quite similar:

renderButtonOn: aCanvas
| button |
button := self componentSupplier
asynchronicButtonLabeled: ‘Click me’
applying: [ :theButton | ].
aCanvas render: button
contentView
^ [ :canvas |
self renderTasksOn: canvas;
renderInputOn: canvas;
renderButtonOn: canvas ]

You can check all the protocol available in the component supplier by browsing the methods in its ‘Supplying’ category.

You only need to create components via the supplier when they are dependent on the front-end framework. This can always be complemented with the standard Seaside interface, just by sending messages to the canvas.

How do I change the front-end framework?

As we mentioned in the introduction post, there are other projects in the Willow ecosystem on GitHub that will install additional component suppliers for different front-end frameworks.

To see an example, we will change our design to use Bootstrap 3. First we need to install Willow-Bootstrap by evaluating:

Metacello new
baseline: ‘WillowBootstrap’;
repository: ‘github://ba-st/Willow-Bootstrap:master/source’;
load

Then we must change the component supplier declared by the ToDoApplication:

componentSupplierForApplication
^ BootstrapComponentSupplier online

The next time we reload the application on our browser (at localhost:8080/to-do), you will notice that the input and button will be styled using the default look and feel defined by Bootstrap.

You must remember that each front-end framework might require additional styling to look as intended. You are encouraged to read the corresponding documentation to learn about each of them.

What can I do with the styling block?

Most of the Willow components allow additional styling configuration by means of a component command. This is the case for the input and button messages we used in our examples.

When we sent singleLineTextFieldApplying: the block was just one argument and no content. Here we can send messages to the instantiated web view that represents the component. Let’s add a hint to the field:

renderInputOn: aCanvas
| field |
field := self componentSupplier
singleLineTextFieldApplying:
[ :theField | theField setPlaceholderTo: ‘Write your task’ ].
aCanvas render: field

We can add specific CSS classes using the same technique. This is specially useful for front-end frameworks that give a semantic meaning to some of them. Let’s mark our button as a primary Bootstrap 3 button:

renderButtonOn: aCanvas
| button |
button := self componentSupplier
asynchronicButtonLabeled: ‘Click me’
applying: [ :theButton | theButton addClass
bootstrap buttonPrimary ].
aCanvas render: button

To learn all the different options available for these styling blocks, refer to the methods implemented in WebComponentCommandBuilder.