{{props}} for rendering top-level hash/dictionary

Equivalent to http://www.jsviews.com/#samples/editable/tags but using hash/dictionary of movies, rather than array. Also using {{on}} binding for all button and click actions.

by BorisMoore

HTML

<script src="//www.jsviews.com/download/jsviews.js"></script>
<link rel="stylesheet" href="http://www.jsviews.com/samples/editable-data/sample.css">
<link rel="stylesheet" href="https://www.jsviews.com/samples/editable-data/sample.css">
<div class="buttons">
  <button data-link="{on ~showData}">show data</button>
  <button  data-link="{on ~deleteLast}">delete last language</button>
</div>

<div class="comment">Click to select and edit</div>

<div id="movieList"></div>

<script id="movieTemplate" type="text/x-jsrender">
  <table>
    <thead><tr>
      <th>Title</th><th>Languages</th>
      <th class="addMovie" data-link="{on ~addMovie}">Add</th>
    </tr></thead>
    <tbody class="movies">
      {^{props}}
        <tr class="hover" data-link="css-background-color{:~bgColor()} {on ~select key}">
          <td>
            {^{:#index + 1}}: {^{>prop.title}}
          </td>
          <td>
            {^{for prop.languages}}
              <div>{^{>name}}</div>
            {{/for}}
          </td>
          <td><span class="removeMovie" data-link="{on ~removeMovie key}"></span></td>
        </tr>
      {{/props}}
    </tbody>
  </table>

  <div class="detail">
    {^{for #data[~selectedKey]}}
      <div>
        <div class="title">Title:</div>
        <div><input data-link="title" /></div>
        <div class="title">
          Languages: <span class="addLanguage" data-link="{on ~addLanguage languages}">Add</span>
        </div>
        {^{for languages ~languages=languages}}
          <input data-link="name" />
          <span class="removeLanguage" data-link="{on ~removeLanguage ~languages #index}"></span>
        {{/for}}
      </div>
    {{/for}}
  </div>
</script>

<script id="showData" type="text/x-jsrender">
  {{props}}
	<div>
    <b>Movie:</b> {{>prop.title}}
    <b>Languages:</b> {{for prop.languages}} {{>name}}{{/for}}
  </div>
  {{/props}}
</script>

<div id="console"></div>

JavaScript

"use strict";
var counter = 0,
  movies = { // Hash/dictionary of movies
    movJb: {
      title:"Meet Joe Black",
      languages: [
        {name: "English"},
        {name: "French"}
      ]
    },
    movEws: {
      title:"Eyes Wide Shut",
      languages: [
        {name: "German"},
        {name: "French"},
        {name: "Spanish"}
      ]
    }
  },

  helpers = {
    bgColor: bgColor,
    select: function select(key, ev, eventArgs) {
      eventArgs.view.ctxPrm("selectedKey", key);
    },
    addMovie: function(ev, eventArgs) {
      var newKey = "mov" + counter;
      $.observable(movies).setProperty(
        newKey,
        {
          title: "NewTitle" + counter,
          languages: [
            {name: "NewLanguage" + counter++}
          ]}
      );
      eventArgs.view.ctxPrm("selectedKey", newKey);
    },
    removeMovie: function(key, ev, eventArgs) {
      eventArgs.view.ctxPrm("selectedKey", null);
      $.observable(movies).removeProperty(key);
      return false;
    },
    addLanguage: function(languages) {
      $.observable(languages).insert({
        name: "NewLanguage" + counter++
      });
    },
    removeLanguage: function(languages, index) {
      $.observable(languages).remove(index);
      return false;
    },
    deleteLast: function() {
      var propsArray = $.view("#movieList").get(true, "array").data;
      if (propsArray.length) {
        var lastMovie = propsArray[propsArray.length - 1].prop;
        $.observable(lastMovie.languages).remove();
      }
    },
    showData: function() {
      $("#console").append("<hr/>" + $("#showData").render(movies));
    }
  },

  movieTmpl = $.templates("#movieTemplate");

function bgColor() {
  return this.ctxPrm("selectedKey")===this.data.key
    ? "yellow"
    : (this.index%2 ? "#fdfdfe" : "#efeff2");
}
bgColor.depends = ["#index", "~selectedKey"];

// Render movies
movieTmpl.link("#movieList", movies, helpers);

// Data-link top-level buttons
$.link(true, ".buttons", movies, helpers);