Adaptive layout (mobile/desktop) in AngularJS - what is the preferred way to do it (example inside)

2.5k views Asked by At

What is the preferred way to handle adaptive (it's not about responsive) layout in AngularJS? What I need to do is to have a different layout with different shared components (shared directives and controllers) for desktop and mobile. I was thinking about using ui-router, here is what I have now:

index.html (main file):

<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="[email protected]" src="https://code.angularjs.org/1.4.1/angular.js" data-semver="1.4.1"></script>
    <script data-require="[email protected]" data-semver="0.2.15" src="//rawgit.com/angular-ui/ui-router/0.2.15/release/angular-ui-router.js"></script>
    <script src="app.js"></script>
  </head>

  <body data-ng-app="plunker" data-ng-strict-di>
    <header>
    <nav>
      <ul>
        <li><a data-ui-sref="mobile.user">Mobile</a></li>
        <li><a data-ui-sref="desktop.user">Desktop</a></li>
      </ul>
    </nav>
    </header>
    <main data-ui-view>
    </main>
  </body>

</html>

desktop.html (wrapper for desktop content):

<h1>Desktop</h1>
<div data-ui-view>
</div>

mobile.html (wrapper for mobile content):

<h1>Mobile</h1>
<div data-ui-view>
</div>

user.html (shared content):

<div data-ng-controller="UserCtrl">
User name: {{name}}
</div>

app.js

var app = angular.module('plunker', ['ui.router']);

app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
  $stateProvider
    .state('mobile', {
      abstract: true,
      url: '/mobile',
      templateUrl: 'mobile.html'
    })
    .state('mobile.user', {
      url: '/user',
      templateUrl: 'user.html'
    })   
    .state('desktop', {
      abstract: true,
      url: '/desktop',
      templateUrl: 'desktop.html'
    })
    .state('desktop.user', {
      url: '/user',
      templateUrl: 'user.html'
    })
}]);

app.controller('UserCtrl', ['$scope', function($scope) {
  $scope.name = 'John';
}]);

Preview & edit:

http://plnkr.co/edit/gRnTJkMa7hTLnffOERMT?p=preview

  1. Is this a preferred way of doing adaptive layout?
  2. How can I (with this approach):

    1. Add a class to in index.html based on mobile/desktop choice
    2. Dynamically load mobile.css or desktop.css

Best Regards,

2

There are 2 answers

0
Kun Andrei On

I recently ran into same issues, that's why i landed here looking for some answers. According to my findings, looks like i have two different options:

  1. Use your approach

Facts

  • Two partial different layouts, because header and footer and some other body tags are still shared. Otherwise you will have to apply data-ui-view on html
  • This can be achieved only if you use ui-router, this isn't working with angular default router
  • You will load all routes and configs, both from desktop and mobile, because you have a shared config file. This means that somehow you need to load all js files even if user is visiting only mobile version. Otherwise you will have to create a new config file for mobile version and use that one for mobile layout, or dynamic exclude some route based on detected device.
  • You will be able to switch from mobile to desktop within your Single Page Application without any reload. Not sure if this is an advantage or not.

Good parts:

You are not going to apply any server logic and your are not going to make a separation of main index.html file served from server. This means that the entire logic lies on the client side.

Bad Parts:

You complicate the logic because you need to make a clear separation between devices and avoid loading unused js files if possible. If you are doing this, basically you are doing another SPA, because desktop version won't be available within your SPA.

  1. Share the same app but from server based on domain you can serve different html file. For example: yoursite.com will serve index.html which will eventually load desktop version and m.yourside.com will serve mobile.html which will eventually load mobile version.

Facts:

  • This is more like a hard separation and implies server logic to make a separation
  • When switching from mobile and desktop, a reload will take place.
  • Now, you can't use the same config or other changes from app.js that refers to your desktop version, so it might need to change some files to not refer anymore to libraries that are used for desktop
  • This approach looks more like a new angularJs app within same project (folder structure/ application folder) with same name maybe and lots of shared components and services, but less dependencies and cleaner.

To me, this sounds more like a logic separation consider that you do not need to server all those js files on your mobile version, maybe you won't use all the features of desktop version to mobile version. The only difference i see here at presentation level, different routing maybe, config file, controllers and views. Still same models and share services and big componentes.

In order to make a decision you need to think on both situations each with good and bad parts. Right now i don't know yet which decision to make, i will investigate maybe a day or two.

Good day and good luck.

Andrei

0
Manish Kumar On
var isMobile = window.orientation > -1;
var orientationVariable = isMobile ? 'Mobile' : 'DeskTop';
if (orientationVariable == 'Mobile'){      
    $urlRouterProvider.otherwise("/mobile/main");
    $stateProvider
         .state('mobile', {
             abstract: true,
             url: '/mobile',
             controller: 'appCtrl',
             templateUrl: '../app/tpl/mobile-main.html'
         })
      .state('mobile.main', {    
            url: '/main',
            controller: 'appCtrl',
            templateUrl: '../app/tpl/app-mobile.html'     
        })}else if(orientationVariable == 'DeskTop') {

    $urlRouterProvider.otherwise("/desktop/main");

    $stateProvider.state('desktop', {
            abstract: true,
            url: '/desktop',
            controller: 'appCtrl',
            templateUrl: '../app/tpl/desktop.html',
        })
         .state('desktop.main', {
             url: '/main',
             controller: 'appCtrl',
             templateUrl: '../app/tpl/desktop.main.html',
         })}