Using a default template


#1

I am using the {{ partial }} helper to select a template according to a field in my model.

By design, if the partial does not exist, nothing is shown.

How can I use a default template when the specified template does not exist?


#2

In general, partial usage should be avoided, and you should almost certainly refactor this to {{component usage instead of {{partial}}.

Regardless of that (even with {{component you may want to display a default when a custom component didn’t exist), you should be able to detect a template being present by using the public owner APIs. A helper that does owner.hasRegistration('template:some-name-here') should largely do the trick…


#3

Do you want to show a default template if the field in the model is empty? Or are there cases where it may contain a name that is not a valid component name?

The approach @rwjblue described should work, but I would be nervous about arbitrary data from models being used to invoke components, as it could become an attack vector for exploits. If the data is well-bounded, I would validate it on the JavaScript side, and use null to signal to the template to show a default template:

{{#if dynamicComponent}}
  {{component dynamicComponent}}
{{else}}
  {{default-component}}
{{/if}}

#4

For a premade is-component helper see https://emberobserver.com/addons/ember-cli-is-component


#5

FWIW, you would have a similar issue with partials where arbitrary user supplied data could force the system to use a template that would not have otherwise been used, and possibly expose more data than you intend.

In most circumstances where I have seen this sort of thing though, the specific types are not user supplied and instead are guaranteed by the API. This helps mitigate some of the security risks associated with that sort of dynamism…


#6

Right, exactly. So ideally you are never in a position where a string in the data maps to a component that doesn’t exist, and you can use a simple conditional like in the example above. Would need to understand more about the use case @hectorsq has in mind to know if this would work. But either way, components are preferable to partials in all cases.


#7

@rwjblue it worked great!

@rwjblue, @tomdale and @ef4 thank you very much for your comments.

Warning: this is my first Ember project (migrating from AngularJS) and my English is a little poor.

Here is some context: I am developing an electronic medical record (EMR) which is compound by multiple documents. Some of the documents are simple, like a progress note with less than 10 fields and some are complex like a medical history with about 120 fields. Each document has several groups and each group has fields. Fields in each document can be integer fields, string fields, select fields, etc.

When a document is loaded in the Ember client I select the required template and show the document’s fields in a nice formatted screen. Here is where I dynamically select the template using {{partial}}. I was originally using components but more on this later.

Writing the template for each document is a time consuming task. So as an aid in the document definition phase I have written a generic template that shows all the fields one after another. This allows me to create a proof of concept and validate it with the doctors.

The only thing I need is to use this generic template if there is not an specific template already developed.

Let’s say that I am developing a surgery note. I create the document definition file in the server, load it in the client, and since there is not yet an specific template for this surgery note (surgery-note.hbs) I will automatically use the generic template (generic-note.hbs). Later I will develop the specific template and use it.

Now let’s talk about why I am using partials. I began using components, so a document was a component and each field was a component too. Everything was working fine until I had to add the following feature. In a document, the doctor has to add multiple medicaments to a list. There are hundreds of medicaments so I needed to perform a search. This is where I got stuck. I needed to perform a search inside a component that was inside another component, but components have no access to the store. After a lot of thinking I decided to simplify a little and use templates for the documents instead of components, this allowed me to create a new nested route for searching the medicaments and show the appropriate template in a named outlet in the document’s body.

As I am writing this I am thinking about another option. I currently have a document route used to show any document, I select the required template according to the document type. I am wondering if it would be better to have a route for each document type and eliminating the need to select the template. I’ll have to check if the route already exists for an specific document type or use a default route.

I hope this message explains the context better. I’ll appreciate any comments.

Thanks again.


#8

They do! This should help:

import { inject } from '@ember/service';
export default Component.extend({
  // now you can do things like this.get('store').findRecord(...)
  store: inject()
})

Your project sounds interesting. I built an Ember-based EMR once too, and HospitalRun is an open source hospital EMR for the developing world written in Ember.


#9

Thank you @ef4 I’ll give it a try.