Display a list from nested json: Sencha Touch 2

Wadester picture Wadester · Mar 30, 2012 · Viewed 10k times · Source

I have a list that displays a list of restaurants with the logo of the restaurant etc.

The view

Ext.define('Test.view.Contacts', {
    extend: 'Ext.List',
    xtype: 'contacts',

    config: {
        title: 'Stores',
        cls: 'x-contacts',

        store: 'Contacts',
        itemTpl: [
            '<div class="headshot" style="background-image:url(resources/images/logos/{logo});"></div>',
            '{name}',
            '<span>{add1}</span>'
        ].join('')
    }
});

When you tap the restaurant i want it to show another list based on the item tapped.

The second view

Ext.define('Test.view.Menu', {
    extend: 'Ext.List',
    xtype: 'contact-menu',

    config: {
        title: 'Menu',
        cls: 'x-contacts',

        store: 'Contacts',
        itemTpl: [
            '<div>{item}</div>'
        ].join(''),
    },
});

The models

Ext.define('Test.model.Contact', {
    extend: 'Ext.data.Model',

    config: {
        fields: [
            'name',
            'logo',
            'desc',
            'telephone',
            'city',
            'add1',
            'post',
            'country',
            'latitude',
            'longitude'
        ],
        proxy: {
            type: 'ajax',
            url: 'contacts.json'
        }
    },
    hasMany: {
        model: "Test.model.Menus",
        name: 'menus'
    }
});

Ext.define('Test.model.Menus', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
            'item'
        ]
    },
    belongsTo: "Test.model.Contact"
});

The store

Ext.define('Test.store.Contacts', {
  extend: 'Ext.data.Store',

  config: {
    model: 'Test.model.Contact',
    autoLoad: true,
    //sorters: 'name',
    grouper: {
      groupFn: function(record) {
        return record.get('name')[0];
      }
    },
    proxy: {
      type: 'ajax',
      url: 'contacts.json',
      reader: {
        type: 'json',
        root: 'stores'
      }
    }
  }
});

The JSON

{
    "stores": [{
        "name": "Science Gallery",
        "logo": "sciencegallery.jpg",
        "desc": "Get some food",
        "telephone": "016261234",
        "city": "Dublin",
        "add1": "Pearse Street",
        "post": "2",
        "country": "Ireland",
        "latitude": "53.34422",
        "longitude": "-6.25006",
        "menu": [{
            "item": "SC Sandwich"
        }, {
            "item": "SC Toasted Sandwich"
        }, {
            "item": "SC Panini"
        }, {
            "item": "SC Ciabatta"
        }, {
            "item": "SC Burrito"
        }]
    }, {
        "name": "Spar",
        "logo": "spar.jpg",
        "desc": "Get some food",
        "telephone": "016261234",
        "city": "Dublin",
        "add1": "Mayor Street",
        "post": "2",
        "country": "Ireland",
        "latitude": "53.34422",
        "longitude": "-6.25006",
        "menu": [{
            "item": "Spar Sandwich"
        }, {
            "item": "Spar Toasted Sandwich"
        }, {
            "item": "Spar Panini"
        }, {
            "item": "Spar Ciabatta"
        }, {
            "item": "Spar Burrito"
        }]
    }]
}

I want to show a list of menu items (item, item, item...) for the restaurant selectedbut when I use a nested list I have to use the same template as the previous list which doesnt suit my needs. At the moment I get the right amount of items but nothing shows. Can you please help me with where I'm going wrong, thanks.

Answer

rdougan picture rdougan · Mar 31, 2012

Before I get to the solution, here are a few problems with your code (which need to be fixed before the solution will work):

  • In your proxy config within the Contacts store, the config for the roog of your JSON is rootProperty, not root.

    proxy: {
        type: 'ajax',
        url: 'contacts.json',
        reader : {
            type : 'json',
            rootProperty : 'stores'
        }
    }
    

    You could also just put this code inside your model, as you already put a proxy config in there. Here are both merged (should be inside your model, and remove proxy from the store):

    proxy: {
        type: 'ajax',
        url: 'contacts.json',
        reader : {
            type : 'json',
            rootProperty : 'stores'
        }
    }
    
  • Model names should always be singular as they represent one object. So use Menu, not Menus.

  • You need to require any classes you use inside the class you use them. For example, you need the Sencha.model.Menu class inside the Sencha.model.Contact class, so add it inside the requires property inside Contact.js:

    Ext.define('Sencha.model.Contact', {
        extend: 'Ext.data.Model',
        requires: ['Sencha.model.Menu'],
    
        ...
    });
    
  • You need to use associationKey in your hasMany association as normally it would look for menus (generated from the Model name), but in your JSON is it menu.

  • hasMany and belongsTo configs should be inside config block within your models.

    Ext.define('Sencha.model.Contact', {
        extend: 'Ext.data.Model',
        requires: ['Sencha.model.Menu'],
    
        config: {
            ...
    
            hasMany: {
                model: "Sencha.model.Menu",
                associationKey: 'menu'
            }
        }
    });
    

As for the solution :) - you can modify your itemTpl inside your list to display associated for the record being shown. To do this, you can use:

<tpl for="associatedModelName">
     {field_of_associated_model}
</tpl> 

So in your case, you can do something like this:

itemTpl: [
    '{name}',
    '<div>',
        '<h2><b>Menu</b></h2>',
        '<tpl for="menus">',
            '<div> - {item}</div>',
        '</tpl>',
    '</div>'
].join('')

Here is a download of a project (generated using the SDK Tools) which includes a demo of this, using mostly your code: http://rwd.me/FS57