0

I am using ember-model for a simple invoicing app, i am new to ember and i cannot fix what's happening here, basically i am using ember-model's hasMany for embedded nested data, (the items for each invoice are in an array), i tried putting the code on jsbin and jsfiddle but it doesn't work up there, so for those who have time to help i put up an archive on my dropbox here : Ember-Model hasMany not updating in template's {{#each}}

To reproduce the bug, navigate to an invoice, then navigate to another invoice using the links, watch the items not updating as if they were stuck, but if you reload the page you will get the right items, what am i doing wrong :/ ?

The following code is of course a very simplified version of my actual app but it does reproduce the bug..

Thanks to anyone willing to help.

Index.html

<!DOCTYPE html>
<html>
<head>
  <meta name="description" content="Ember - Starter" />
  <meta charset=utf-8 />
  <title>Ember-Model</title>
  <script src="libs/jquery-1.9.1.js"></script>
  <script src="libs/handlebars-1.0.0.js"></script>
  <script src="libs/ember-1.1.2.js"></script>
  <script src="libs/ember-model-latest.js"></script>
  <script src="app.js"></script>
</head>
<body>

<script type="text/x-handlebars">
  <h1>Invoices</h1>
  {{ outlet }}
</script>

<script type="text/x-handlebars" data-template-name="factures">
{{#each}}
    <ul>
        <li>{{#link-to 'facture' this}}{{title}}{{/link-to}}</li>
    </ul>
{{/each}}
  <hr/>
    {{outlet}}
</script>

<script type="text/x-handlebars" data-template-name="facture">
<h2>Items for {{title}}</h2>
{{#each items}}
<ul>
  <li>{{desc}}</li>
</ul>
{{/each}}
</script>

</body>
</html>

App.js

var App = Ember.Application.create();

// ROUTER
App.Router.map(function () {
    this.resource('factures', function () {
        this.resource('facture', {
            path: '/:facture_id'
        });
    });
});

App.FacturesRoute = Ember.Route.extend({
    model: function (params) {
        return App.Facture.find();
    }
});

App.FactureRoute = Ember.Route.extend({
    model: function (params) {
        return App.Facture.fetch(params.facture_id).then(function (modelData) {
            return App.Facture.find(params.facture_id);
        });
    }
});

// Redirect
App.IndexRoute = Ember.Route.extend({
    redirect: function () {
        this.transitionTo('factures');
    }
});


// MODELS
var attr = Ember.attr,
    hasMany = Ember.hasMany,
    belongsTo = Ember.belongsTo;

// Factures
App.Facture = Ember.Model.extend({
    title: attr(),
    items: hasMany('App.Item', {
        key: 'items',
        embedded: true
    })
});

App.Facture.adapter = Ember.FixtureAdapter.create();

// Facture
// -> [ Items ]
App.Item = Ember.Model.extend({
    desc: attr(""),
    qty: attr("number"),
    price: attr("string")
});

// FIXTURES

App.Facture.FIXTURES = [
{
    id: 1,
    title: "Invoice #1",
    items: [
        {id: 1, desc: 'An Item for #1', qty: 2, price: "45"}, 
        {id: 2, desc: 'An Item for #1', qty: 5, price: "75"}
    ]
}, 
{
    id: 2,
    title: "Invoice #2",
    items: [
        {id: 1, desc: 'An Item for #2', qty: 2, price: "250"},
        {id: 2, desc: 'An Item for #2', qty: 5, price: "200"}
    ]
}];
Wilhearts
  • 169
  • 1
  • 1
  • 6

2 Answers2

0

Your items id's should be unique, ember model doesn't care if it's embedded or not, if it sees the same id it returns the model that's already existing for that id.

http://emberjs.jsbin.com/eKibapI/1/edit

App.Facture.FIXTURES = [
{
    id: 1,
    title: "Invoice #1",
    items: [
        {id: 1, desc: 'An Item for #1', qty: 2, price: "45"}, 
        {id: 2, desc: 'An Item for #1', qty: 5, price: "75"}
    ]
}, 
{
    id: 2,
    title: "Invoice #2",
    items: [
        {id: 3, desc: 'An Item for #2', qty: 2, price: "250"},
        {id: 4, desc: 'An Item for #2', qty: 5, price: "200"}
    ]
}];
Kingpin2k
  • 47,277
  • 10
  • 78
  • 96
  • Thanks alot, i am always surprised at how simple the solution seems to be, or how stupid i am.. i tried not setting any ID for items in my fixtures and it does work too. Thanks again. – Wilhearts Nov 23 '13 at 00:32
0

I'm not sure why you're wrapping App.Facture.find() in App.Facture.fetch(). Note that the way it is set up the return of your callback function will not be returned by your model hook. Try the following:

App.FactureRoute = Ember.Route.extend({
    model: function (params) {
        return App.Facture.find(params.facture_id);
    }
});

Of course this is also the default behavior, so you should be able to just remove the entire App.FactureRoute.

chopper
  • 6,649
  • 7
  • 36
  • 53
  • I am doing this to return a promise because in my app the template used to render before receiving the items, this way the template doesn't render before the promise is done (and the App stays in LoadingRoute) and everything is fine. – Wilhearts Nov 23 '13 at 00:30
  • You should be able to do App.Facture.find({id: params.facture_id}) instead. If I'm not mistaken that should return a promise. – chopper Nov 23 '13 at 05:16
  • Yup i tried without nesting in a fetch and it seems to work now, i am confused > – Wilhearts Nov 23 '13 at 10:58
  • Ember-model doesn't return promises by default, this caused more problems down the road so i had to use .fetch() instead of .find(), but not nested, this time (it is useless) – Wilhearts Nov 24 '13 at 23:06
  • If I'm not mistaken `find()` returns a promise if you specify query parameters (e.g. `find({id: params.facture_id})`). Give it a try and let me know if that works. – chopper Nov 25 '13 at 00:39