0

Ember routing works nicely when working with strict linear paths of resources. However, it's becoming more prevalent in mobile design for apps — such as the Facebook app — to use infinite stacks of multiple interconnected resources:

Example Facebook infinite stack

  1. User starts in the feed.
  2. Presses on a link to a user profile.
  3. Navigates to user's list of friends.
  4. Visits a new user profile.
  5. Goes to any other types of resources such as posts, groups etc.
  6. THEN can navigate all the way back with each page state persisted.

We start off in a known resource - let's say it's Facebook's news feed. In Ember, we'd define that route as:

this.route('feed');

But from there, we'd like to be able to visit any combination of our resources - whilst still maintaining the state of each route. The temptation is to solve the problem through some funky route solution, such as using catch-all route definitions:

{ path: '*' }

But that'd take some heavy path management as discussed here (or perhaps there's some method of utilising Tilde's router.js URL generation?!). But as per the illustrated example above, it would leave us with huge goddamn route paths:

/feed/users/:user_id/friends/users/:user_id/another_resource/:another_resource_id

Not that that's a problem in a mobile app, but I'm not sure if it's the optimal way of achieving this.

So that leads me to consider whether there's some method of dynamically creating outlets as stacks get deeper (akin to modals) - or whether the only way to achieve state persistence is using an app level object, Ember data or an Ember service to track/persist history & state.

Anyway, tl;dr, I'm not desperately needing this functionality - just interested if anyone has a smart insight into how achieve this ...umm ... infinite interconnected nested resource stack thingy.

Community
  • 1
  • 1
Will Viles
  • 281
  • 2
  • 11

1 Answers1

2

The answer does not lie in nesting routes in an attempt to prevent them from being torn down.

Instead the answer lies in state management.

Browser history can be used to manage URLs and bound to the back buttons on each page in our stack. However, restoring the exact state of the page (including scroll position, especially when models may be lazy loaded) requires some additional design.

The easiest method of doing it is using the ember-state-services addon.

In particular, this video by Travis Hoover from Oct '15 was really helpful. It explains how ember-state-services creates 'buckets' for different model instances.

So, when navigating through our Facebook stack, the state of each page can easily be stored & restored even if we visit pages which reference the same route/controller. In our example, it helps with preserving the state of the two user profiles we navigate to (user/:user_id).

So, for storing each user profile pages' scroll positions, get the scroll offset from your scrolling div/component and use ember-state-services as so:

// app/controllers/feed
scrollPos: stateFor('scrollPos', 'model')

// app/routes/feed
saveScrollPos: Ember.on('deactivate', () =>
  this.set('scrollPos', scrollValue);                                     
));

It'll store your last scroll positions on users/1 AND users/2 separately because state is bound to the particular user models.

The only gotcha I can foresee is if the user was to visit the exact same route multiple times in one stack, but there's not many use cases where that would be a problem.

Will Viles
  • 281
  • 2
  • 11