Preface: The goal of the following is to create a discussion on – or at minimum get people thinking about – the potential downsides of something the Ember community typically praises.
I’ve been using Ember since pre-1.0 before ember-cli was ember-cli. It’s served me extremely well over the years. I have run two successful consultancies with Ember being one of our core differentiators, and worked one full-time position also primarily for my Ember skills. I am deeply grateful to the people who have committed thousands (hundreds of thousands?) of hours creating, maintaining, and improving Ember and its ecosystem.
I’ve benefited from what Yehuda recently described as the ‘togetherness’ of Ember and its community. I agree there is a lot of value in ‘togetherness’, and it is that togetherness that has brought Ember (and me) where we are today.
Recently we’ve been working on the problem of rendering very large tables (50k cells) quickly for printing from the browser. In the process, we have come across a few things I would like to point out as downsides of togetherness.
Requirements of the table:
Render 50k cells in a ‘reasonable’ amount of time
All data must be visible at the same time, no pagination, occlusion / smoke-and-mirrors. If it’s not visible, it won’t print.
Because it’s for print, it does not need interactivity.
We first researched within the Ember community Discord/Discourse archives for similar questions. There are a few questions out there on this, and they all have essentially the same response: Don’t render large tables. While I personally agree with this solution, it does not change the business requirements I’m faced with.
Then we tried a few approaches:
Use pure function helpers for prepping cell data for display
Use a custom component manager to manage custom cell components
Cache translations and values aggressively
Cache internationalized values aggressively
Manually concat document fragments, then only when finished, attach to the DOM
Of these, only the last one (keep everything out of the DOM) provided any significant improvement in render time. Creating our own virtual DOM offscreen was sufficiently fast, but not without a major downside:
- We can now only render raw strings to each table cell. Specifically we can no longer render Ember components in a table cell.
As far as we understand, glimmer does something similar where it composes document fragments off-screen and only attaches them to the DOM when absolutely necessary. We were specifically looking at htmlbars and glimmer itself.
After we felt we learned everything we could from what was already asked/answered and reading source code, we started posting to Discord. This is really where community togetherness started to show its weaknesses for us in this case. Even with clearly stated questions and explanations of the problem, answers roughly fell into two buckets:
Don’t render large tables. This response was annoying more than anything. It was clear the responder had not taken the time to even read the requirements, but instead drew from the ‘together’ answer.
Vague. Supposedly there are other tools (in-element, ember-table, vertical-collection) that do what we are trying to do, but after reading through their sources, none seem to.
We found reference in a few places to
compileTemplate methods, but when we asked about them, the answer was:
- Render the table in each loops in the handlebars template.
Yeah we get it. Ember is a together framework and that has benefits. We have benefited. But does togetherness mean when someone needs to do something non-standard APIs are either not made public or made public and not documented? Further, does it mean that every response that is given by the community needs to be the ‘together’ answer, even when that answer clearly doesn’t solve the problem?
In order to meet our requirements, we have had to port Ember components over to helpers which we are able to call as raw functions from our table’s Typescript file. It’s not terrible. But it’s frustrating that something so fundamental in other frameworks (
ReactDOM.render()) and seems like it should be – or is – possible in Ember but instead are not only difficult (impossible?) in Ember, but also that the community is so into being together it provides a few canned responses even when the responses don’t fit the situation.
For reference, here is a table using Handlebars
each loops with 50k cells (>25s load time):
And here is one using document fragments (>13s load time):
While the document fragment approach isn’t blazing fast, it is twice as fast as Ember/Handlebars.
Thank you for reading. Please reply if you have any thoughts on how we can/should approach community togetherness differently when trying to solve problems that fall off the beaten path.