Out of the box, Mulberry’s great at making simple content-driven apps, but one of the things we heard loud and clear during the closed alpha was that developers want to use Mulberry to make more complex, data-driven apps as well. No worries – Mulberry has that covered too.
In this post, we’ll take a look at what’s involved in creating a Twitter app –
Twitter is the new “hello world”, after all. In the process, we’ll learn about
creating custom components, templates, interactions, and routes. You can
follow along by downloading Mulberry and
heading to the demos/twitter directory.
To start, we’ll create a new Mulberry project named “twitter”. Once you’ve
downloaded Mulberry and added the location of the binary to your $PATH, you
can run the following command from any location on your filesystem where you
have write permission:
1
| |
This will create a new directory named twitter; you’ll want to cd twitter
so you’re inside the project as you follow along.
An Overview of the App
The app we’re building will have two pages:
- A home page that shows a list of people, and allows users to choose from the list; choosing a person from the list will show a map of the city where that person lives, their latest tweet, and their Twitter bio with a link to see all of their tweets.
- A secondary page that shows a person’s 10 latest tweets.
Creating the Data
Since this is a data-driven app, the first thing we need to do is provide it with some seed data to drive it. We’ll run this command from the root of our Mulberry project:
1
| |
This creates a data file named users.yml in the assets/data/ directory
inside your project. It’s an empty file, so we’ll add some information about
our users to the file, in the YAML format:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
Note that there are two pieces to this data: a type property, and a users
array that contains the actual data. The type property will help us locate
the data later; its presence means that a page can have easy access to many
different kinds of data.
Now that we’ve added this data to our project, we can move on to setting up the home page.
Creating the Home Page
When you scaffold a Mulberry app, a home page is automatically created in your
project’s pages/ directory. By default, this page uses the home-tablet and
home-phone templates, but we’ll want to change that, as well as tell the page
that it should have access to the data we created. Here’s what our home.md
file looks like when we’re done:
1 2 3 4 5 6 | |
Next, let’s take a closer look at the mockup of the home page. There are four distinct pieces of functionality on the page, and in Mulberry we refer to these pieces of functionality as “components”:

Mulberry has a GoogleMap component built in, but we’ll need to create custom components for the rest:
1
| |
Running this command creates skeleton files in the project’s
javascript/components directory for each of the components, and adds
dojo.require statements to the project’s javascript/base.js file so that
the components will automatically be available to the rest of your code.
Writing Our Custom Components
Components in Mulberry have three jobs:
- receiving data from an external source
- rendering that data
- announcing user interaction with the component, if applicable
Mulberry automatically provides every component on a page with the information
associated with that page; it puts that information into a component’s
baseObj property. This means that our components will automatically get
access to the user data we associated with the home page.
Because we remembered to add a type property to that data, fetching the array
of people in the users.yml file from inside a component is easy:
1
| |
Let’s look at the UserList component as an example. Its job is to receive data (in this case, a list of people); render that data (in this case, as an unordered list), and then announce user interaction (in this case, when a user selects a person from the list).
Here’s what our UserList component ends up looking like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | |
The prep and init methods are called automatically; the prep method is
called before the component’s DOM structure is created, and the init method
is called after its DOM structure is created.
In the case of the UserList component, we use the prep method to prepare the
data associated with the page. Then, in the init method, we tell the component to
listen for user interaction – in this case, a click on the component’s root
DOM node. A click on that node will result in the component’s _handleClick
method being called; if _handleClick decides that the click was on a list
item, then it will call the component’s onSelect method, passing it the
username of the person who was selected.
Note that the onSelect method doesn’t do anything. Later in this post,
we’ll see how we can “connect” to that method from another part of our code,
but it’s important to understand this key concept in Mulberry apps: components
should never directly affect other pieces of the application. Their job is to
receive, render, and announce – nothing more.
Some of the components in our app will need to receive data once they’re
already on the page. We can use “setter methods” to make this possible. For
example, the UserInfo component should be able to display a different user
without having to re-create the component from scratch. To allow this, we
create a _setUserAttr method on the UserInfo component
1 2 3 4 5 6 7 8 9 10 11 | |
This means that any code that has access to an instance of the UserInfo component can do the following:
1
| |
Whenever the set method is called on a component instance, the component
looks for a setter method that matches the property name passed as the first
argument to set. If it finds a corresponding method, it calls it; if it does
not find a corresponding method, then it simply sets the value of a property on
the component instance.
(This functionality is based entirely on Dojo’s dijit._WidgetBase –
see how it works here.)
In the UserInfo component, we refer to several properties that do not seem to
be defined anywhere: this.nameNode, this.bioNode, etc. These properties get
set automatically by the component’s template using “attach points.” Here’s the
UserInfo template, located at javascript/components/UserInfo/UserInfo.haml:
1 2 3 4 5 | |
Using attach points greatly reduces the need for querying the DOM; you can create an attach point on any node, and then refer to that node from inside your component by using the attach point’s name:
1
| |
Creating the Page Template
Once we’ve created our components (see the demo in the repo for details on how
the rest of the components are set up), it’s time to assemble them into a page
template. Earlier, we told our home page to use the twitter template; now,
we’ll ask Mulberry to create that template for us:
1
| |
This creates a file at templates/twitter.yml that contains the skeleton of a
template. We’ll fill it out with the details about how we want our page to
look:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Again, we use YAML here, this time to say that we want a page template named “twitter” that has one screen named “index” (page templates can have more than one screen, with the intention that only one screen is visible at any time, but the details of that are beyond the scope of this post). That screen is broken down into regions:
- The first region will be a fixed height, and it will contain our custom LatestTweet component.
- The second region will be a fixed height, and it will contain the built-in GoogleMap component.
- The third region will be split into two sub-regions, which will be displayed
as columns (that is, side-by-side). The first sub-region will contain the
custom UserList component, and will get the class name
user-listso that we can target it with CSS; the second sub-region will contain the custom UserInfo component, and will get the class nameuser-info. The sub-region that contains UserList will be fixed-width; the UserInfo sub-region will flex to fill the remaining size.
At this point, we should be able to serve our application using the Mulberry development server:
1
| |
If you navigate to the home page, you’ll see that all of the components display in the proper arrangement, but not much is happening yet. We need some data.
Loading the External Data
We want to create an interface to the Twitter data we’ll need in order to populate our pages. For our home page, we’ll need a user’s latest tweet, as well as their bio; for our secondary page, we’ll need a users 10 most recent tweets. We can start by asking Mulberry to create a skeleton file for our datasource:
1
| |
This creates a file at javascript/data/Twitter.js, and adds a dojo.require
statement to our project’s javascript/base.js file. There’s not much here
(we’re working on a more elaborate API for remote data sources), so for now
it’s up to us to fill in the details:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | |
There’s not much that’s interesting here, except that this code takes advantage
of the fact that all async methods in Dojo – such as dojo.io.script.get –
return a “promise.” Promises are incredibly useful structures that greatly
facilitate the development of asynchronous processes. A promise is, quite
literally, a promise: a promise object is a guarantee that when the async operation
is completed, the promise will execute any function that was passed to it via
the promise’s then method.
In this example, the getLatest method receives the promise returned by the
_get method, then attaches a callback to it using the then method of the
promise. The then method ultimately returns another promise, which is resolved
with the return value of the _getLatest method. This means that another piece
of code with access to an instance of the Twitter datasource can do this:
1 2 3 | |
The code inside the function will run once the tweets have been fetched.
Connecting the Components
We have our page template, we have our components, and we have our data – now it’s time to glue it all together. Mulberry uses “capabilities” to broker communication between components and datasources. We’ll ask Mulberry to create a capability that we’ll use to encapsulate the functionality of our Twitter page:
1
| |
This creates a skeleton file at javascript/capabilities/Twitter.js, and
adds a dojo.require statement to our project’s javascript/base.js file.
Capabilities have a requirements object that indicates the components that it
expects to be present. It assigns those components names that will be used to
reference the components inside the capability. Capabilities also have a
connects array, which contains zero or more arrays that describe how the
capability will react when a certain method on a component is called. Remember
how we had an empty onSelect method in our UserList component? Our Twitter
capability connects to it, and describes how the rest of the page should react.
Finally, capabilities have an init method, where you can do initial setup of
components that might be page-dependent.
Here’s the Twitter capability in its entirety; you can see how all of the pieces we’ve talked about above come together:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | |
Adding a Custom Route for the User Tweets Page
Our last task before our Twitter app is complete is to create the secondary page, which shows a specific person’s recent tweets. We already know how to set up a page template and a new component, and how to fetch the Twitter data using the datasource we created, but how do we get the page to figure out which person’s Tweets to load?
For the sake of this discussion, let’s assume that we’ve created another page
template named user, and that the page template includes a custom component
named Tweets. (You can see this page template and the custom component in the
demo in the repo.)
In the UserInfo component, we indicated that we want to access the page by
visiting #/twitter/<username>. We need to define a custom route that will run
when a user navigates to this kind of page; the route will need to capture the
username from the URL’s hash, and then get the proper data to the components on
the page. We can add the following to our project’s javascript/base.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
This code tells the Mulberry router to be on the lookout for a URL hash that looks
like /twitter/:username. When the router sees this hash, it should run the
provided function. The provided function receives a params object, which
contains any parameters that were included in the hash. So, in this case, our
params object would have a username property, containing whichever username
was in the URL hash.
Inside the function, we create an instance of our Twitter datasource, and then
we ask the Mulberry PageFactory to create a page for us by passing it an
object. This object will be available to all components on the page; it also
must have a pageController property, which the PageFactory will use to
determine which page template to use in creating the page.
The object that we pass to the PageFactory’s createPage method also has a
tweets property, and here we see the power of promises again. We use the
tweets property to pass the promise that’s returned by the Twitter
datasource; by doing this, we allow the Tweets component to receive data
without interacting directly with the datasource. The Tweets component simply
attaches a callback to the promise using the promise’s then method. When the
promise resolves, it provides an array of tweets to any callbacks that were
attached; the Tweets component uses that array to populate itself.
Conclusion
Mulberry’s built-in components and page templates are focused on facilitating the rapid creation of static content apps, but the underlying patterns, tools, and architecture provide powerful tools for building all kinds of apps. If you build something interesting, we hope you’ll let us know!