Hid
Hid

Reputation: 613

Switch between mobile or desktop template using condition in templateUrl (Angular 7)

I want to switch between a desktop and mobile template depending on the screen width to ensure my application is responsive. I am trying to do the following:

@Component({
    selector: 'app-root',
    templateUrl: "./" + (window.innerWidth < 768) ? "app.component.html" : "app.component.mobile.html",
    styleUrls: ['./app.component.css']
})

However, this is not working. Instead of the template loading, the string, "app.component.html", appears on the screen instead.

What is even more interesting is that, if I use the following:

@Component({
    selector: 'app-root',
    templateUrl: "./" + (false) ? "app.component.html" : "app.component.mobile.html",
    styleUrls: ['./app.component.css']
})

The page still only shows the string "app.component.html".

Is there no support for using conditional statements as the value for the templateUrl field in the @Component decorator?

If not, what is an alternative solution I could use to achieve this level of responsiveness that is still modular and follows best practices?

Update: I got this to work by using ng serve --aot instead of just ng serve. However, I decided to not go through with this idea because it does not switch templates as the window resizes.

Upvotes: 8

Views: 8833

Answers (3)

user2739963
user2739963

Reputation: 75

If your app does a lot of stuff, then you are going to need to route to a /mobile, perhaps using the navigator.userAgent, and push the user there.

You cannot use the old 'screen width <= 768' as the iPhone XR for example has a screen width of 1792 pixels on a 5.9" wide screen, so actually it's pixel density that matters, which you cannot access.

Upvotes: 0

bryan60
bryan60

Reputation: 29325

your approach here is off. Templates are assigned to components at build time, not runtime and Angular will build all of it's components, well before any window exists with a width to write a conditional off of. You need to set up logic to tell your app when to show which component.

The "best practice" (IMO) here, if you can't do responsive design (note: it's important to understand your audience and how the app will be used. I always try to opt for a mobile first responsive design, but also recognize this isn't always appropriate), is to have 2 components, and use route guards to enforce the correct component.

@Injectable()
export class MobileGuard implements CanActivate {

  constructor(
    private router: Router
  ) { }

  canActivate() {

    if (window.innerWidth >= 768) {
      this.router.navigate(['/']);
      return false;
    }

    return true;
  }
}

@Injectable()
export class DesktopGuard implements CanActivate {

  constructor(
    private router: Router
  ) { }

  canActivate() {

    if (window.innerWidth < 768) {
      this.router.navigate(['/m/']);
      return false;
    }

    return true;
  }
}

then define your routing structure somewhat like this:

const routes: Routes = [
  {
    path: '',
    component: AppComponent,
    canActivate: [DesktopGuard],
    children: [... desktop routes ...]
  },
  {
    path: 'm',
    component: MobileAppComponent,
    canActivate: [MobileGuard],
    children: [... mobile routes ...]
  }
]

as for the components themselves, your mobile component just extends your non mobile component and has a different template / styles associated.

an alternative approach here is to do something like this:

export class WrapperAppComponent {
    isMobile: boolean;

    constructor() {
      this.isMobile = window.innerWidth < 768;
    }
}

with template like:

<desktop-app *ngIf="!isMobile"></desktop-app>
<mobile-app *ngIf="isMobile>></mobile-app>

but this isn't a very scalable approach and has the same "duplication" of components.

Upvotes: 11

Abhi
Abhi

Reputation: 60

I believe you can you do this -

export function getTemplateUrl() {
  if (window.innerWidth < 768)
    return "app.component.html";
  else 
    return "app.component.mobile.html";
}

@Component({
  selector: 'app-root',
  templateUrl: getTemplateUrl(),
  styleUrls:['./app.component.css']
})

and then compile your application using aot - ng serve --aot . this works with lower version of angular, I haven't tried with Angular 7 but should work.

Upvotes: -3

Related Questions