rendrjs / rendr-handlebars

Handlebars template adapter for Rendr apps
MIT License
21 stars 31 forks source link

Add context option for child views like in NestedView #2

Open ghost opened 11 years ago

ghost commented 11 years ago

Why you don't added context option binding for child views like in your NestedView plugin for Backbone ?

Example: {{#each models}} {{view "myChildView" context=this}} {{/each}}

spikebrehm commented 11 years ago

Interesting. I suppose I never encountered that case. It also seems more brittle, and safer to explicitly pass in specific attributes.

What's your use case?

ghost commented 11 years ago

app/templates/places/list.hbs {{#each places}} {{view "places/place" context=this}} {{/each}}

app/templates/places/place.hbs {{context.name}}

It's better if we can do that:

app/templates/places/list.hbs {{#each places}} {{view "places/place" context=this}} {{/each}}

app/templates/places/place.hbs {{name}}

spikebrehm commented 11 years ago

Hmm. Well, typically you'd be working with models and collections, right? So you could do something like:

{{#each places}}
{{view "places/place" model=this}}
{{/each}}
ghost commented 11 years ago

app/templates/places/list.hbs {{#each places}} {{view "places/place" model=this}} {{/each}}

app/templates/places/place.hbs {{name}}

I have this exception: Express 500 TypeError: Object # has no method 'toJSON' at module.exports.Backbone.View.extend.getTemplateData (/Users/chris/workspace/croosty/croosty-app/node_modules/rendr/shared/base/view.js:97:25)

ghost commented 11 years ago

It's working when I do that, tell me if it's a good usage.

app/controllers/places_controller.js

var _ = require('underscore');

module.exports = {
  index: function(params, callback) {
    var spec = { 
      places: { collection: 'Places', params: { location: params['location'] }, expires: 3600 }
    };

    this.app.fetch(spec, function(err, results) {
      if (err) return callback(err);
      callback(err, results);
    });
  },

  show: function(params, callback) {
    var spec = {
      place: { model: 'Place', params: params }
    };

    this.app.fetch(spec, function(err, result) {
      if (err) return callback(err);
      callback(err, result);
    });
  }
};

app/views/places/index.js

View = require('../../base/view');

module.exports = View.extend({
  className: 'places-index',

  getTemplateData: function() {
    var data = View.prototype.getTemplateData.call(this);
    data.location = this.options.places.meta.location;
    data.places_count = this.options.places.meta.places_count;
    return data;
  }
});

module.exports.id = 'places/index';

app/templates/places/index.hbs

<aside id="sidebar">
  <header id="location-infos">
    <h2>{{places_count}} restaurants à {{location.name}}</h2>
  </header>

  {{view "places/list" collection=places}}
</aside>

app/views/places/list.js

View = require('../../base/view');

module.exports = View.extend({
  tagName: 'ul',
  id: 'places-list'
});

module.exports.id = 'places/list';

app/templates/places/list.hbs

{{#each _collection.models}}
  {{view "places/place" model=this}}
{{/each}}

app/views/places/place.js

View = require('../../base/view');

module.exports = View.extend({
  tagName: 'li',
  className: 'place'
});

module.exports.id = 'places/place';

app/template/places/place.hbs

{{name}}
jeromegn commented 10 years ago

I'm stumbling on the same issue.

I tried your code. For the first instantiation, it seems fine. However, if I reload the same page (in rendr), then all of my models seem to equal the last model in the array. Caching issue?

Also, curiously _models and _collection.models is not the same (first one will give me "toJSON error").

Are we doing something wrong?

jeromegn commented 10 years ago

Looks like adding a idAttribute to my model (because they don't have an id) makes this go away. Nice.

spikebrehm commented 10 years ago

Good catch re: idAttribute!

vincentbello commented 10 years ago

I found myself being able to nest views within loops like this:

{{#each models}}
   {{view="xview" model=this}}
{{/each}}

And then, in xview.js:

var BaseView = require('./base');
module.exports = BaseView.extend({
    getTemplateData: function () {
        return this.model;
    }
});
module.exports.id = 'xview';

Because in xview, this.model is just an array. But in this case, the "xview" instances lose their model. All the views in App.router.currentView.childViews have model "undefined." Is there any workaround for this? It would be tremendously useful to be able to do this to nest views and still access these views' models.

StevenLangbroek commented 9 years ago

+1 on this. This allows for a nice and clear encapsulation of model's view logic within a template (click handlers etc), without having to override the getTemplateData, or breaking convention (a view's model property should always be exactly that: its model, not a JSON representation of a model).