Desing Fullscreen Search Interface With Ember - Part 2
In the first part we prepared a routable modal for the search interface.
Now it’s time to allow a user to interact with that modal and query backend.
Add search form component
1 | $ ember generate component search-form |
app/components/search-form.js
1 | import Ember from 'ember'; |
app/templates/components/search-form.hbs
1 | <h1>Hello!</h1> |
app/templates/search.hbs
1 | {{#full-screen-modal-dialog}} |
tests/integration/components/search-box-test.js
1 | import { test, moduleForComponent } from 'ember-qunit'; |
Now it’s time to make this component able to act.
We add an acceptance test first as it’s very easy to describe what we want to achieve.
tests/acceptance/search-test.js
1 | test('search by query', function(assert) { |
app/components/search-box.js
1 | import Ember from 'ember'; |
app/controllers/search.js
1 | import Ember from 'ember' |
And finally:
app/templates/components/search-box.hbs
1 | <h1>Hello!</h1> |
app/templates/search.hbs
1 | {{#modal-dialog}} |
On the end action can be tested in component integration test this way:
tests/integration/components/search-box-test.coffee
1 | test('when clicks on submit', function(assert) { |
So now we have a component that can communicate and pass a query to the router. But there is one bummer! When user types, URL changes instantly. We don’t want to send a query to the backend on each key down event though, so let’s fix it:
Let’s rename the component property, and thanks to this propagate property changes only after submitting our form:
tests/acceptance/search-test.js
1 | test('search by query do not modify url without submit', function(assert) { |
app/components/search-box.js
1 | export default Ember.Component.extend({ |
app/templates/components/search-box.hbs
1 | <form class="form-horizontal" {{action "updateParams" on="submit"}}> |
Add querying mechanism
That part is pretty simple, it’s just passing data down to the component from model hook:
app/routes/search.js
1 | import Ember from 'ember'; |
I will skip the rest of the code, now let’s focus on:
Mocking backend
I know two reasonable approaches. Okay, technically there are three:
- hard-coded array of JavaScript objects in route`s model hook
- http-mock https://ember-cli.com/user-guide/#mocks-and-fixtures (check this out too: https://www.npmjs.com/package/ember-cli-testem-http-mocks)
- using client-side mock server ember-cli-mirage (http://www.ember-cli-mirage.com/)
As the last one have a great opinion in the community we will choose it.
By the way, it could be used both for development and tests by default, while http-mock requires an additional addon to make it work in the test environment.
Our updated tests/acceptance/search-test.js
1 | test('queries notes by title', function(assert) { |
Now time to make the test pass!
Mirage provides powerful stuff, for example, database methods known from Rails world, for example:http://www.ember-cli-mirage.com/docs/v0.3.x/database/#where
It will be really useful in our search feature!
By the way, I just found that it could be used to do super spicy things, check
https://github.com/samselikoff/ember-cli-mirage/pull/379 for details.
Let’s dive into code:
mirage/factories/note.js
1 | import { Factory } from 'ember-cli-mirage'; |
mirage/config.js
1 | export default function() { |
Improvements
Speed up rendering the modal
There is also one thing that is not so good. Fetching all data in the model hook blocks the transition. So if there are no Notes in Ember store yet, it could take some time to show a modal filled with data. We want to show a modal smoothly.
This video (https://www.youtube.com/watch?v=kPxiiAGMSzE) from EmberConf 2017 was an inspiration on how to solve this problem elegantly.
Check this out:
app/routes/search.js
1 | import Ember from 'ember'; |
The thing is that we schedule fetching the notes after rendering a template:
(BTW loading text could be replaced with any nice spinner instead)
As you can see even if a long list of notes is fetched, a modal dialog is opened immediately. That was not possible with the previous solution.
Add close button to back to home page
tests/acceptance/search-test.js
1 | test('close search modal', function(assert) { |
app/templates/search.hbs
1 | {{#full-screen-modal-dialog}} |
Simple, right?
Thanks for reading!