Ember flexible, dynamic search - best practice?


#1

I would like to add a dynamic search form functionality to my ember app. What is best practice? It seems to me that using request parameters is not a recommended approach, but then how?

I would imagine I could do something like the following in my routes?

posts/search/:search_term

But what if the search is made up of a collection of drop-down selections or similar input controls (like a form) or not just a text search? For such a search, one or more of these search filters may be active for a given search operation. LEt’s say I have 1-3 search filters. Would this be the way to go?

posts/search/:search1/:search2/:search3

Or if I have only 2 filters active?

posts/search/:search1/:search2

I assume that the first matching route is executed, so I could do:

posts/search/:search1/
posts/search/:search1/:search2
posts/search/:search1/:search2/:search3

But this still seems like repitition and anti-pattern.

Would I simply have a posts/search route and then manually extract the request params from the request in my router (handler)? Does this even work with the /#/ hash routing?

Thanks.


#2

I’d say the restful norm would be http://example.com/posts?term1=xyz&term2=pdq. http://example.com/posts/search/term1=xyz;term2=pdq should work according to HTTP spec.

Thank you for bringing this up – it only just dawned on me that I haven’t seen any mention of this in Ember docs (for the router) or the little I’ve seen of the extant data adapters (for the service calls). Hmm. Kind of a big gap in support for restful pages and services.


Best practice: Dynamic Parameter for find()
#3

Yeah, that is the “naïve” approach that we would use in a traditional Model2 MVC app, such as Rails or Spring, but… not sure how it would work with Ember, since it uses the “anchor” part of the url (after the # to resolve a path). I guess I can use window.location.hash, but I wonder if the Router simply discards whatever part of the URL remains after it has matched a route? I can find no mention of how to access the url params, only the “params” which as I understand it only refer to the dynamic part of a Route path.

/#/posts/search/:search

So a query of the form “Copenhagen 2 km radius” could be decoded to form a valid Ember route:

/#/posts/search/copenhagen%202%20km%20radius

Or using underscore as spaces

/#/posts/search/copenhagen_2_km_radius_with_3_rooms

Then I could use RegExp to extract the search terms from the :search param

var radius = params.search.match(/(\d+)\s*\km\s+radius/i)
var location = ??

I guess this approach could be made to work?


#4

I didn’t realize the question had anything to do with server technology. It it really related?

What I was noticing was that the router and the data adapters speak in hierarchies and not boolean parameters.


#5

This question has nothing to do with the server.

You can extract the URL options this way:

var options = decodeURIComponent(window.location.search.slice(1))
                      .split('&')
                      .reduce(function _reduce (/*Object*/ a, /*String*/ b) {
                        b = b.split('=');
                        a[b[0]] = b[1];
                        return a;
                      }, {});

JS reduce can be used just fine with modernizer.js.

However, it looks like Ember normalizes the url into the form /x/y/z and discards anything after ? Otherwise #/posts/1?a=7 would result in a lookup posts: {post_id: ‘1?a=7’}. It doesn’t look like the discarded options and made accessible from within the route or router. Not sure if this is intentional by design or if it’s a good or bad thing. It looks to me that going the URL options route is a “no go” at this point. Perhaps in the future, the ApplicationRoute could me made to extract the options before normalizing the URL path?


#6

Yeah, I remember I saw kind of the same question on stackoverflow. The solution that I read there, was to put the query string before the #, so you can access it later.

Take a look here https://github.com/emberjs/ember.js/issues/1773


#7

This is an incredibly helpful forum post, thank you all for bringing up the discussion!

I’m new to Ember, and am still struggling with routing…I’m trying to do something similar to this:

App.Router.map(function() {
  this.route('search', {path: '/search/*query'});
  this.route('incidents', {path: '/incidents/*query'});
});

App.SearchRoute = Ember.Route.extend({
  model: function(params) {
    // using jquery-bbq-deparam
    return $.deparam((params && params.query) || '');
  },
  setupController: function(controller, model) {
    controller.set('model', model);
  }
});

App.IncidentsRoute = Ember.Route.extend({
  model: function(params) {
    var filter = $.deparam(params.query);
    return Ember.RSVP.Promise(function(resolve, reject) {
      // use filter object to construct AJAX request...model comes from REST service
    });
  },
  setupController: function(controller, model) {
    controller.set('model', model);
  }
});

Here are my questions:

  1. Is there some way I can update the url just before the transition from #/search to #/incidents/fromDate=yyyy-mm-dd;toDate=yyyy-mm-dd so when the user hits the back button, the search form can be restored to its last state?
  2. Should I not be using a model in my search controller to store current form values? I know it’s “application state” and not “persisted state,” but I want the user to be able to modify their query without starting from scratch…so, doesn’t this have to be held in the URL?
  3. Am I just completely missing the point here?! LOL :stuck_out_tongue:

Thanks!