This 11-step tutorial will guide you through building a handful of cards in the playground. By the end of this tutorial you will know how to:
- create cards;
- load and use external resources in cards;
- compose cards and
- synchronize data between cards.
Each step in the tutorial has an associated git tag, for example tutorial-1. It is recommended that you checkout the corresponding tag as you read a section and view the state of the cards in the browser.
In this tutorial we'll be building a very tiny app that shows the user a SuperBowl ad and then asks them to rate the ad. We'll have three cards: one for playing a video through a third-party service (YouTube), one for showing a survey, and one for coordinating the two.
Start by checking out the repository, installing grunt, and starting the server.
git clone git://github.com/tildeio/conductor.js.git
cd conductor.js
# Install grunt-cli
npm install -g grunt-cli
# Install conductor.js dependencies
npm install
# Start the server
grunt server
Now navigate to http://localhost:8002/example/playground. At each step of the tutorial, it is recommended that you checkout that step's tag, refresh the browser and open the analytics tab at the bottom of the page so you can see the cross-card communication.
tutorial-1 starts us off with a simple hello world card.
In the containing environment we load the card via conductor.load
. The
playground does some more work for the sake of the UI: don't worry, we'll see
card loading when we get to nested cards.
In the card's js file we create the card with a call to Conductor.card
. This
connects the sandbox with the containing environment, and wires up services in
the containing environment to consumers in the card.
activate
is invoked once the sandbox is fully initialized and all resources
have been loaded.
It will usually be necessary for cards to have their own consumers, but some
functionality is so common that Conductor.js provides it via a set of standard
service-consumer pairs. One of these is the render service. The render
function we pass to Conductor.card
is invoked when the containing environment
asks the card to render itself.
tutorial-2 shows us
how to use Conductor.require
to load external resources like jQuery and
handlebars.
It's important to note that these resources are loaded asynchronously: they
won't be loaded right after the call to Conductor.require
, but they'll be
available once your card is activated.
That's why we wait to compile the Handlebars template until activate
is
invoked.
tutorial-3 finishes the basic functionality of the survey card. Now we can initially render a list of grade options and switch to a report mode once the user has picked a grade.
tutorial-4 adds events to the survey card: after all, our goal is to communicate between sandboxed cards!
The card communicates with its parent environment through its card-specific
consumers passed to Conductor.card
, as well as the standard consumers.
consumers
is a map of capability names matched to consumer classes.
Normally we'd need to extend Conductor.Oasis.Consumer
, as in the following:
Conductor.card({
consumers: {
myCapability: Conductor.Oasis.Consumer.extend({
requests: {
// …
},
events: {
// …
}
})
}
// …
});
However, we don't need to respond to events or requests from the containing environment: we just need to establish a channel to send messages across.
Notice that the code to send the surveyTaken
and gradeChanged
events is
guarded by card.consumers.survey
. That's because consumers are not
necessarily connected: only consumers with capabilities specified by the
containing environment are connected.
When writing cards, some capabilities might be optional and others required. If
you require capabilities, you should check for them in your card's activate
function.
You may have noticed that the analytics tab in the playground didn't show us our
events. This is precisely because the playground hadn't provided the survey
capability via a service.
tutorial-5 adds the
survey
capability. Now when we give a grade, we can see the events in the
analytics panel!
tutorial-6 is the first step in creating a simple YouTube card so that we can use the YouTube player API safely.
We already knew that Conductor.require
would load additional JavaScript
resources, and here we can see that Conductor.requireCSS
loads CSS resources.
Notice that the containing environment (the playground) loads data for our card
via conductor.loadData
. This data is passed to our card in activate
.
tutorial-7 actually loads the YouTube API. It makes use of promises via RSVP - a great way to handle asynchronous resources in JavaScript.
It's important to note that because flash cannot yet be run in a sandboxed
<iframe>
, you have to be logged in to YouTube and opted in to YouTube's html5
beta program for this part of the tutorial.
tutorial-8 adds a consumer to our YouTube card, but this time we do want to respond to events from our containing environment, so that it can remotely start the video.
It's important to note that the play
event handler in the video consumer uses
loadPlayer
to guard the call to playVideo
behind a promise. That's because
it's possible for consumers to receive events before a card is activated, and
certainly before external resources like the YouTube API have been loaded.
Try starting the video programmatically: in your browser console, run the following:
videoPort.send('play');
This will send the play
event to our video consumer which will in response
start the player. We can also see this event appear in the analytics tab.
Note that we put the video service's port in the global scope in the service initializer: this is obviously not a best practice, it's done here for convenience in the tutorial.
tutorial-9 puts our two cards together. Much of this step should be familiar, but now we introduce a card that has its own child cards.
This is common enough that there's a convenient shorthand: an array of
childCards
can be passed to Conductor.card
. This will create a nested
Conductor
and load each specified card. Before the cards are loaded,
loadDataForChildCards
is invoked and passed this card's data so you can
extract any data your child cards need.
tutorial-10 shows us how to use the data service to inform child cards that their data has changed so we can keep data in sync.
We've added a little UI widget that lets the user load either of two SuperBowl
ads, reusing the YouTube card. All we need to do is make another call to
conductor.loadData
- Conductor realizes the data has changed and informs the
child.
But the child card still needs to do something about it.
tutorial-11 is the
last step of our tutorial and completes the data synchronization picture.
Recall that when a card is initially created activate
is called and passed the
initial data. When the containing environment updates the data, the data
consumer invokes didUpdateData
so your card can respond and stay in sync.