tvOS Tutorial: Using TVML Templates

In this tvOS tutorial, you’ll learn how to use TVML templates and templating engines to make great-looking user interfaces. By Chris Belanger.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

The Product Template

The first document you’ll create uses , which is designed to display all information relating to a specific product — in this case, a video.

Open video.tvml and add the following code between the tags:

<banner>
  <infoList>
    <info>
      <header>
        <title>Presenter</title>
      </header>
      <text>Ray Wenderlich</text>
    </info>
    <info>
      <header>
        <title>Tags</title>
      </header>
      <text>development</text>
      <text>teams</text>
      <text>business</text>
    </info>
  </infoList>
</banner>

This code snippet introduces a lot of new element types. Taking them one-by-one:

  • <banner>: Displays content across the top of a page.
  • <infoList>: Displays a list of elements, arranged in a vertical list.
  • <info>: Acts as a container forthis s the content to appear as an item in an or an .
  • <header>: Serves as a description of the content of the section in which it resides.
  • <title>: Contains the text of a short title.
  • <text>: Displays text.

Build and run the app to see what your new TVML looks like:

This page represents a video, but it currently lacks a title. Time to change that.

Add the following inside the <banner> tags, just after the closing tag:

<stack>
  <title>Teamwork</title>
  <row>
    <text>17m 54s</text>
    <text>Inspiration</text>
    <text>2015</text>
    <badge src="resource://nr" />is 
    <badge src="resource://cc" />
    <badge src="resource://hd" />
  </row>
</stack>

This section introduces more TVML elements:

  • <stack>: Stacks lay out their content vertically down the screen in a manner similar to . There’s a wider range of tags that can be in a Stack.
  • <row&gt: A row is like a stack, but with a horizontal orientation instead of vertical.
  • <badge&gt: Badges display a small inline image. The URL is provided by the src attribute.

Notice that the URL of the two badge images begin with resource://. This is a special URL scheme that points to images that exist within tvOS itself. These images include common action icons, such as “play”, rating images for different countries and video information such as HD.

Note: For a full list of the resource images available within tvOS, check out Apple’s documentation at apple.co/1T930o9.

Build and run again to see how the page is shaping up:

It’s starting to look good, but there’s still a long way to go. Before continuing with the template coding, you first need to consider the separation of the data and the view.

Data Injection

As your video document currently stands, all the data is hard-coded. To show information about a different video, you’d have to create a whole new page. If you wanted to reformat the video page once you’ve created all the pages, you’d have to go back through and edit every single one of them.

A much better approach is to use a templating engine, where you build the video page as a template and specify where the data should be injected. At runtime, the template engine takes the page template along with the data and generates the TVML page for tvOS to display.

Note: The word “template” is now being used for two different purposes: TVML templates are the layouts provided by tvOS that render your documents on the screen, whereas a templating engine uses template documents combined with data to generate complete TVML documents. Don’t worry too much about the distinction; it’ll be much more clear once you’ve used them both.

Mustache.js is a popular simple templating engine for JavaScript. You might recognize the templating syntax which is based around curly-braces:

{{property-name}}

The Mustache.js library is already part of wenderTV, but you need to build the mechanisms to use it. This presents you with several tasks to accomplish:

  • The data is stored as JSON files in the app bundle. The JavaScript app needs the ability to request them.
  • When a document is loaded, it now requires data, and this should be combined with the document string using Mustache.js.
  • Images that are present in the app bundle need their complete URL substituted.
  • The video document should be updated to turn it into a templated document.

You’ll address each of these in order.

However, prototyping is only available at the <section> level, while you’re using it in this tutorial for much more, such as in the <header> section of your TVML. You’ll continue to use Mustache.js as a templating engine for this section of the book, but if you end up having to handle large content sets in your tvOS apps, read up on prototyping and data binding at apple.co/2utDXXm.

Note: As of tvOS 11, TVML supports the concept of prototypes in TVML that leverage data binding to link data elements to the TVML. This reduces code redundancy and improves performance when paginating large content sets, such as a huge list of YouTube videos in a grid view format.

Reading JSON From the App Bundle

Open ResourceLoader.js and add the following method to ResourceLoaderJS:

getJSON(name) {
  var jsonString = this.nativeResourceLoader
    .loadBundleResource(name);
  var json = JSON.parse(jsonString);
  return json;
}

This function uses the native resource loader to pull the JSON file from the app bundle before parsing it into a JavaScript object.

Note: It would be relatively simple to replace this functionality with a method that calls a remote server for data instead of finding static data inside the app bundle. The rest of this templating approach would continue to work as it stands.

Injecting Data Into the Document String

Now that you can obtain the data for a given page, you need to combine it with the document template itself. Update getDocument() in ResourceLoaderJS to match the following:

getDocument(name, data) {
  data = data || {};
  var docString = this.nativeResourceLoader
    .loadBundleResource(name);
  var rendered = Mustache.render(docString, data);
  return this.domParser.parseFromString(rendered,
    "application/xml");
}

Here you’ve added an additional data argument to the method and used render on Mustache to convert the template and data to a completed document. As before, you use a DOMParser to convert the document string to a DOM object.

Resolving Image URLs

For simplicity’s sake, your sample data stores images as the names of files in the app bundle, which need to be converted to URLs before you can display them. The utility functions to do this are already in the resource loader, so you just need to call them. You’ll do this at the same time as you update the initial document loading to use the templating engine.

Open main.js and update loadInitialDocument() to match the following:

function loadInitialDocument(resourceLoader) {
  var data = resourceLoader.getJSON("teamwork.json");
  data["images"] = resourceLoader
    .convertNamesToURLs(data["images"]);
  data = resourceLoader
    .recursivelyConvertFieldsToURLs(data, "image");
  data["sharedImages"] = _sharedImageResources(resourceLoader);
  return resourceLoader.getDocument("video.tvml", data);
}

First, you load the data using the new getJSON() method. Then you use the utility functions to perform the image name-to-URL conversion. These convert three different image name sources:

  • Each value in the images object on the data array.
  • Every value associated with a key of image anywhere within the JSON data structure.
  • A set of shared images that are useful for all documents in wenderTV.

That takes care of the plumbing underneath; you’re ready to use this powerful new functionality.

Chris Belanger

Contributors

Chris Belanger

Author

Over 300 content creators. Join our team.