Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic XML Template in TVML/TVJS

Tags:

tvos

tvml

Does anyone know how to Dynamically generate a template in an apple tv app using TVJS/TVML? Basically I want to hit my API, get back an array of objects and then insert that data into my XML template.

I've been searching for info on how to accomplish it but have come up short. I've found many tutorials that use hard coded images, videos, etc but nothing dynamically generated.

Any help would be appreciated.

like image 394
Drastick Avatar asked Sep 27 '15 16:09

Drastick


2 Answers

Finally, I've figured this out. It wouldn't be difficult to generate a template on-the-fly, but instead I wanted to reuse the Presenter and the ResourceLoader, and to have the template as a *.xml.js file. Here is the solution I managed to arrive at.

For the initial view, I used a catalogTemplate, as demonstrated in Ray Wenderlich's tutorial. Instead of conference talks, however, I was displaying categories of men's and women's merchandise. Once a category was selected, I wanted to display a stackTemplate with a number of options for that category. The problem was how to pass any information, the title of the category in the simplest case, to the second template.

In the first template, I had the lockups configured like so:

<lockup categoryTitle="Women: Dresses" categoryDir="w-dresses">
  <img src="${this.BASEURL}images/dresses.jpg" width="230" height="288" />
  <title>Dresses</title>
</lockup>

In application.js, I had a listener attached, in the same way how tutorials show:

doc.addEventListener("select", Presenter.load.bind(Presenter));

Here is the second template (Category.xml.js):

var Template = function(categoryTitle) { 
    return `<?xml version="1.0" encoding="UTF-8" ?>
    <document>
      <stackTemplate>
        <banner>
          <title>${categoryTitle}</title>
        </banner>
      </stackTemplate>
    </document>`
}

This is a JavaScript, so in your case you can pass into the function, say, an array of values and then construct the template accordingly. The tricky part was to pass a value.

First, I made a couple of changes to the ResourceLoader (this can be done better, of course, it's just a proof of concept). I simply added categoryTitle as an additional parameter to the top-level function and when calling the Template:

ResourceLoader.prototype.loadResource = function(resource, callback, categoryTitle) {
  var self = this;
  evaluateScripts([resource], function(success) {
    if(success) {
      var resource = Template.call(self, categoryTitle);
      callback.call(self, resource);
    } else {
      var title = "Resource Loader Error",
      description = `Error loading resource '${resource}'. \n\n Try again later.`,
      alert = createAlert(title, description);
      navigationDocument.presentModal(alert);
    }
  }); 
}

Finally, in the Presenter, in the load, I am passing categoryTitle to the resourceLoader:

load: function(event) {
  var self = this,
  ele = event.target,
  categoryTitle = ele.getAttribute("categoryTitle");
  if (categoryTitle) {
    resourceLoader.loadResource(`${baseURL}templates/Category.xml.js`, function(resource) {
        var doc = self.makeDocument(resource);
        self.pushDocument(doc);
    }, categoryTitle);
  }
},

This works for me.

One final note: for some categories, I had titles with an ampersand, like 'Tops & T-shirts'. Naturally, I replaced the ampersand with an XML entity: 'Tops &amp; T-shirts'. This, however, didn't work, probably because this string was decoded twice: the first time the entity was turned into an ampersand, and on the second pass the single ampersand was flagged as an error. What worked for me was this: 'Tops &amp;amp; T-shirts'!

like image 91
Sundraw Avatar answered Sep 22 '22 16:09

Sundraw


It is simple if you are using atvjs.

// create your dynamic page
ATV.Page.create({
    name: 'homepage',
    url: 'path/to/your/json/data',
    template: function(data) {
        // your dynamic template
        return `<document>
                    <alertTemplate>
                        <title>${data.title}</title>
                        <description>${data.description}</description>
                    </alertTemplate>
                </document>`;
    }
});

// later in your app you can navigate to your page by calling
ATV.Navigation.navigate('homepage');

Disclaimer: I am the creator and maintainer of atvjs and as of writing this answer, it is the only JavaScript framework available for Apple TV development using TVML and TVJS. Hence I could provide references only from this framework. The answer should not be mistaken as a biased opinion.

like image 35
eMAD Avatar answered Sep 20 '22 16:09

eMAD