0
votes

I have some kind of a "dash view" with a subset of items displayed. The item may "toggled" (by some navigation) for display in the main outlet. So far I lived without Ember's Router / StateManager stuff but instead I serialized/deserialized the URL manually and used window.history to enable back/foward and "restore by URL" features. e.g.

#/dash/foo,bar 
#/dash/foo,bar,ted

should display the foo, bar and ted elements respectively. The items may be lazy loaded in the background. So if you restore the app state by bookmarking/directly editing the URL, the items actually may arrive some time later.

I try to employ Ember.Router for this stuff, but I just dont get it working...

I read through this great guide http://emberjs.com/guides/router_primer/. But the scenario described is always like: you have a collection view and a single item view. But that does not apply for my app.

In this scenario I have 3 items and I want to select some of them for display in the outlet, by toggling them. (In general this could be selecting,unselecting,toggling or search-by-expression). The selected-items-in-dash-state should be serialized to URL and be restorable by bookmarking or directly modyfing the URL.

Toggling is implemented like this:

<a {{action toggle name href=true}}>{{name}}</a>

This is how the data fetching and selection logic is right now (ITS NOT WORKING THOUGH)

App.dash = Ember.Object.create({
    FIXTURE: [App.Item.create({
        name: 'foo',
        detail: 'Some very interesting foo'
    }),App.Item.create({
        name: 'bar',
        detail: 'Bar is the root of all stuff'
    }),App.Item.create({
        name: 'ted',
        detail: 'Ted is not my name'
    })],
    store: Ember.A(),
    selected: Ember.A(),
    _items: Ember.A(),
    watchItems: function () {
        var items = this._items;
        var store = this.store;
        items.clear();
        this.get('selected').forEach(function (name) {
            var item = store.findProperty('name', name);
            items.pushObject(item);
        });
    }.observes('selected', 'store'),
    toggle: function (name) {
        var selected = this.get('selected');
        var index = selected.indexOf(name);
        if (index !== -1) {
            selected.removeAt(index);
        } else {
            selected.push(name);
        }
        this.set('selected', selected);
    },
    fetch: function () {
        var FIXTURE = this.FIXTURE;
        var store = this.store;
        if (store.length == 0) {
            Ember.run.later(function () {
                FIXTURE.forEach(function (item) {
                    store.pushObject(item);
                });
            }, 2000);
        }
    },        
    items: function (selected) {
        var items = this._items;
        items.clear();
        if (Ember.isArray(selected)) {
            this.set('selected', selected);
        } else {
            this.set('selected', []);
        }
        this.fetch();
        return items;
    }
});

And that is the router:

 App.Router = Ember.Router.extend({
    root: Ember.Route.extend({
        index: Ember.Route.extend({
            route: '/',
            redirectsTo: 'dash'
        }),
        dash: Ember.Route.extend({
            route: '/dash/:selected',
            connectOutlets: function (router, context) {
                var selected = context && context.selected;
                var dashItems = App.dash.items(selected);
                router.get('applicationController').connectOutlet('dash', dashItems);
            },
            deserialize: function (router, params) {
                if (params.selected !== 'undefined') {
                    return params.selected;
                }
            },
            serialize: function (router, context) {
                var selected = App.dash.get('selected');
                var hash = {
                    selected: selected
                };
                return hash;
            },
            toggle: function (router, event) {
                event.preventDefault();
                event.stopPropagation();
                var name = event.context;
                App.dash.toggle(name);
                var selected = App.dash.get('selected');
                router.transitionTo('dash', {
                    selected: selected
                });
            },
            eventTransitions: {
                toggle: 'dash'
            }
        })
    })
});

This is the complete fiddle http://jsfiddle.net/NWfbj/1

Any help is very appreciated!

1
in the items fonction, you call items.clear(), then return items. is it what you really want ? - sly7_7
yes, that is what is intended. the items function should clear the current selection / all the items displayed now. this._items will be populated in the watchItems observer (async). i tried to be close to the Router Primer emberjs.com/guides/router_primer App.Shoe.find method. - lipp

1 Answers

1
votes

Well, I try to fix some things:

  • when accessing properties in an Ember oject, a good rule is to always use get('property')/set('property',value')

  • When you want to observe arrays content, use the special '@each'. The observer is then triggered when any object in the array is added/removed.

  • I removed what I thought an extra items.clear() in the items() function.

If you want to be able to manipulate through the URL, I think you have to review the deserialize method, in order to return what you expect (I don't know if it should be string array, an array of items (retrieved from the store), it's up to you :)

You can see what I've done here: http://jsfiddle.net/Sly7/NWfbj/23/

<script type="text/x-handlebars" data-template-name='application'>
  <h1>Dash Router App</h1>
  {{#each App.dash.store}}
  <a {{action toggle name href=true}}>{{name}}</a>
  {{/each}}
  {{outlet}}
</script>

<script type="text/x-handlebars" data-template-name='dash'>
   {{#each controller}}
    <h1>{{detail}}</h1>
   {{/each}}
</script>
App = Ember.Application.create();
App.Item = Ember.Object.extend({
    name: null,
    detail: null
});

App.dash = Ember.Object.create({
    FIXTURE: [{
        name: 'foo',
        detail: 'Some very interesting foo'
    }, {
        name: 'bar',
        detail: 'Bar is the root of all stuff'
    }, {
        name: 'ted',
        detail: 'Ted is not my name'
    }],
    store: [],

    selected: [],

    _items: [],

    watchItems: function () {
        console.log('watch items', this.get('selected'));
        var self = this;
        this.get('_items').clear();
        this.get('selected').forEach(function (name) {
            var item = self.get('store').findProperty('name', name);
            console.log(name);
            self.get('_items').pushObject(item);
        });
    }.observes('selected.@each', 'store.@each'),

    toggle: function (name) {
        var selected = this.get('selected');
        var index = selected.indexOf(name);
        if (index !== -1) {
            selected.removeObject(name);
        } else {
            selected.pushObject(name);
        }
        console.log('toggle', name, 'is now selected?', selected.indexOf(name) !== -1, selected);
        this.set('selected', selected);
    },

    fetch: function () {
        var FIXTURE = this.get('FIXTURE');
        var store = this.get('store');
        if (store.length == 0) {
            Ember.run.later(function () {
                FIXTURE.forEach(function (item) {
                    store.pushObject(item);
                });
            }, 2000);
        }
    },  

    items: function (selected) {
        if (Ember.isArray(selected)) {
            this.set('selected', selected);
        } else {
            this.set('selected', []);
        }
        this.fetch();
        return this.get('_items');
    }
});
App.ApplicationController = Ember.Controller.extend({});
App.ApplicationView = Ember.View.extend({
    templateName: 'application'
});
App.DashView = Ember.View.extend({
    templateName: 'dash'
});
App.DashController = Ember.ArrayController.extend({});

App.Router = Ember.Router.extend({
    enableLogging: true,
    root: Ember.Route.extend({
        index: Ember.Route.extend({
            route: '/',
            redirectsTo: 'dash'
        }),
        dash: Ember.Route.extend({
            enter: function () {
                console.log('entering dash');
            },
            exit: function () {
                console.log('exiting dash');
            },
            route: '/dash/:selected',
            connectOutlets: function (router, context) {
                debugger;
                var selected = context && context.selected;
                console.log('connectOutlets', 'selected:', selected);
                var dashItems = App.dash.items(selected);
                console.log('connectOutlets', dashItems);
                router.get('applicationController').connectOutlet('dash', dashItems);
            },
            deserialize: function (router, params) {
                debugger;
                console.log('deserialize', 'selected:', params.selected);
                if (params.selected !== 'undefined') {
                    return params.selected;
                }
            },
            serialize: function (router, context) {
                var selected = App.dash.get('selected');
                var hash = {
                    selected: selected
                };
                console.log('serialize', 'selected:', selected);
                return hash;
            },
            toggle: function (router, event) {
                debugger;
                event.preventDefault();
                event.stopPropagation();
                var name = event.context;
                console.log('clicked',name);
                App.dash.toggle(name);
                var selected = App.dash.get('selected');
                console.log('transitionTo', 'selected:', selected);
                router.transitionTo('dash', {
                    selected: selected
                });
            },
            eventTransitions: {
                toggle: 'dash'
            }
        })
    })
});
App.initialize();