Search code examples
javascriptember.jsember-cliobservable

How to filter down dropdown results based on selection from another dropdown?


I would like to filter items by state and/or county. I am able to accomplish this, but what I would like to add is the functionality of only displaying de counties that are in relation with the chose state, if no state is chosen then show all available counties. I'm pretty sure I must use an observable but cannot think of a way of doing so(beginning to learn about observables). If there is a better way of achieving this without the use of an observable attribute, please explain how.

Example:

If Florida is selected, then only the counties Miami and Orlando should be in the counties dropdown.

This is what I have at the moment:

JavaScript:

App = Ember.Application.create();

App.Router.map(function() {
  // put your routes here
});

App.IndexRoute = Ember.Route.extend({
  model: function() {
    return Ember.A(

    [
      Ember.Object.create({ name: "John", county: "Miami", state: "Florida" }),
      Ember.Object.create({ name: "Sam", county: "Orlando", state: "Florida" }),
      Ember.Object.create({ name: "Tim", county: "Los Angeles", state: "California" }),
      Ember.Object.create({ name: "Liam", county: "San Francisco", state: "California" })
    ]);
  }
});

App.IndexController = Ember.ArrayController.extend({
  counties: function(){
    return this.get('model').getEach('county').uniq();
  }.property(),

  states: function(){
    return this.get('model').getEach('state').uniq();
  }.property(),



  filtered: function(){
    var country = this.get('country');
    var state = this.get('state');

    var model = this.get('model');
    if(country ){
      model = model.filterBy('country', country);
    }
    if(state){
      model = model.filterBy('state', state);
    }

    return model;    
  }.property('country', 'state')
});

HTML:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Ember Starter Kit</title>
  <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.css">
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
  <script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v2.0.0.js"></script>
  <script src="http://builds.emberjs.com/tags/v1.9.1/ember.js"></script>
</head>
<body>

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

  <script type="text/x-handlebars" id="index">
  <h1>Non Filtered</h1>
  <ul>
  {{#each person in model}}
    <li>{{person.name}}({{person.county}}, {{person.state}})    
  </li>
  {{/each}}
  </ul>

  <h1>Filtered</h1>
  Counties: {{view "select" content=counties value=county prompt="Pick one..."}}
  States: {{view "select" prompt="Pick one..." content=states value=state}}

  <ul>
  {{#each person in filtered}}
    <li>{{person.name}}({{person.county}}, {{person.state}})    
  </li>
  {{/each}}
  </ul>

  </script>
</body>
</html>

JSBin

Thanks in advance!


Solution

  • You will need to create a state to county mapping (to tell it which county maps to which state). Then, you can use the mapping to filter which counties you display for your counties dropdown.

    stateToCountyMapping: [
      Ember.Object.create({ state: 'Florida',
                        counties: ['Miami', 'Orlando']}),
      Ember.Object.create({ state: 'California',
                        counties: ['Los Angeles', 'San Francisco']})
    
    ],
    counties: function(){
      var counties = this.get('model').getEach('county').uniq();
      var state = this.get('state');
    
      if(state){
        var mapping = this.get('stateToCountyMapping').findBy('state', state);
        counties = counties.filter(function(county){
          return mapping.get('counties').contains(county);  
        })
      }
    
      return counties;
    }.property('state')
    

    Working demo here

    UPDATE

    If you don't want to add another property for mapping, you can do the following:

    counties: function(){
      var counties = this.get('model').getEach('county').uniq();
      var state = this.get('state');
    
      if(state){
        counties = this.get('model').filter(function(record){
          return record.get('state') === state;  
        }).mapBy('county').uniq();
      }
    
      return counties;
    }.property('model', 'state'),
    

    Basically, filter each record in the model based on the chosen state, then only take the counties and then make sure that they are unique.

    Working demo here