Docs

Everything You Need to Know

Views

Use

Setup

When initializing a view, you can make it empty, or provide a number of options to give it various types of functionality. If you want a view, but aren't ready to have it associate with someting in the DOM quite yet, you can make an empty one like so:

var emptyView = $.View();
          

To be useful a view needs at least an element to associate it with. You basically anchor the view to that elemnet. This element may hold a template that the view will use, but it doesn't have too. You can always attach a template to a view later. Now, let's create a view with an associated DOM element:

var simpleView = $.View({
  element: '#special'
});

The above view presupposes the existence of an element called #special. This might have a template inside. There are several ways to do that. We can use a script tag, a template tag, or plain markup. When putting the template markup directly in the element, always give the element a class of cloak:

// Script tag:
<script type='text/template'>
  <ul class='list'>
    <li>{= data }</li>
  </ul>
</script>
// Template tag:
<template'>
  <ul class='list'>
    <li>{= data }</li>
  </ul>
</template>
// Template in element with "cloak":
<ul class='list cloak'>
  <li>{= data }</li>
</ul>

When we point a view to an element with a template, ChocolateChip-UI extracts the template and parses it. After that, we can render the template with our data:

//Markup:
// The template will include the span tag:
<div id='stuff'>
  <span class='important'>{= data }</span>
</div>
// Define a view:
var simpleView = $.View({
  element: '#stuff'
});
// Render the view with data:
simpleView.render(myStuff);

If myStuff were an array of objects, the view would render the template with each object. And if myStuff was a simple object, it would render just once. Views do not care whether data is iterable or not. It checks the data before rendering and decides. As long as the properties on an object or array of objects matches your template, it will render without issue.

Here's an example of a simple view with its template in the markup rendering an array of items:

See the Pen View Example 1 by Robert Biggs (@rbiggs) on CodePen.

Template in View

If you don't like having templates defined in the document, you can also define your template directly in the view's definition. If your template spans multiple lines, you will need to escape the new lines with backslashes. When you provide a template string in a view's setup, ChocolateChip-UI will not try to extract a template from the element. Notice that the template uses the workd data to represent what we want to render. In the following example, we are rendering a view with a simple string. The term data in the template is the placeholder for the data we wish to render. It will get replaced with the data when we render the view:

// Markup:
<div id='someTag'></div>
// Script:
var stuff = 'This is my stuff.'
var simpleView = $.View({
  element: '#someTag',
  template: '{= data }'
});
simpleView.render(stuff);

The above view would render "This is my stuff." inside the div. We could just as easily use a number as well.

Templates with Complex Data

So far we've only rendered simple data. If you have an object and want to render it, you'll need to change how you use the data variable. Suppose we have an object like this:

// Object:
var person = {
  name: 'Joe',
  job: 'Mechanic'
}

Because of the nature of this object, simply using data in the template won't work. We need to tell the template which object properties it should render. We do that using normal object dot notation:

// Define view with string template:
var simpleView = $.View({
  element: '#special',
  template: 
'<li>
  <h3>{= data.name }</h3>
  <h4>{= data.job }</h4>
</li>'
});
// Render view with the person object:
simpleView.render(person);

Using a Variable in a Template

When we are rendering our view with complex data, using the default data variable in our templates can make them hard to understand. ChocolateChip-UI lets you define a custom variable to use in your template. Since we are using a data object called "person", we can tell our view to use that as instead of the generic term "data". To do that, we give the view a variable property and assign it the term we want to use in the template. So, let's use the term "person":

// Define view with string template:
var simpleView = $.View({
  element: '#special',
  // Define a custom variable for the template:
  variable: 'person',
  // Use the "person" variable in the template:
  template: 
'<li>
  <h3>{= person.name }</h3>
  <h4>{= person.job }</h4>
</li>'
});
// Render view with the person object:
simpleView.render(person);

As you can see, this makes it easier to understand what the template is rendering.

Here's the same example from before, but with the template now defined in the view itself:

See the Pen View Example 2 by Robert Biggs (@rbiggs) on CodePen.

When you define your templates in your view, your app's markup is less clutter and the template and view object are together as a single unit. No need to look around for where the view's template is. This also makes it easier for you to reuse a view elsewhere.

To learn more about templates please visit the documentation for templates.

Defining a View's Data

Rather than passing the data to your view as an argument of the render function, you can tell the view what data it should use when rendering. Then whenever you call its render function, it will use that data. You do that by giving the view a data property to which you assign the data:

See the Pen View Example 3 by Robert Biggs (@rbiggs) on CodePen.

// Define view with string template:
var simpleView = $.View({
  element: '#special',
  variable: 'fruit',
  data: fruits,
  template: '<li>{= fruit }</li>'
});
// Render view:
simpleView.render();

Binding a View to a Model

As we saw above, you can use plain JavaScript objects with ChocolateChip-UI's views. However, in many cases you might prefer to create an model and use that with a view. Why? Models offer you functionality that plain JavaScript objects do not. However, the most important reason for binding your view to a model is that whenever the model changes, the view will render automatically. No need for you to call the view's render function. For more information about Models, read their documentation.

To use a model with a view, assign the model to the view's model property instead of the data property:

var boundView = $.View({
  element: '#peopleList',
  model: peopleModel
});

Make a View Interactive

You can make a view interactive by providing events. For this you use the events property, which takes an array of event objects. Event objects have three possible properties: the event you want to capture, a callback and an optional element to delegate the event to. Instead of an element you can use the string "self" to target the view's anchor element. You can use any of the events that ChocolateChip-UI supports from mouse events to touch gestures. For the callback, this will refer to the element the user is interacting with:

var interactiveView = $.View({
  element: '#items',
  model: ItemsModel,
  // Define an event delegated to the list items:
  events: [{
    element: 'li',
    event: 'tap',
    callback: function() {
      console.log($(this).text());
    }
  }]
});

You can add events at any time to an existing view using the view's addEvents method:

myView.addEvent({
  element: 'h3',
  event: 'doubletap',
  callback: function() {
    console.log($(this).text());
});

And here's an example of a view with events bound to its items. Click on an item and it will alert the content:

See the Pen View Example 4 by Robert Biggs (@rbiggs) on CodePen.

Changing a View's Template

As we mentioned previously, you can change the template that a view is using. If you wanted to render different data in the same view depending on what the user chooses, you can change the view's template. To do so, you assign the template using the setTemplate() method. After changing the template, you will need to render the template again, as illustrated below:

// Get a template:
var template2 = $('#template2').html();
// Assign new template to view:
peopleView.setTemplate(template2);
// Rerender view with new template:
peopleView.render(newData);

If you want to change a view's model and template, change the template first, then the model. That way, the new model will render the view with the new template. Please see templates for more details.

Sequential Numbering

Sometimes you want to output sequential numbering in your view's template. You do this by first using the $.view.index value in your template. By default all sequential numbering starts from 1. However, you can set a view to start from any number you want. You can even use a negative number. The number you provide will be the first number output in the template.

// Template numbering will begin at 10:
peopleView.startIndexFrom(10);

And here's a template using it:

<ul class="list cloak" id="arrayTemplate1">
  <li class='comp'>
    <h3>
      {= $.view.index }: {= data.firstName } {= data.lastName }
    </h3>
  </li>
</ul>

Formatting Output

You may need to format the data you are rendering in a view's template. ChocolateChip-UI provides a number of formatters to help you. These go right in the template where you want the data formated. ChocolateChip-UI has the following formatters:

  • formatNumber
  • sum
  • currency
  • formatTime
  • sortDate
  • sortNumbers
  • sortNumbersDescending

Please see temlates for more information about data formatters.

Using Template Helpers

Depending on your data, you may have the need to do some specialized formating of the data before outputing it. ChocolateChip-UI helps you accomplish this using a template helper. You do this using the defineHelper method:

$.defineHelper({
  upperCase: function(data) {
    return data.lastName.toUpperCase();
  }
});

You can then use it like so:

<ul class="list cloak" id="basicRepeater">
  <li>{= $.helpers.upperCase(data) }</li>
</ul>

Nested Templates

Rendering a data object that has a child element that is a collection is tricky. ChocolateChip-UI allows you to cover this by running JavaScript directly in your template. In fact, when the view is processed, your template gets converted into a JavaScript function. This means that you can use any JavaScript inside a template. You do need to escape the JavaScript code from the template. This is done using doube curly braces.

Let's say we want to output a list like in our above examples. But each list item might have a sub list. Using JavaScript in our template, we can have our view render the sub items. So let's use this data and see how to render the sub-lists of data:

var people = [
  {
    name: 'Joe',
    friends: [
      {
        name: "Sam"
      },
      {
        name: "Jack"
      }
    ]
  },,
  {
    name: 'Brad'
  }
  {
    name: 'Ellen',
    friends: [
      {
        name: 'Sherry'
      },
      {
        name: 'Jill'
      },
      {
        name: 'Bill'
      }
    ]
  },
  {
    name: 'Tom',
    friends: [
      {
        name: 'Joe'
      },
      {
        name: 'Mary'
      },
      {
        name: 'Kevin'
      }
    ]
  }
]

In the above data set, Brad has no friends. Therefore we want to check if each person has friends before create the friends list. Below is the code to make that happen. Notice that we check the person object to see it if has friends:

var peopleView = $.View({
  element: '#list',
  variable: 'person',
  data: people,
  template:
  `<li>
    <div>
      <h3>{= person.name }</h3>
      {{ if (person.friends && person.friends.length) { }}
        <div>
          <h3>Friends:</h3>
        </div>
        <div>
          <ol>
            {{  person.friends.forEach(function(friend) {  }}
            <li> {= friend.name}</li>
            {{  });  }}
          </ol>
        </div>
      {{ } }}
    </div>
  </li>`
});

peopleView.render();

And here's a working example:

See the Pen View Example 5 by Robert Biggs (@rbiggs) on CodePen.

Styles in Views

Since version 4.1.0, you can use the styles property to define styles for a view. This creates a virtual stylesheet for the view. This makes the view's style portable so you can reuse the view in other projects. If you are following the default patterns provided by ChocolateChip-UI you may not need to provide styles in a view. However, if you are creating a unique layout or customizing an existing one, you can use the styles property to make the view's layout look the way you want.

ChocolateChip-UI uses a special style object notation to define the styles for a view. Selectors must be quoted. These recevie a style object consisting of a property and value. Values must be quoted. Simple properties do not need to be quoted. However, if they are hyphenated, then they need to be quoted, otherwise you can use the camel case version of the property without quotes. The base of the stylesheet is the selector provided as the anchor of the view. In the example below, that will be the element #list. You can nest child elements in their parent selectors, similar to SASS and LESS. Note: Because this is object notation, CSS definitions do not end with a semi-colon but with a comma. Putting a semi-colon after your CSS value will throw an error.

// View with style object:
var myView = $.View({
  element: '#list',
  variable: 'person',
  template:
  `<li>
    <h3>{= person.name }</h3>
    <h4>{= person.age }</h4>
  <li>`,
  // Define a style object for the view:
  styles: {
    // Style the partent list:
    border: 'solid  2px green',
    margin: '10px 20px',
    // Style the list's child elements:
    '> li': {
      '> h3': {
        color: 'red'
      },
      '> h4': {
        color: 'blue'
      }

    }
  }
})

And here's the stylesheet that gets created:

#list: {
  border: solid  2px green;
  margin: 10px 20px;
}
#list > li > h3 {
  color: red;
}
#list > li > h4 {
  color: blue;
}

To learn more about using a view's style property, please go to the page dedicated to it.

Reusing Views

If you want to, you could reuse a single view again and again by chainging what element, template and data it uses. Although you can do this, organization-wise it's better to have an view object for each view in the document.

At run time you can change what element a view is attached to, what model it is bound to, what template it is using, and what events are bound to its rendered elements. In the following example we are rendering two scroll panels, one for red wines and one for whites. They are in two different places and with different data, otherwise their templates and events are identical:

var specialRedsView = $.View({
      element: '#picksRed',
      variable: 'wine',
      template: 
      "<li data-id='{= wine.id }'>\
        <div>\
          <h3>{= wine.name }</h3>\
          <h4>{= wine.winery }</h4>\
          <p>{= wine.year }</p>\
        </div>\
      </li>",
      events: [{
        event: 'tap', 
        element: 'li', 
        callback: function() {
          var whichWine = $(this).attr('data-id');
          // dispatch wine id with route:
          $.GoToScreen('selectedWine:' + whichWine);
        }
      }]
    });
    // Render with red wine data:
    specialRedsView.render(bestWines[0].data);

    // Reuse specialRedsView for white wines.
    // Change the parent element and render
    // with white wines.
    //=======================================
    var specialWhitesView = specialRedsView;
    // Set element to white wines scroll panel:
    specialWhitesView.setElement('#picksWhite');
    // Render with white wine data:
    specialWhitesView.render(bestWines[1].data);