Implementing a master/detail responsive layout similar to many native apps


#1

Hi there,

I’m trying to build an Ember app that works well at many different screen sizes. The layout I’m trying to create is a master-detail layout that is very common on iOS and Android. I’d also like the layout to be adaptive / responsive in the way that most of these apps are.

I made some sketches to illustrate what I’m talking about. First, consider a larger screen such as a tablet or desktop. Because there’s so much horizontal space, it’s common to show a list of items along the left side of the screen:

Clicking on one of the items on a larger screen would render the details about that item into the main content panel:

So far, so good. That’s trivial to build as a nested pair of routes & templates in Ember. In the example here, that would be a tasks route and a tasks.show route.

Now, consider how a master-detail list usually works on a small device like a phone. You’ve typically got a screen with your list of items like this:

Clicking on one of the items takes you to the individual item’s route and hides the list:

Again, this is pretty straightforward to do on its own with Ember if it was the only view I was trying to build. I’d have a tasks.index route and a tasks.show route and only ever see one of their templates at a time.

Where this breaks down for me is when considering how to build a single app that can adapt to either scenario. This doesn’t seem like an issue of just using media queries, as the content is not just hiding or showing based on screen size but also based on which route we’re on. For example, on the tasks.show route we want to hide the list of tasks but only if we’re also on a smaller-width device.

It’s also important to point out that the list of items would need to be able to maintain its scroll position as the user goes back and forth between the list and individual tasks. So I think this means the list DOM can’t be torn down.

So far I’ve come up with two different ways to solve this. I’ve implemented both and they both “work” at a basic level:

  1. Use an addon or service such as ember-body-class to append a route-specific CSS class to the main body (i.e. .tasks-index and .tasks-show). I can then use CSS media queries to show or hide specific panes depending on the route name.

  2. Use a wrapper component for the panes that I want to show or hide on specific routes. This component takes an argument with the name of a route. When that route is the current one, the component can apply a class to itself or set its isVisible value. I can either put media queries into that component using something like ember-responsive or again just fall back to pure CSS media queries based on the class that’s applied.

{{#route-class-name route='tasks.index' addClass='currentRoute'}}
  {{task-list}}
{{/route-class-name}}

Option 2 feels a bit weird because I’m adding an extra level of markup (a div) for the wrapper component. It also requires me to add an initializer that injects the applicationController into the wrapper component so it can get the currentRouteName. Both options depend on detecting the current route, which may be unavoidable but AFAIK is not considered a good practice.

Is there a better, more idiomatic way to achieve this kind of thing in Ember? It’s a responsive layout that is so common on iOS but is a bit different from how this sort of thing is typically done in web apps (as far as I’ve seen).

I’d especially like to make sure that whatever approach I choose lets me easily get nice transition animations (i.e. with liquid-fire). AFAICT the mobile transition between the list and detail views may not be easy to transition because of how they’re nested (and how the list isn’t going away, just hiding). But maybe I’m missing something there too!

Any insights would be appreciated :slight_smile:


#2

The only insights I can currently offer is that I also run into these challenges ALL THE TIME. I’ve dedicated so much of my time to making things responsive, and now much of my time to getting into Ember, but I’ve yet to find a path that lets me combine the routing concept and mobile-first concepts.

I’ve been experimenting with a service that will pay attention to view-port size and can let your routes and components lead to different states - but certainly no where near a solid reusable approach has emerged.


#3

Have you taken a look at flexi? It seems to try and solve this https://github.com/runspired/flexi


#4

This article from @runspired really breaks things down nicely. http://blog.isleofcode.com/modern-responsive-design - generally skeptical about anything with x="3" but I’m exited to spend some time with this. : )


#5

Thanks @jasonmit! I have been looking at Flexi and I’m excited about the direction it’s going. I did run into some fairly large bugs with it early on (here and here), but those should be solvable.

I agree with @sheriffderek that the blog post introducing Flexi does an excellent job breaking down the problems with this sort of thing. Flexi might be just the thing to give Ember a lead in this space, just as ember-concurrency does for the problems it solves.