Multiple views depending on screen orientation

I know how to show multiple views with multiple controllers in one page, but I have some trouble on changing the states so it fits depending on the orientation.

To put some background in, I’m trying to do a master-detail flow, where the master is a list of items.
When the device is in portrait, I want to show only one view (the list). When the user clicks on a list item, it goes to another state where it shows only the detail page.
And when the device is in landscape, I want to show the list on the left side, and keep the detail on the right side (by default, the first item).

I could make these two scenarios separately, but I can’t find a way to merge them. Let’s put as example the ionic sidemenu seed.

I thought of making two templates for the app.playlists state:

  1. The default seed code, but putting a ion-nav-view that will mask the list’s view when it goes into app.playlists.single
  2. Another that splits the view into two, keeping the list on the left and putting the ion-nav-view to the right.

If I set one of these two as default they work correctly. However, I can’t manage to switch between these two when the window’s orientationchange event is fired. I tried by “resetting” the stateprovider as:

app.config(function($stateProvider, $urlRouterProvider) {
	$stateProvider
	.state('app.playlists', {
		url: "/playlists",
		views: {
			'menuContent':{
				templateUrl: "templates/browse.html"
			}
		}
	});
});

This example should show the “browse” template when going into playlists (it’s easier to test… small clean code), but it just does nothing. What am I missing? or maybe its better to get this layout using another approach?

This is a feature that is still in the works. Right now, this isn’t supported and we’re looking at all possibilities on how to set this up. You’re trying to create a layout similar to iOS mail on iPad?

Almost. I see iOS mail as a side menu and the view on the right. There’s a stackoverflow post somewhere that asks how to keep the side menu open (to prevent it from closing when touching outside), and it’s solved. The problem with that solution is that the menu is hardly dynamic.

My application would have a side menu that takes you to the different parts of the app (and it should hide), and some of these parts there’s a list of items that each item has more information to be shown, and I think that for wide screens (tablets and/or orientation changes) it would be ideal to put the list on the left, and the detail on the right.

edit: The only solution that comes into my mind is to put different urls for different setups. Like /app/portrait_playlists and /app/landscape_playlists, and change ALL the links when you change from one to the other. Sounds very buggy, yes. But maybe angular two-way binding can help?

Regardless of the official support for split views from Ionic, you can already have different views for each orientation, with little logic separation. AngularUI Router understands functions for both templateUrl and controller fields on the state configuration. Idealy, you’d be doing something like this:

//detect orientation here
function weAreOnPortrait() {return true};
$stateProvider.state({
  templateUrl: function () {
    if (weAreOnPortrait()) {return 'tmpl/feature/_portrait.html'}
    else {return 'tmpl/feature/_landscape.html'}
  },
  controller: 'CommonControllerForBothCases'
});

With that, you can run $state.reload() (read here) to refresh the view. Unfortunately this implementation will destroy the current “state” of your state: if the user had provided any input or scrolled down a list, it will be gone. Those can be solved though, and you can also have both views on the same templateUrl, and just use ng-ifs to switch between landscape and portrait as soon as a $rootScope.orientation field changes.

1 Like

@fredericogalvao it looks great, but for some reason I can’t get it to work.

The first case, using the function as template URL, for some reason the template after changing the orientation doesn’t replace the old one, although it gets loaded correctly (I’m using chrome’s dev tools -> Network, and it shows as the _landscape.html got loaded), but in the screen (and in the DOM via DOM inspector) still shows the _portrait.html. No error/warnings in the console either.

I’m going to try the second method, will update on how it goes.

Did you put $state.reload() inside your orientationchange code? It should look something like this:

app.controller('controllerName', function ($state, $window, $rootScope) {
  function reloadThisStateOnly() {
    $state.reload();
  }

  function cleanUp() {
    $window.removeEventListener('orientationchange', reloadThisStateOnly, false);
    deregistration();
  }

  $window.addEventListener('orientationchange', reloadThisStateOnly, false);

  var deregistration;
  deregistration = $rootScope.$on('$stateChangeStart', cleanUp);
});

Sorry for not updating this. I froze this issue for later (and now, looking at it again, in google I found this thread haha!).

Finally I used media queries. I have the detail view split into two by putting two "ng-include"s, that they’re “position:absolute”, the left one references the list view, and the right one the detail itself.
If it’s in landscape, the left one gets width:20em, and the right one gets left:20em. If it’s in portrait, just change these “20em” for “0px” and it’s gone (well, hidden).

Then I will set up an orientationchange listener to change the current app state depending on what I want (eg. If the user is in portrait in the list state, he will be taken to the detail state (that’s now splitted into two views, and if it’s in portrait the left one gets hidden) of the first item).

Thank you very much for your support, it gave me many ideas.

I have the need of this feature on tablet device as well while reusing the exact same templates for phone device. Decided to have a shot at it by utilizing responsive-grid.

Got it working and pretty happy with the result. I’ve put the source up on github.

Hopefully it can be of useful to others.

1 Like

@wanchiu Thank you! This is what i was looking for.