Detecting Ember.js Components Entering or Leaving the Viewport

Koushik Radhakrishnan
5 min readJul 26, 2020

--

This is my first blog in Ember.js. In this blog, I would like to detail how we reduced our app route’s initial API requests and boost our application’s performance.

The prelude to numerous network calls:

Our application’s first landing page is a Dashboard screen that contains multiple widgets. This route fetches a batch of datum using the Promise.all() method which sends all the API requests in parallel (based on the browser’s available maximum number of concurrent HTTP requests). This datum is subsequently passed on to those widgets on the dashboard route to show up in the UI. In general, the suggested practice to load data when you get started into the Ember.js world is that you make API requests in the route’s model() hook to fetch data.

For demonstration purpose and ease of understanding, I have created a demo project simulating our dashboard functionality with 10 widgets in it. The dashboard route’s model hook looks like below:

Dashboard route’s model hook.

Running the app, and navigating to the dashboard route yields the network stats like below:

Network tab while making requests on route’s beforeModel() or model().
Network tab while making requests on route’s beforeModel() or model().
Quick snapshot of above requests:No.of request: 16No.of request's for dashboard: 10 (1 to 10 from above example)Finish: 1.54sDOMContentLoaded: 881msLoad: 1.90s

From the above investigations of the network tab, the DOMContentLoaded is 881ms and the time API requests take to finish is 1.54 seconds. The finish is calculated as the max timestamp of all captured requests minus start Timestamp. This metric clearly explains what happens when we fetch all the requests in the dashboard route’s model() hook. The UI also didn’t seem interactive and felt less responsive due to the unfinished API requests. To provide an improved UX for end-users, let’s see how we can fix it.

Ideas to improve UX:

As we saw in the previous section, the reason for this performance degradation is due to the render-blocking requests made at the route’s model() hook. It isn’t a mistake, but for a batch of requests, it may not be the optimal solution.

One way to solve this is to move our bulk of requests from the route to its controller or the individual components. But I’d chosen to do them in individual component, and as we make progress in this blog post, you will understand why I suggest that.

Note: You can reuse the same component if your widgets are similar.

On moving the API requests to individual components, we would be able to show the widget’s UI — that have resolved their data — instead of waiting for all the requests to be completed, as we saw in the route’s model() hook approach. This will improve the user’s perceived interactive behaviour in the UI and appear to be faster than expected.

Below is the network tab showcase, post the movement of API requests from the route’s model hook to the components:

Network tab while making requests at the component level.
Network tab while making requests at the component level.
Quick snapshot of above request:No.of request: 16No.of request's for dashboard: 10 (As you see 1 to 10 from above example)Finish: 1.00sDOMContentLoaded: 373ms

Wow ⭐️ ! With this approach, we saved 550ms in loading the DOM content and we saved 0.54seconds completing all requests!

The next question that arises is, “Do we need all widgets to fetch its data at the same time when a user lands on a dashboard page? What if the user’s viewport shows only 5 widgets at a time and not 10?”.

Apparently, the dashboard is the first landing page for our product and the most used one by our customers. The user might land into the dashboard page first and navigate to other parts of the application. In such a scenario, the real question is should we fetch all the information while the user lands on this page and let all the components ask for data? So, how do we solve this?

We need all the data to be shown to the user but at the same time, we shouldn’t make 10 API requests at a time. Thankfully, the EmberJS addon ecosystem has got us all covered through an addon called ember-in-viewport.

This addon is written and maintained by the DockYard. Let’s see what it does? Under the hood, there are listeners to watch for user’s scroll behaviour and trigger events when a component enters or leaves the viewport. So let’s add the ember-in-viewport addon to our demo app to see how that works for us. Thereby, ensuring our components make the API requests when they enter the viewport.

The addon accepts a configuration called viewPortTolerance. This option determines how accurately the Component needs to be within the viewport for it to be considered as entered or leaving. For example, if we pass the viewPortTolerance value as “{ bottom: 200 }”, it means the component’s onEnter event is triggered when the component reaches the bottom of 200px in the viewport.

The addon provides two ways for usage, it can be either used as an Ember service by injecting in your component or as an Ember mixin which can be used in your component. The addon also supports both vertical and horizontal scrolls. Feel free to go through the documentation here about the addon in detail.

Let’s take the approach of injecting it as a service in our demo app. The code snippet looks like below:

Usage of in-viewport as service.

Now let’s see how the demo app’s page and performance look like after we have used the addon.

Page and network requests after the usage of the in-viewport addon
Page and API requests after the usage of the in-viewport addon

Hoorah 🎉 ! As you see from the above GIF, it saves us a lot of request at the initial load. The components that are not in view haven’t asked for API data, unlike the route’s model hook. As we scroll, the API call is made to fetch the required data which has saved us a bunch of network calls and save us from API overload.

That’s all I had to share if you find something interesting or have used ember-in-viewport, feel free to drop a comment :) Cheers :)

If this blog helped you as well, please show your support by giving a clap :)

I would like to thank Abhilash L R for encouraging, guiding me to write my first technical blog on Ember.js.

--

--