0
votes

I have a "books" page that initially displays a list of books. I want users to be able to enter search text and then change the list of books that display. Doing this based on books already in embers data store (client side) is straight forward but what I want to do is perform the search server side. How can I do this?

So far I have the following in my ember controller

App.BooksIndexController = Ember.ArrayController.extend({
  search: '',
  actions: {
    query: function() {
      var search = this.get('search');
      var books = this.store.find('books', { search_text: search });
    }
  }
})

But I don't know what to do next. How do I transition to the books list route with the books passed in? Or is there a more ember-ish way to do it, like creating a "search" route/controller/template and displaying the results on that page?

1
It can be done using query parameters. Look here: emberjs.com/guides/routing/query-params and one of the examples (that you can find on the bottom of the page; it's server-side sorting, but it's similar): jsbin.com/hiyalu - andrusieczko
@andrusieczko it sounds like I'm going about it in a non-ember way and that's why it's hard. I'll try adding the query param to the books list page and do the search there if it's present. - Dty
Good luck! ask if you need help with that - andrusieczko

1 Answers

0
votes

I just implemented this in my own app and it can be a bit tricky. First, you'll want to add an input field into your app for users to use to search and bind it to a property on your controller:

{{input value=search classNames='form-control' placeholder="Search books"}}

As you see above, it's bound to the search property of your controller and I've given it the default Bootstrap class form-control

Now, let's make a new computed property in your controller called books:

App.BooksIndexController = Ember.ArrayController.extend({
    ...
    books: function() {
        var model = this.get('model');
        var search = this.get('search');
        var current = 1;
        var books = model.filter(function(item) {
            var reg = '.*'+search.toLowerCase()+'.*';
            var name = item.get('name').toLowerCase();
            if(name.match(reg) && current < 20) { 
                current++;
                return true; 
            }
        });
        return books;
    }.property('search','model.length')
    ...
});

What this does it get all your items for you model and checks to see if the name property matches the name of any of the books in the model. You can change which property it matches or add new ones as you like. I don't like all the matched items being returned at once, so I add a limit with current so that only the first 20 matches are returned. You can remove this if you like. Doing it this way means you'll automatically display items that match that's already in the store.

Now, update your template so it iterates over books instead of model or some such stuff:

{{#each book in books}}
   // do stuff with each book
{{/each}}

This is great you say! Now you're filtering all your books as you type because the books property will be updated whenever search is modified, but now you need to fetch new items from your API. We do this with another computer property:

searchApi:function() {
    var search = this.get('search');
    var books = this.store.find('books',{search_text: search});
    var model = this.get('model');
    books.then(function() {           //  In my tests, this isn't
        model.addObjects(books);      //  actually necessary, bit I still
    });                               //  like to keep it for clearness
}.observes('search').on('init')

This will trigger a search of your API whenever the search field is modified as well. Any matched items are then added to the model. That's why we added model.length to the books computed property. Any matched items will be added to the model and increase its length, triggering books to be updated again.

That should be everything you need. No new routes or controllers, just do it right in the index. If anyone has a cleaner approach to this, I'd love to see it!