Ember Octane filtering using queryParams

Hi there! I am recently migrating to Ember Octane and right now I’m trying to figure out how to filter through a model that is passed down to components since there is no two-way binding for component arguments anymore.

Using previous Ember versions I used to do the following:

//app/routes/posts.js

export default Route.extend({
     model(){
         return this.store.findAll('post');
     },

    actions: {
     filterPosts(postCategory){
       this.store.query('post', {category: postCategory}).then((posts) => {
            this.controllerFor('posts.index').set('model', posts);
        });
      }
    }
})
//app/templates/posts.hbs
<PostsTable @posts={{model}} filterPosts={{route-action "filterPosts"}}/>

I used the ember-route-action-helper to get this to work. And from within my component (posts-table) I just fired the filterPosts to filter as I needed.

Now with Ember Octane I now (and I tried :cold_sweat:) to bind the property that is passed through the component, but it doesn’t work. So I started to try with queryParams which I think is a much better way to do this, but I couldn’t reload the model that is passed down to my component and I have no clue how to do this. Right now I have something like this:

//app/routes/posts.js
import Route from '@ember/routing/route';
import {
  inject as service
} from '@ember/service';

export default class PostsRoute extends Route {
  @service store;

  queryParams = {
    category: {
      refreshModel: true
    }
  }

  async model(params) {
    return this.store.query('posts');
  }
}

//app/controllers/posts.js
import {
  action
} from "@ember/object";


export default class AppProvasIndexController extends Controller {
  queryParams = ['category'];
  category = null;

@action
  async filter() {
    this.model = await this.store.query('posts', {category: 'post-category-to-filter'});
    }
}
//app/templates/posts.hbs
<Posts::PostsList @posts={{@model}} />

When I fire the filter action it brings what I need and it sets the data do this.model but it won’t reload the model that is passed to my component. So how can I achieve this behavior using queryParams? I need to refresh the model that is passed to my component.

I also have tried this:

//app/controllers/posts.js
import {
  action
} from "@ember/object";


export default class AppProvasIndexController extends Controller {
  queryParams = ['category'];
  category = null;

@action
  filter() {
    this.transitionToRoute('posts.index', { queryParams:  {category: 'post-category-to-filter'}});
    }
}

But it won’t fire the transitionToRoute and I don’t have any error logs too.

Is that the right approach or would you suggest a better way to do this?

The use case is: I have a non-filtered model that is fetched from the store and passed down to a component. I need to filter this model with some params that the user will choose and update the data that is passed down to the component.

I need to refresh the model because I also have paginated data so It would be right to filter just the data that I already have fetched from the store.

I am a little bit lost here and would appreciate your help and suggestions. Thank you so much!

Hi @kamillacrozara, I think typically the best way to do this is to use query params in the route and filter the original request whenever the query params changes. Is there any particular reason you want the model to be non-filtered? Typically you’d also probably want to bind pagination params as query params and refresh the model when those change too, but it depends on your situation I guess.

Anyway, unless you really need the unfiltered model somewhere I would skip the controller stuff and just do this in the route:

//app/routes/posts.js
import Route from '@ember/routing/route';
import {
  inject as service
} from '@ember/service';

export default class PostsRoute extends Route {
  @service store;

  queryParams = {
    category: {
      refreshModel: true
    }
  }

  async model(params) {
    return this.store.query('posts', params);
  }
}
1 Like

Hey there! Thank you for your answer! Sorry for the late response, I was working on other features.

I need the controller because I have 5 different filters to apply. I’m setting all the filters that I need on the controller and then I do something like this:

this.model = await this.store.query('prova', { 
          'supercargo': this.supercargo ? this.supercargo.supercargo : '',
          'cargo': this.cargo ? this.cargo.cargo : '',
          'banca': this.banca ? this.banca.nome : '',
          'estado': this.estado ? this.estado.nome : '',
          'dataInicial': this.dataInicial,
          'dataFinal': this.dataFinal,
    });

I was expecting that the filtered model would be set correctly using this.model on the controller but it is not working. I need the unfiltered model because at the same route first I’m showing an unfiltered list. After the user set the filters and click on “Filter” then I’m trying to fetch the filtered model and show to the user.

Ideally this would still be done in the route. It’s not a completely hard and fast rule but in general you should not fetch data in a controller. And since you’re essentially re-fetching the same model (but with filters) it would make sense to bind these to query params and allow them to refetch the model when they change. At a high level that would look like this:

  • set up query params on the route for each filter, with refreshRoute: true so when the query params change it will refetch the model via the model hook.
  • set up the query params on the controller so you can bind to them with your UI controls
  • the action that handles clicking the “filter” button can then just set the filter values on the controller, this will trigger a route refresh, which will run your model hook again with your filters

Another reason it’s good to use query params for your filters is that the filters are persistent in the URL, allowing a user to copy/paste URLs to the same page and persist their previous filters.

I would recommend this section from the guides: configuring query params to refresh your model hooks