Example #1
0
/**
  Lookup both on root and on window. If the path starts with
  a keyword, the corresponding object will be looked up in the
  template's data hash and used to resolve the path.

  @method get
  @for Ember.Handlebars
  @param {Object} root The object to look up the property on
  @param {String} path The path to be lookedup
  @param {Object} options The template's option hash
*/
function handlebarsGet(root, path, options) {
  var data = options && options.data;
  var normalizedPath = normalizePath(root, path, data);
  var value;

  // In cases where the path begins with a keyword, change the
  // root to the value represented by that keyword, and ensure
  // the path is relative to it.
  root = normalizedPath.root;
  path = normalizedPath.path;

  value = get(root, path);

  if (isGlobalPath(path)) {
    if (value === undefined && root !== Ember.lookup) {
      root = Ember.lookup;
      value = get(root, path);
    }
    if (root === Ember.lookup || root === null) {
      Ember.deprecate("Global lookup of "+path+" from a Handlebars template is deprecated.", options.silenceGlobalDeprecation);
    }
  }

  return value;
}
Example #2
0
/**
  Use the `{{with}}` helper when you want to scope context. Take the following code as an example:

  ```handlebars
  <h5>{{user.name}}</h5>

  <div class="role">
    <h6>{{user.role.label}}</h6>
    <span class="role-id">{{user.role.id}}</span>

    <p class="role-desc">{{user.role.description}}</p>
  </div>
  ```

  `{{with}}` can be our best friend in these cases,
  instead of writing `user.role.*` over and over, we use `{{#with user.role}}`.
  Now the context within the `{{#with}} .. {{/with}}` block is `user.role` so you can do the following:

  ```handlebars
  <h5>{{user.name}}</h5>

  <div class="role">
    {{#with user.role}}
      <h6>{{label}}</h6>
      <span class="role-id">{{id}}</span>

      <p class="role-desc">{{description}}</p>
    {{/with}}
  </div>
  ```

  ### `as` operator

  This operator aliases the scope to a new name. It's helpful for semantic clarity and to retain
  default scope or to reference from another `{{with}}` block.

  ```handlebars
  // posts might not be
  {{#with user.posts as blogPosts}}
    <div class="notice">
      There are {{blogPosts.length}} blog posts written by {{user.name}}.
    </div>

    {{#each post in blogPosts}}
      <li>{{post.title}}</li>
    {{/each}}
  {{/with}}
  ```

  Without the `as` operator, it would be impossible to reference `user.name` in the example above.

  NOTE: The alias should not reuse a name from the bound property path.
  For example: `{{#with foo.bar as foo}}` is not supported because it attempts to alias using
  the first part of the property path, `foo`. Instead, use `{{#with foo.bar as baz}}`.

  ### `controller` option

  Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of
  the specified controller with the new context as its content.

  This is very similar to using an `itemController` option with the `{{each}}` helper.

  ```handlebars
  {{#with users.posts controller='userBlogPosts'}}
    {{!- The current context is wrapped in our controller instance }}
  {{/with}}
  ```

  In the above example, the template provided to the `{{with}}` block is now wrapped in the
  `userBlogPost` controller, which provides a very elegant way to decorate the context with custom
  functions/properties.

  @method with
  @for Ember.Handlebars.helpers
  @param {Function} context
  @param {Hash} options
  @return {String} HTML string
*/
function withHelper(context, options) {
  var bindContext, preserveContext, controller, helperName = 'with';

  if (arguments.length === 4) {
    var keywordName, path, rootPath, normalized, contextPath;

    Ember.assert("If you pass more than one argument to the with helper, it must be in the form #with foo as bar", arguments[1] === "as");
    options = arguments[3];
    keywordName = arguments[2];
    path = arguments[0];

    if (path) {
      helperName += ' ' + path + ' as ' + keywordName;
    }

    Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop);

    var localizedOptions = o_create(options);
    localizedOptions.data = o_create(options.data);
    localizedOptions.data.keywords = o_create(options.data.keywords || {});

    if (isGlobalPath(path)) {
      contextPath = path;
    } else {
      normalized = normalizePath(this, path, options.data);
      path = normalized.path;
      rootPath = normalized.root;

      // This is a workaround for the fact that you cannot bind separate objects
      // together. When we implement that functionality, we should use it here.
      var contextKey = jQuery.expando + guidFor(rootPath);
      localizedOptions.data.keywords[contextKey] = rootPath;
      // if the path is '' ("this"), just bind directly to the current context
      contextPath = path ? contextKey + '.' + path : contextKey;
    }

    localizedOptions.hash.keywordName = keywordName;
    localizedOptions.hash.keywordPath = contextPath;

    bindContext = this;
    context = path;
    options = localizedOptions;
    preserveContext = true;
  } else {
    Ember.assert("You must pass exactly one argument to the with helper", arguments.length === 2);
    Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop);

    helperName += ' ' + context;
    bindContext = options.contexts[0];
    preserveContext = false;
  }

  options.helperName = helperName;
  options.isWithHelper = true;

  return bind.call(bindContext, context, options, preserveContext, exists);
}
Example #3
0
 contextualizeBindingPath: function(path, data) {
   var normalized = normalizePath(null, path, data);
   if (normalized.isKeyword) {
     return 'templateData.keywords.' + path;
   } else if (isGlobalPath(path)) {
     return null;
   } else if (path === 'this' || path === '') {
     return '_parentView.context';
   } else {
     return '_parentView.context.' + path;
   }
 },
Example #4
0
 contextualizeBindingPath: function(path, data) {
   if (SELF_BINDING.test(path)) {
     return path.slice(6); // Lop off "_view."
   }
   var normalized = normalizePath(null, path, data);
   if (normalized.isKeyword) {
     return 'templateData.keywords.' + path;
   } else if (isGlobalPath(path)) {
     return null;
   } else if (path === 'this' || path === '') {
     return '_parentView.context';
   } else {
     return '_parentView.context.' + path;
   }
 },
Example #5
0
  arrayDidChange: function(content, start, removed, added) {
    var addedViews = [];
    var view, item, idx, len, itemViewClass, emptyView;

    len = content ? get(content, 'length') : 0;

    if (len) {
      itemViewClass = get(this, 'itemViewClass');
      itemViewClass = readViewFactory(itemViewClass, this.container);

      for (idx = start; idx < start+added; idx++) {
        item = content.objectAt(idx);

        view = this.createChildView(itemViewClass, {
          content: item,
          contentIndex: idx,
          _blockArguments: [item]
        });

        addedViews.push(view);
      }
    } else {
      emptyView = get(this, 'emptyView');

      if (!emptyView) { return; }

      if ('string' === typeof emptyView && isGlobalPath(emptyView)) {
        emptyView = get(emptyView) || emptyView;
      }

      emptyView = this.createChildView(emptyView);
      addedViews.push(emptyView);
      set(this, 'emptyView', emptyView);

      if (CoreView.detect(emptyView)) {
        this._createdEmptyView = emptyView;
      }
    }

    this.replace(start, 0, addedViews);
  },
Example #6
0
/**
  Use the `{{with}}` helper when you want to scope context. Take the following code as an example:

  ```handlebars
  <h5>{{user.name}}</h5>

  <div class="role">
    <h6>{{user.role.label}}</h6>
    <span class="role-id">{{user.role.id}}</span>

    <p class="role-desc">{{user.role.description}}</p>
  </div>
  ```

  `{{with}}` can be our best friend in these cases,
  instead of writing `user.role.*` over and over, we use `{{#with user.role}}`.
  Now the context within the `{{#with}} .. {{/with}}` block is `user.role` so you can do the following:

  ```handlebars
  <h5>{{user.name}}</h5>

  <div class="role">
    {{#with user.role}}
      <h6>{{label}}</h6>
      <span class="role-id">{{id}}</span>

      <p class="role-desc">{{description}}</p>
    {{/with}}
  </div>
  ```

  ### `as` operator

  This operator aliases the scope to a new name. It's helpful for semantic clarity and to retain
  default scope or to reference from another `{{with}}` block.

  ```handlebars
  // posts might not be
  {{#with user.posts as blogPosts}}
    <div class="notice">
      There are {{blogPosts.length}} blog posts written by {{user.name}}.
    </div>

    {{#each post in blogPosts}}
      <li>{{post.title}}</li>
    {{/each}}
  {{/with}}
  ```

  Without the `as` operator, it would be impossible to reference `user.name` in the example above.

  NOTE: The alias should not reuse a name from the bound property path.
  For example: `{{#with foo.bar as foo}}` is not supported because it attempts to alias using
  the first part of the property path, `foo`. Instead, use `{{#with foo.bar as baz}}`.

  ### `controller` option

  Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of
  the specified controller with the new context as its content.

  This is very similar to using an `itemController` option with the `{{each}}` helper.

  ```handlebars
  {{#with users.posts controller='userBlogPosts'}}
    {{!- The current context is wrapped in our controller instance }}
  {{/with}}
  ```

  In the above example, the template provided to the `{{with}}` block is now wrapped in the
  `userBlogPost` controller, which provides a very elegant way to decorate the context with custom
  functions/properties.

  @method with
  @for Ember.Handlebars.helpers
  @param {Function} context
  @param {Hash} options
  @return {String} HTML string
*/
function withHelper(context, options) {
  if (arguments.length === 4) {
    var keywordName, path, rootPath, normalized, contextPath;

    Ember.assert("If you pass more than one argument to the with helper, it must be in the form #with foo as bar", arguments[1] === "as");
    options = arguments[3];
    keywordName = arguments[2];
    path = arguments[0];

    Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop);

    var localizedOptions = o_create(options);
    localizedOptions.data = o_create(options.data);
    localizedOptions.data.keywords = o_create(options.data.keywords || {});

    if (isGlobalPath(path)) {
      contextPath = path;
    } else {
      normalized = normalizePath(this, path, options.data);
      path = normalized.path;
      rootPath = normalized.root;

      // This is a workaround for the fact that you cannot bind separate objects
      // together. When we implement that functionality, we should use it here.
      var contextKey = jQuery.expando + guidFor(rootPath);
      localizedOptions.data.keywords[contextKey] = rootPath;
      // if the path is '' ("this"), just bind directly to the current context
      contextPath = path ? contextKey + '.' + path : contextKey;
    }

    emberBind(localizedOptions.data.keywords, keywordName, contextPath);

    return bind.call(this, path, localizedOptions, true, exists);
  } else {
    Ember.assert("You must pass exactly one argument to the with helper", arguments.length === 2);
    Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop);
    return helpers.bind.call(options.contexts[0], context, options);
  }
}
Example #7
0
test("if there is a 'this' in the path, it's not a global path", function() {
  ok( !isGlobalPath('this.myProperty') );
  ok( !isGlobalPath('this') );
});
Example #8
0
test("global path's are recognized", function() {
  ok( isGlobalPath('App.myProperty') );
  ok( isGlobalPath('App.myProperty.subProperty') );
});
Example #9
0
test("if the path starts with a lowercase character, it is not a global path", function() {
  ok( !isGlobalPath('myObj') );
  ok( !isGlobalPath('myObj.SecondProperty') );
});
QUnit.test('if there is a \'this\' in the path, it\'s not a global path', function() {
  ok(!isGlobalPath('this.myProperty'));
  ok(!isGlobalPath('this'));
});
QUnit.test('global path\'s are recognized', function() {
  ok(isGlobalPath('App.myProperty'));
  ok(isGlobalPath('App.myProperty.subProperty'));
});