Rendering the same model to multiple outlets


#1

Hi,

I may be going about this wrong but I’m building a map application that lets the user plan a trip on various levels of granularity. How does one go about rendering the same model results to multiple outlets on ember 3. Is that even the way to do it anymore? All the questions I can find related to this are from 2014 and before.

I am using ember-leaflet and want the maps on one outlet and the details in a sidebar outlet like the attached diagram.

As it currently stands I have something like this as my templates:

Trips (hasMany) Rides (hasMany) Waypoints

Trips route

{{#link-to 'trips'}}Trips{{/link-to}}

{{#leaflet-map lat=lat lng=lng zoom=zoom class='map-div'}}
  {{tile-layer url="https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png"}}
  {{outlet}} // note outlet here
{{/leaflet-map}}

Ride route - renders in above outlet

{{#each model.waypoints as |waypoint|}}
  {{#marker-layer location=waypoint.lnglat draggable=true icon=waypointIcon onDragend=(action 'updateLocation' waypoint)}}
    {{#popup-layer}}
      {{waypoint.name}}
    {{/popup-layer}}
  {{/marker-layer}}
{{/each}}

But I have no idea how to render a text representation of the model data into the sidebar also. Is this even the correct way to do it? Pushing data to application level template?

The sidebar contents will be different each time but the map contents will be cascading based on the level the user is editing at Trips (shows all trips start markers) > Trip (Shows individual trip rides start markers) > Ride (Shows individual ride waypoint markers).

Thanks in advance for any help, i hope I’ve explained myself reasonably well.


#2

You can still render into multiple outlets to get what you’re trying to do working. I have a reporting dashboard that does a very similar thing to what you’re explaining. I’ve a route with a model that renders to two different outlets, one a sidebar and one as the main body.

Your “trips” route can render into your outlet and sidebar so something like this inside your Route:

renderTemplate(/* controller, model */) {
     // Render to your regular, unnamed {{outlet}}
     this.render(); 
    
     // Render the template named "trips.sidebar" to the outlet {{outlet "sidebar"}}
     // As a bonus this outlet doesn't even need to exist in your trips template. It can exist
     // in a route higher up your route's hierarchy if it suites your layout. It's what I did.
     this.render('trips.sidebar', {
         outlet: 'sidebar
     });
}

This will render into both outlets with the same controller decorated with the same model. You are able to give a different controller as an option to the has given to the render method if you wish. Check out the API Ref on renderTemplate.

Cheers :slight_smile:


#3

If you had the following hierarchy:

-- trips
   |-- index    /trips
   \-- details  /trips/:id 

Then I would say have the findAll('trip') in the trips route and have trips.hbs render the map then trips.index shows a please select a trip to see details here message and in trips.details use find('trip', id).

But I don’t think this is what you are looking for. A legacy solution is to use named outlets as @bmurphy explained. I think that if your app not very flexible in its design you might have to go that route.

The alternative is to use components for each and wrap them in {{#if ...}} blocks which are driven by what model is chosen (maybe by queryParams). Possibly having the sidebar component use the find(). Possibly using ember-concurrency to offer loading state and error handling.

I think I would invest a great deal of effort attempting to accomplish this with components before I conceded to named outlets. Because the former has much better situation for long term maintenance and cognitive reasoning.


#4

Many thanks for the solutions, peeps.

@bmurphy i had just started reading about renderTemplate when i posted this message. I didn’t quite get to how it worked with the outlets being in application.hbs though. I found a few references on stack overflow suggesting to go with components instead so I thought I’d ask.

Thanks for the input, both.


#5

Load the model in the parent route and then set it on a service. Use that service wherever you need that model.


#6

But ember-data already offers you a service. You don’t need to keep a reference to a model in a service when you can easilly find(‘model’, id) in any component you wish.


#7

True, plus there is peekRecord.