AmpersandJS / ampersand-view

A smart base view for Backbone apps, to make it easy to bind collections and properties to the DOM.
http://ampersandjs.com
MIT License
92 stars 39 forks source link

Please explain how subviews work #61

Closed gr2m closed 10 years ago

gr2m commented 10 years ago

I try to build a simple app showing a console and a log of the executed commands. I have a main view and two subviews: one for the current command, and one for the log:

var View = require('ampersand-view');
var AmpersandModel = require('ampersand-model');
var AmpersandCollection = require('ampersand-collection');

var domready = require('domready');

// VIEWS
var MainView = View.extend({
    template: '\
        <body>\
            <h1>Hoodie Console</h1>\
            <div data-hook="console">\
            </div>\
            <div data-hook="log">\
            </div>\
        </body>',
    autoRender: true,

    subviews: {
      input: {
        hook: 'console',
        waitFor: 'model',
        constructor: ConsoleView
      },
      log: {
        hook: 'log',
        waitFor: 'collection',
        constructor: LogView
      }
    },
});
var ConsoleView = View.extend({
    template: '\
        <div data-hook="console">\
            &gt; <span data-hook="command"></span>\
        </div>',
    autoRender: true,
    bindings: {
        'model.name': { hook: 'name'}
    }
});
var LogView = View.extend({
    template: '\
        <div data-hook="log">\
            <h2>Log</h2>\
            <ul data-hook="commands"></ul>\
        </div>',
    autoRender: true,
    render: function() {
        this.renderWithTemplate(this);
        this.renderCollection(this.collection, LogItemView, this.queryByHook('commands'));
        return this;
    }
});
var LogItemView = View.extend({
    template: '<li data-hook="command"></li>'
})

// MODELS
var Command = AmpersandModel.extend({
    props: {
        name: ['string', true, '']
    }
});
var CommandCollection = AmpersandCollection.extend({
    model: Command,
    bindings: {
        'model.name': { hook: 'name'}
    }
});

var app = window.app = {};
domready(function () {
    app.command = new Command({name: 'hoodie.'});
    app.commands = new CommandCollection({
        name: 'hoodie.account.signUp("foo", "bar")'
    },{
        name: 'hoodie.store.findAll()'
    },{
        name: 'hoodie.id()'
    });
    self.view = new MainView({
        el: document.body,
        model: app.command,
        collection: app.commands
    });
});

The resulting HTML currently looks like

<body>
    <h1>Hoodie Console</h1>
    <div data-hook="console"></div>
    <div data-hook="log"></div>
</body>

I'd have expected

<body>
    <h1>Hoodie Console</h1>
    <div data-hook="console">&gt; <span data-hook="command">hood.ie</span></div>
    <div data-hook="log">
        <h2>Log</h2>
        <ul data-hook="commands">
                <li data-hook="command">hoodie.account.signUp("foo", "bar")</li>
                <li data-hook="command">hoodie.store.findAll()</li>
                <li data-hook="command">hoodie.id()</li>
        </ul>
    </div>
</body>

I'm sure I get something very basic wrong, I'm just getting started with &.js. Could you please help me to get this right?

Here's my package.json for reference

{
  "name": "hoodie-console",
  "version": "0.0.0",
  "description": "Hoodie Console",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Gregor Martynus <gregor@thehoodiefirm.com>",
  "license": "MIT",
  "dependencies": {
    "ampersand-collection": "~1.3.16",
    "ampersand-model": "~4.0.2",
    "ampersand-view": "~6.0.11",
    "domready": "^1.0.5",
    "hapi": "^6.0.2",
    "moonboots_hapi": "^2.3.2"
  }
}
gr2m commented 10 years ago

Okay, I found the problem, wrong ampersand-view version :) Which is odd, because I run npm install --save ampersand-view before.

But anyway, I'd like to strongly support the suggestions for the shorthand subview declarations at https://github.com/AmpersandJS/ampersand-view/issues/43, feels more than necessary repetitive code right now. For reference, here's the code I ended up with:

var View = require('ampersand-view');
var AmpersandModel = require('ampersand-model');
var AmpersandCollection = require('ampersand-collection');

var domready = require('domready');

// VIEWS
var ConsoleView = View.extend({
    template: '\
        <div data-hook="console">\
            &gt; <span data-hook="command"></span>\
        </div>',
    bindings: {
        'model.name': {
            type: 'text',
            hook: 'command'
        }
    }
});
var LogView = View.extend({
    template: '\
        <div data-hook="log">\
            <h2>Log</h2>\
            <ul data-hook="commands"></ul>\
        </div>',
    render: function() {
        this.renderWithTemplate(this);
        this.renderCollection(this.collection, LogItemView, this.queryByHook('commands'));
        return this;
    }
});
var LogItemView = View.extend({
    template: '<li data-hook="command"></li>',
    bindings: {
        'model.name': {
            type: 'text',
            hook: 'command'
        }
    }
})
var MainView = View.extend({
    template: '\
        <body>\
            <h1>Hoodie Console</h1>\
            <div data-hook="console">\
            </div>\
            <div data-hook="log">\
            </div>\
        </body>',
    autoRender: true,

    subviews: {
      input: {
        hook: 'console',
        waitFor: 'model',
        constructor: ConsoleView,
        prepareView: function (el) {
            return new this.subviews.input.constructor({
                el: el,
                parent: this,
                model: this.model
            });
        }
      },
      log: {
        hook: 'log',
        waitFor: 'collection',
        constructor: LogView,
        prepareView: function (el) {
            return new this.subviews.log.constructor({
                el: el,
                parent: this,
                collection: this.collection
            });
        }
      }
    },
});

// MODELS
var Command = AmpersandModel.extend({
    props: {
        name: ['string', true, '']
    }
});
var CommandCollection = AmpersandCollection.extend({
    model: Command
});

var app = window.app = {};
domready(function () {
    app.command = new Command({name: 'hoodie.'});
    app.commands = new CommandCollection([{
        name: 'hoodie.account.signUp("foo", "bar")'
    },{
        name: 'hoodie.store.findAll()'
    },{
        name: 'hoodie.id()'
    }]);
    self.view = new MainView({
        el: document.body,
        model: app.command,
        collection: app.commands
    });
});
kamilogorek commented 10 years ago

Roger that! :+1:

HenrikJoreteg commented 10 years ago

@gr2m finally sat down to look at this (i had seen your tweet before) but looks like you figured it out.