gmac / backbone.epoxy

Declarative data binding and computed models for Backbone
http://epoxyjs.org
MIT License
615 stars 89 forks source link

From what context to get select options source? #95

Closed patrick-radius closed 9 years ago

patrick-radius commented 10 years ago

Hi, i'm hoping to transfer from backbone.stickit to backbone.epoxy. Mostly epoxy seems better to our needs. But i'm trying to get a <select> filled.

in stickit i would do:

    bindings:{   
         '#category': {
                observe: 'category_id',
                selectOptions: {
                    collection: function(){
                        return this.App.request('collection', 'frontofficemenu');
                    },
                    labelPath: 'name',
                    valuePath: 'id'
                }
            }
      }

the this.App.request returns a collection i'd like to use. Is there some way to do this in epoxy, including the labelPath and valuePath options set per view? maybe through a custom handler?

maxpoulin64 commented 10 years ago

Use the options: handler, it is made specially for <select>s. options: takes either an array or a Backbone collection, which can either contain plain objects or models. The handler picks the label and value property of the object/model. You can use Epoxy computeds on your model to map the label and value to the appropriate field(s) if needed, or use Epoxy.bindings.config() to change it globally if all your models follow an existing naming convetion.

If that still doesn't do it then you may want to consider writing your own binding, it's not too complicated.

patrick-radius commented 10 years ago

thanks for the reply, i did see the options: handler was meant for that, but i don't see how to get the collection from the collection. it says my collection is undefined. I also tried defining the collection in bindingSources but that also didn't work, because it does not have the right context.

maxpoulin64 commented 10 years ago

How does your bindingSources look like? How is your model structured? You may need to use $collectionSource instead of just collectionSource (the $ prefix means the source itself).

Example:

bindingSources: {
    myOptions: [....] // < Make sure [....] is either a Collection or an Array
},

bindings: {
    'select.my-select': 'options:$myOptions'
}

This is what I uses and it works all the time. I had many issues when using a collection inside a model, so I recommend to always put the collections as standalone binding sources and access them directly with $.

patrick-radius commented 10 years ago

Thanks again, i more or less got it to work using this (the trick was the $):

        bindingSources: {
            categories: function(){
                var map = new FoMenuMap();
                map.fetch();
                return map;
            }
        },
        bindings: {
            '#category':'options:$categories'
        }

but this is less then ideal, because it now calls fetch on every select that needs this binding. Since the collection is rather large, i'd really like to be able to use the repository pattern i had before with the this.App.request() call.

Also, i think it would really really be helpfull in some ways to be able to define labelPath and valuePath in the specific binding.

maxpoulin64 commented 10 years ago

Is there anything preventing you from creating and fetching your map and then just pass the resulting collection to Epoxy? Like this:

// Somewhere before your .extend()
var MAP = new FoMenuMap(); MAP.fetch();

// ....
    bindingSources: {
        categories: MAP
    },
    bindings: {
        '#category':'options:$categories'
    }

That way you only ever have one instance of the collection, it's already fetched and everything. Don't worry about it being loaded or not, all Epoxy views bound to it will instantly refresh as soon as it will be done fetching.

Also, i think it would really really be helpfull in some ways to be able to define labelPath and valuePath in the specific binding.

Totally agreed, but I'm not sure if it can be implemented in the current state. The way optionsDefault and optionEmpty work is already a bit hackish (they are hardcoded exceptions to the regular handlers map). I have no idea if the author is planning on making that possible or not. Maybe there's a way to change options: to accept an hash instead so we can so stuff like options:{collection:$myOptions,label:'myNameField',value:'id'} like events: does.

patrick-radius commented 10 years ago

never mind, i found out i can just do this:

        bindingSources: function() {
            return {
                categories: this.App.request('collection', 'fomenumap')
            };
        },
        bindings: {
              '#category':'options:$categories'
        }

so the $ prefix and the fact that bindingSources can be a function itself, did the trick for me.

patrick-radius commented 10 years ago

@maxpoulin64 yes {options:$myOptions,label:'myNameField',value:'id'} this would be awesome.