We were all surprised to find that one of our Mulberry friends from Brazil,
Nuba Princigalli, had presented at
RioJS about Mulberry and Dojo (Mulberry uses Dojo as a foundation)!
Nuba is working on getting his slides translated into English and once he does,
we’ll link to them from here. Mulberry’s march across the world continues!
Dan Imal gave a good overview of the 0.3 release of Mulberry the other
day, but I wanted to follow up by taking a deeper dive into some of the new
features that landed that make it easier to use Mulberry to build all kinds of
apps. I’ll be talking about all of this at some upcoming events, but if you can’t make it
to any of them, hopefully this post will give you good insight into how these
improvements can help you build apps faster using Mulberry.
Probably the easiest way to understand these features is to look at a simple
Todos app. Our app will have a home page that lets you add todo items and view
unfinished ones, and a page that shows completed items; those items, and their
state, will persist on the device until the user deletes them. You can follow
along with the completed app on
GitHub.
Once we’ve scaffolded our app by running mulberry scaffold todos, we know
that we’ll need a few things:
A page definition for the Todos page
A page definition for the Completed page
A store for the todo items
Components for entering todos, listing todos, and working with todos en masse
We can add all of those things via the command line:
123
mulberry create page_def todos completed
mulberry create store todos
mulberry create component TodoForm TodoList TodoTools
We’ll also want to specify routes for our two pages. As of 0.3, we get a new
API method, mulberry.page, that lets us define a data-driven route and skip
the need to create a markdown file for the pgae. Here’s what our routes.js
file will look like:
Each of these calls to mulberry.page passes an object that has details about
how to display the page associated with the route, and in each case that object
has a pageDef property. Page definitions spell out how different components
will be laid out for a given page, and what capabilities that page will have.
Here’s the page definition for the todos page, located at page_defs/todos.yml:
The 0.3 release also introduced the concept of “stores,” which help us persist
and query data. The default behavior of a store is to persist the data on
device using local storage, though you can override or extend that behavior if
you’d like. Here’s our todos store, located at javascript/stores/todos.js:
This creates a store at client.stores.todos; that store will automatically
get methods like add, query, remove, and put, plus the additional
finish and unfinish methods that we’ve specified in the prototype we
passed. Those last two methods are used to pass messages to individual models.
We also specify a model property, which tells the store to expect that we’ve
defined a Todo model. We do so in javascript/models/Todo.js, and it looks
like this:
Now, we’re ready to wire up our app’s todos page. When we generated the page
definition file for the todos page, Mulberry automatically assigned it a
capability called PageTodos; this capability will be responsible for getting
the page set up, and then brokering communication between the components as the
user works with their todo items.
Here’s what that capability, located at javascript/capabilities/PageTodos.js,
looks like, with some inline comments to make it easier to follow:
dojo.provide('client.capabilities.PageTodos');mulberry.capability('PageTodos',{/* * The capability expects the following components to be present in order for * the capability to work. */requirements:{todoList:'custom.TodoList',todoForm:'custom.TodoForm',todoTools:'custom.TodoTools'},/* * These "listeners" will be set up as part of setting up the page. So, for * example, when the TodoForm component instance announces that a user has * added a todo (by calling its `onAdd` method), then the capability's `_add` * method will be run. */connects:[['todoForm','onAdd','_add'],['todoList','onComplete','_complete'],['todoList','onDelete','_delete'],['todoTools','onCompleteAll','_completeAll']],/* * When the page is set up, we'll grab a reference to the todos store, and * then update the list of todos. */init:function(){this.todos=client.stores.todos;this._updateList();},_delete:function(id){this.todos.remove(id);this._updateList();},_add:function(item){this.todos.add(item);this._updateList();},_complete:function(id){this.todos.finish(id);this._updateList();},_updateList:function(){varitems=this.todos.query(function(item){return!item.complete;});this.todoList.set('todos',items);},_completeAll:function(){this.todos.query(function(item){return!item.complete;}).forEach(dojo.hitch(this,function(t){t.finish();this.todos.put(t);}));this._updateList();}});
All that’s left to do now is to actually make the components do what they need
to do. Components are intentionally “dumb” – their job is to receive data from
an outside source, render that data using a template, and announce user
interaction by calling (often empty) methods. Here, for example, is the
TodoForm component, located at javascript/components/TodoForm.js:
It displays the form (using the template located at
javascript/components/TodoForm/TodoForm.haml), listens for the user to submit
the form, checks to make sure there was some input, and then calls its onAdd
method, which the PageTodos capability has registered its interest in. The
other components are similarly simple.
The Mulberry framework was born out of our experience with making content-rich apps,
but we’re hard at work making it easy to use Mulberry to build all kinds of
apps – pretty much anything that might manifest itself as a single-page
JavaScript application. We hope you’ll download the 0.3 version, take the Todos demo for a spin, and let us know how we’re doing.
It’s a new year, and with that comes a new release of Mulberry! As with the previous release, our goal is to make it easier to get started with Mulberry, and once you’re up and running, we want to make it easier to customize your app. Some of the highlights:
Installer scripts for OSX and Windows (Ubuntu is on the way):
Getting started with Mulberry used to require downloading and installing a lot of other dependencies. While we haven’t been able to completely automate this, we have made it a whole lot easier. Find the script for your OS in /install/<os>/, follow the instructions in the README, then run the installer script, and you’ll be up and running.
Customizable default theme:
The “default” theme used to be difficult to customize. If you decided to use it you were basically stuck with a lot of gray. Now all the colors and fonts can now be changed from the themes/default/_settings.scss file, so you don’t have to dig through alot of code or create a completely new theme just to change some colors. The icons are now customizable as well. Simply replace any of the images in themes/default/icons with whatever you want.
Templates are now “page_defs”:
In 0.2, we had “templates” which were yaml files where you’d define the layout for a page in columns and rows. But these templates also had a lot of non-presentational information, which tended to be confusing. So we changed things so that page layout is handled in CSS (as it should be), and page_defs got a little simpler. We added a few Sass mixins to help with page layout. Read more about page_defs.
New and Improved Code Generators:
It was already possible to create new routes, but now you can run mulberry create route '/foo/:bar' and it will generate the necessary code and add it to routes.js, which you can then customize. Running mulberry create page_def will now generate a boilerplate scss file and add import directive.
In addition to all the new stuff, we’ve got a bug of bugfixes, improved development server stability, and more complete test coverage. For the full story, read the full release notes.
As always, follow us at @touradev to keep up to date on the latest developments, or get help in the Google Group.
Mulberry 0.2 is out today, with several new features to make it
even easier to customize your apps:
In Mulberry 0.1, you had to be very verbose when overriding the default
styles. As of 0.2, when an app is scaffolded, the default theme is copied
into the app, where it can be modified directly. To pull any upstream changes
to the built-in themes, we added mulberry update_themes to the CLI.
We beefed up mulberry create component; it now automatically
creates a stub .scss file for the component, and adds an @import statement to
your theme’s base.scss file.
Now when you add pages to your sitemap, you can just run mulberry scaffold
inside your Mulberry project to create them all at once, rather than having
to remember which ones to create yourself. Read about all the command line options.
It’s often necessary to repopulate a component or a piece of a component when
it gets new data or its state changes. Mulberry 0.2 introduces the populate
and populateElement methods on components to make this easier – just pass
it a template and the data for the template, and it handles the rest. Read
more about custom components.
I’m in London after attending the Full Frontal JavaScript Conference,
put on by the fantastic Remy and Julieanne
Sharp in lovely, foggy, chilly Brighton. It was a great chance to meet a ton of
developers from whom I’m usually separated by an ocean, and of course I spent
a lot of time talking to those developers about Mulberry.
One of the things that seemed to especially get their attention was how
Mulberry lets you do day-to-day development in a browser, avoiding
time-consuming compilations most of the time. Setting up browser-based
development on a PhoneGap project can be tricky, and development on device or
in a simulator harkens back to the bad old days of IE6.
If you want to develop content-rich native apps that include a non-trivial
amount of JavaScript, you’re going to want the comforts of a modern browser.
We’ve solved this in Mulberry in a few different ways.
Wrappers for PhoneGap APIs
PhoneGap and PhoneGap plugin APIs don’t work when you’re running your code in
a desktop browser – indeed, if you try to call them, you’ll usually get an
error that blocks execution of any code that comes next, which can make
debugging a serious pain.
Mulberry solves this by providing an interface for creating wrappers for
PhoneGap APIs and plugins; it also has a few built-in wrappers. For example,
here’s a wrapper for the notification API:
We set up the built-in wrappers using toura.app.PhoneGap.registerAPI(apiName,
module), which you can use to create your own wrappers, too. Each wrapper is
simply a function that gets two arguments: a boolean indicating whether
PhoneGap is present, and a device object with information about the device
that’s being targeted. Wrappers return an object with methods that become
available at toura.app.Phonegap[apiName].
In the example of the notification API above, once the API is registered we can
call the function toura.app.PhoneGap.notification.alert from anywhere in our
code, and it will “just work” whether we’re developing in a browser or on
device. Mulberry’s wrapper around the ChildBrowser plugin
is a more complex example – it simulates ChildBrowser’s behavior in a desktop
browser, and handles differences in the Android and iOS implementations behind
the scenes, so opening a new URL in your app requires the exact same code
regardless of the environment.
Rapid Switching Between Device-Specific Code Paths
Even though most of your JavaScript can be the same from one device to the
next, sometimes you’ll need to branch your code to deal with a quirk of
a particular OS. Working on the different code paths can be a challenge when
you’re developing in the browser, because you need a way to tell your code
which environment you want to be simulating.
Mulberry comes with a built-in server, and the server is set up so that
switching between configurations is as simple as changing the URL. You can work
on the iPad version of your app at http://localhost:3001/ios/tablet/, and then
switch to working on the Android phone version of your app at
http://localhost:3001/android/phone/. All of your code will automatically get
access to information about the “device” you’re working on by calling
toura.app.Config.get('device'), and of course any changes you make to your
code or content will be visible as soon as you reload the page.
Remote Debugging
While browser-based development is great, nothing compares to testing your app
on a device, but when you do, you’re suddenly in a world where console.log
and alert feel like your only friends.
Every development build you make with Mulberry has a button in the lower
right-hand corner of the screen marked “weinre”. If you haven’t seen
weinre before, prepare for your life to
be changed, because it lets you debug an app that’s running on your device from
the comfort of your computer, using the Webkit developer tools you know and
love.
As of this week, Toura is hosting its own version of weinre for Mulberry
developers, and it’s connected directly to the weinre button in Mulberry apps.
Read more about it here.
More to Come
The Toura Dev team doesn’t just work on Mulberry – we’ve also shipped more
than 100 apps that use the underlying Mulberry technology, which means we’re
always trying to make our own experience of the toolkit a pleasant one.
Pretty much every developer-friendly feature we’ve added so far was added
because we’re developers, too, and we hate when our tools get in our way. That
said, we know tools can always be better – if there’s something we could do to
make your life easier when developing mobile apps, we hope you’ll let us know.
At Toura, we perform the vast majority of our day-to-day development in our
desktop WebKit browsers - Chrome and Safari. Although the desktop browsers
are a very close approximation to the mobile browsers, there’s no substitute
for actually performing final QA and fit & finish checks on the mobile
browsers themselves followed by a final sanity check with a compiled application.
What happens, though, if you find a display bug on device browser that you
don’t see on the desktop? What about a JavaScript error in the WebView? How would
you go about debugging that? Enter: Weinre.
Weinre, written by Patrick Mueller, provides
a client/server environment to present you with a familiar “web inspector”-like
experience on apps running on mobile device browsers and webviews.
Installing and configuring Weinre is simple enough and you can accomplish this
by following the instructions at the Weinre website. OR, instead of going through
all of that and keeping Weinre running (it requires a local process) and poking
holes in your firewall, you can use our free hosted Weinre service!
To use the hosted Weinre service:
Boot any app in development mode via:
a. mulberry serve and point your device browser to your local Mulberry server OR
b. mulberry test and run the app in your device simulator/emulator
In the lower right hand corner, tap the “Weinre” button
Go to Toura Weinre and enter the code displayed at the top of the page
Click the provided link and debug away; that’s it!
Now you can develop on your local browser and perform final checks on your
iOS and Android devices. If you encounter a bug, you can quickly and easily
hook a remote web inspector to your page. Access the Javascript console, edit
HTML, and more - right on the device!
One of the questions that’s come up several times in talking to other
developers about Mulberry is this: How is Mulberry any different from all of
the other mobile app development frameworks that are out there?
At Toura, we’ve been developing and using the technology behind Mulberry for
more than a year, creating apps for clients that range from museums to major
media companies. Because our core business is centered on the rapid creation of
content-rich apps, we focused our development efforts on reuse from the start.
That focus led us to an opinionated
framework, one that makes some assumptions about the kinds of applications
developers will build with it and about how those applications will be built.
Being opinionated results in a framework that dramatically reduces the effort
that’s required to develop an app, and subsequent similar apps are easier
still, because it’s vastly more likely that functionality developed for one app
can be reused down the road.
While many JavaScript developers have come to think of DOM nodes as their
building blocks, in Mulberry, the fundamental building block of an app is
a component. The functionality of an app is described by how components,
arranged on a page, display the app’s content and interact with each other.
Mulberry comes with lots of components
and page layouts that use those components
— making it easy to create content-rich applications that incorporate images,
videos, map locations, and more — and it also provides simple APIs for creating
custom components and adding them to the underlying framework. Custom and
built-in components can be arranged into pages, and those pages can be enabled
with pre-defined or custom interactions between components. Components neatly
encapsulate their DOM structure, behavior, and state, and expose predictable
interfaces that make them easy to integrate into applications. Mulberry apps
aren’t about wiring up buttons and lists and grids — they’re about defining the
interactions between far richer components, and then customizing the
presentation layer to create a unique experience.
For Toura, all of this means that creating a particular kind of app has become
a reliably repeatable process; more importantly, adding custom functionality to
individual apps is also a reliably repeatable process whose results can be
readily reused across multiple applications and platforms. Units of
functionality — a Twitter feed component, say, or a component that displays
a location’s name, address, phone number, web site, and map — take the place of
DOM nodes as the building blocks for applications, and they can be mixed and
matched and connected in any combination that makes sense.
We’ve placed a lot of value on being able to define new functionality, creating
APIs like mulberry.component and mulberry.capability that let us set up new
components and interactions without impacting existing functionality. We’ve
also been diligent about keeping our presentation layer very loosely coupled
with the behavior and data layers. All of this means that we can dramatically
alter the “look and feel” of an app while leaving the underlying functionality
largely intact, and create new experiences with shockingly little code.
Mulberry embraces a view of how we should be building apps that moves away from
some of the DOM-centric habits that JavaScript developers have learned over the
last few years — habits that have presented challenges as those same developers
transition from building websites to building full-fledged apps. By rethinking
the nature of apps to emphasize patterns that facilitate smart reuse, Mulberry
makes app development and customization a simple, repeatable, and scalable
endeavor.
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
mulberry scaffold twitter
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
mulberry create_data users
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:
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:
demos/twitter/pages/home.md
123456
---title:Hometemplate:twitterdata:-users.yml---
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:
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
this.baseObj.getData('users').users
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:
dojo.provide('client.components.UserList');mulberry.component('UserList',{componentTemplate:dojo.cache('client.components','UserList/UserList.haml'),prep:function(){this.users=this.baseObj.getData('users').users;},init:function(){this.connect(this.domNode,'click','_handleClick');},_handleClick:function(e){vartarget=e.target;while(target!==this.domNode&&target.parentNode!==this.domNode){target=target.parentNode;}if(target.nodeName.toLowerCase()!=='li'){return;}if(dojo.hasClass(target,'selected')){return;}this.query('.selected').removeClass('selected');dojo.addClass(target,'selected');this.onSelect(dojo.attr(target,'data-twitter-username'));},onSelect:function(username){// stub for connection}});
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
This means that any code that has access to an instance of the UserInfo
component can do the following:
1
myUserInfoInstance.set('user',userObject);
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:
.component.user-info%h1{dojoAttachPoint:'nameNode'}%p.bio{dojoAttachPoint:'bioNode'}%p%a{dojoAttachPoint:'twitterLinkNode'} View all tweets
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
this.myAttachPointName
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
mulberry create_template twitter
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:
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-list so that we
can target it with CSS; the second sub-region will contain the custom
UserInfo component, and will get the class name user-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
mulberry serve
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
mulberry create_datasource Twitter
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:
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:
123
myTwitterInstance.get('rmurphey').then(function(tweets){console.log('these are the tweets',tweets);});
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
mulberry create_capability Twitter
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:
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:
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!