Docs

Everything You Need to Know

Tutorials

Views

Understanding ChocolateChip-UI

A Room with a View

When building applications, separation of concerns is absolutely necessary. ChocolateChip-UI is built on the paradigm of mediators, views and models. Mediators know about views, models, local data stores and remove servers. Views don't know anything except how to render the data they are given and how to execute their registered events for user interaction. Models don't know anything about anything, but when you modify them they will let your app know. Any mediators paying attention to the models will be able to deal with the model's changes by rendering a view, putting the changes in a local data store, or sending it off to a server.

Views are the part of ChocolateChip-UI that you will use most often. Maybe it's the only part you might use in your app. Views are flexible. They can be bound to a model, or consume raw JavaScript data. They can be set to persist themselves in a local data store through ChocolateChip-UI's Box. Views can grab templates from the DOM, or they can hold templates defined on them. You can switch out what template a view is using at any time. You can do that with their models as well. Views can hold events that controller user interaction. You might use views with ChocolateChip-UI mediators or with ChocolateChip-UI's router, or you might just write your own code to handle everything.

Views remove the necessity of jQuery-style DOM manipulations. When you have DOM nodes that need to have their text or attributes changed dynamically, look at how to create a view to handle it. Once you define a view, you can render it with data and let the view take care of updating the DOM. No more mess of apppend(), html(), text(), etc.

First Steps

Main main reason you are going to use a view is to render a template. Granted, it is possible to make a view that does not use any template. You would do this where you are using a view purely for wiring up events in the DOM.

Templates

There's a lot of negativity these days about templates. Some developers are of the opinion that templates are evil. This is usually because they want to define the entire app in JavaScript. The fact is, having templates makes it easy to reason about the dynamic parts of an app. Having templates defined in place lets you scan your apps document structure to see how it all fits together - like a storyboard. Or defining your template directly on your view or component makes it easy for you to reason about its behavior.

Create a View

When you create a view, you want to associate it with an element. This element will serve as the container of DOM elements rendered by the view's template, or hosting registered events. To create a view, just assign it to a variable, which will hold the view's instance. There are many properties we can set when we create a view, but at bare minimum we can just provide it with an element to anchor to. When you indicating the element for the view, you can use standard CSS selector markup, or a DOM node or a ChocolateChipJS DOM object. Usually you'll just use the selector approach:

var appTitleView = $.View({
  element: '#appTitle'
});

The above code creates a view instance named "MyView". We can use this instance to render templates, bind to models, etc.

Delimiters

Templates are defined using standard HTML markup and delimiters. ChocolateChip-UI has two kinds of delimiters: interpolators and executors. Interpolator delimiters allow you to ouput a data value. Executor delimiters lets you write JavaScript that will be run when the template is rendered. You can use any JavaScript you could possibly need in a template using executor delimiters.

ChocolateChip-UI's interpolator delimiters follow the HTML5 template string convention:

<h1 id="appTitle" >{= data }</h1>

As you noticed, we used the term data to indicate that we wanted a value ouput. The space around data is optional. By default, ChocolateChip-UI uses data as the term for any data being rendering by a template. You can change this in the view setup to use a term that makes more sense for what the template is rendering.

With a template defined, an a view instance defined, when the document loads, ChocolateChip-UI looks at the view instance, sees the element selector, looks for it in the DOM, extracts its template and stores it on the view instance. This allows us to render the template repeatedly at any time with the same template. In this case we can render this template with a simple string:

appTitleView.render('My Great App');

This would produce:

<h1 id="appTitle" >My Great App</h1>

Using this technique we can render a view with a string or a number. However, we usually have more complex data to use with views. Continue reading to learn how to handle that.

Object Properties

The previous example was quite simple. In most cases you are not going to be rendering a view with such simple data. Instead you're going to be dealing with objects. You can expose and object's properties to the view using dot notation. Let's create a person object:

var person = {
  firstName: 'John',
  lastName: 'Doe',
  age: 32,
  job: 'developer'
};

We can use the following template structure to render this object in our view:

<ul id='person'>
  <li>
    <div>
      <h3>{= data.firstName } {= data.lastName }</h3>
      <h4>{= data.age }</h4>
      </p>{= data.job }</p>
    </div>
  </li>
</ul>

To consume this template, we'll create the following view:

var personView = $.View({
  element: '#person'
});
// Render the view with the person object:
PersonView.render(person);

The above will give us this:

<ul id='person'>
  <li>
    <div>
      <h3>John Doe</h3>
      <h4>32</h4>
      </p>developer</p>
    </div>
  </li>
</ul>

See the Pen PZvWQB by Robert Biggs (@rbiggs) on CodePen.

Custom Variable Names

As I mentioned earlier, you can provide custom variable names so that the templates make sense for the data they are consuming. Instead of data, use the term you want in the template. Then when you define the view, provide the name you want to use on the view's variable property:

<!-- Use the variable "person" -->
<ul id='person'>
  <li>
    <div>
      <h3>{= person.firstName } {= person.lastName }</h3>
      <h4>{= person.age }</h4>
      </p>{= person.job }</p>
    </div>
  </li>
</ul>

// Define "person" as custom variable:
var personView = $.View({
  element: '#person',
  variable: 'person'
});

// Render with the person object:
personView.render(person);

See the Pen xZNgjR by Robert Biggs (@rbiggs) on CodePen.

We could do that with our title view as well:

<!-- Use the variable "title" -->
<h1 id="appTitle" >{= title }</h1>

// Define "title" as custom variable:
var AppTitleView = $.View({
  element: '#appTitle',
  variable: 'title'
});

// Render the title:
AppTitleView.render('My Great App');

Arrays of Objects

So far we saw how to render simple strings and number, as well as objects with properties. However, many times we need to render an array of objects to create a list. Although other frameworks have special mechanisms to handle this, ChocolateChip-UI does not. That's because ChocolateChip-UI doesn't care whether you data is an object or an array. As long as the properties on the simple object and the array of objects are the same, ChocolateChip-UI will render them as a single item or a list. This means that you can turn a view rendering a single object into a list by rendering the same view with an array of similar objects. Or rendering a list view with a single object. The only requirement for this to work is that the object properties and the template variables match.

So, in the case of our person object, we can change our object to an array like this:

var persons = [
  {
    firstName: 'John',
    lastName: 'Doe',
    age: 32,
    job: 'developer'
  },
  {
    firstName: 'Sam',
    lastName: 'Smith',
    age: 28,
    job: 'mechanic'
  },
  // etc.
];

See the Pen obRBOo by Robert Biggs (@rbiggs) on CodePen.

Executor Delimiters

So far we've looked at how to use data interpolation in view templates. We can also write executable JavaScript in our templates. This allows you to render data conditionally, or whatever pre-rendering task you might need to perform.

The executor delimiters are a pair of opening and closing curly braces, like Mustache templates:

// JavaScript in template:
<div id='executable'>
  {{ alert("I just ran!"); }}
  <p>{= data }</p>
</div>

var ExecutableView = $.View({
  element: '#executable'
});
ExecutableView.render('Executable template')

When the above view is loaded in the browser, you'll get an alert with "I just ran!" This is a trivial example. Let's see how we might do something more practical. The number one reason you might need is for conditional rendering. You only want to render based on a value test. Here's how you can do that:

// JavaScript in template:
<li id='personsList'>
  {{ if (data.name !== 'Joe') { }}
    {= data.name }
  {{ } }}
</li>

To use a condition block, notice how it spans several lines. I had to enclose the opening if state with double curly braces, and terminate it with double curly braces. Yeah, I know, it isn't pretty. But it works. Using this technique you could also implement a loop or any other type of JavaScript block. Open up views2.html in the examples folder of the source code in a text editor to see how we did JavaScrpit loops in a template.

The fact is, you should try to do any kind of filtering or looping before you hand off data to a view for rendering. Executable delimiters are there for when there is no other options for pre-processing your data for a view.

Data Binding

To learn how to bind a model to a view, read the tutorial about models. You need to know how models work before using them with views.

ES6 and Template Literals

If you are writing in ES6 or transpiling from ES6 to ES5, you can take advantage of ES6 template literals to make you view templates more managable. Although defining templates directly in the View definition is great for organization, having to escape new lines with backslashes is a pain. If you're using ES6 as a target or just using it to transpile to ES5 with Babel, Traceur or TypeScript, then template literals are you best friend. No need for backslashes. Just you back ticks to enclose the template definition:

// Use ES6 template literal:
var personView = $.View({
  element: '#person',
  variable: 'person',
  events: [
    {
      element: 'h3',
      event: 'tap',
      callback: function() {
        console.log($(this).text());
      }
    }
  ],
  template: 
  `<li id='person'>
    <div>
      <h3>{= person.firstName } {= person.lastName }</h3>
      <h4>{= person.age }</h4>
      </p>{= person.job }</p>
    </div>
  </li>`
});

As you can see in the above example, including everything into the view definition makes it easy to reason about what the view is doing. It also gives you one place to look for everything related to the view, templates, events, etc.