Search code examples
javascriptangularjsangular-ui-routerangularjs-routingangular-components

Angular ui-router render different component based on route params


Using Angular UI Router, I'm trying to render a different component based on a $state.params value, but I can't find a clean way to do this.

I figured out a working solution already (with some ES2015 fanciness), but that's far from optimal:

/* ----- chapter/chapter.controller.js ----- */
class ChapterController {

  constructor($state) {
    this.$state = $state;
    this.chapterNb = this.$state.params.chapterNb;
  }

}

ChapterController.$inject = ['$state'];

export default ChapterController;

/* ----- chapter/chapter.controller.js ----- */
import controller from './chapter.controller';

const ChapterComponent = {
  controller,
  template: `
    <chapter-01 ng-if="$ctrl.chapterNb === 1"></chapter-01>
    <chapter-02 ng-if="$ctrl.chapterNb === 2"></chapter-02>
  ` // and more chapters to come...
};

export default ChapterComponent;

/* ----- chapter/index.js ----- */
import angular from 'angular';
import uiRouter from 'angular-ui-router';

import ChaptersComponent from './chapters.component';
import ChaptersMenu from './chapters-menu';
import Chapter from './chapter';

const chapters = angular
  .module('chapters', [
    uiRouter,
    ChaptersMenu,
    Chapter
  ])
  .component('chapters', ChaptersComponent)
  .config($stateProvider => {
    'ngInject';
    $stateProvider
      .state('chapters', {
        abstract: true,
        url: '/chapters',
        component: 'chapters'
      })
      .state('chapters.menu', {
        url: '/menu',
        component: 'chaptersMenu'
      })
      .state('chapters.chapter', {
        url: '/{chapterNb:int}',
        component: 'chapter'
      });
  })
  .name;

export default chapters;

The thing is that every <chapter-0*> component is very different, that's why they all correspond to their own template. I would like to find a way to reference automatically the chapter component corresponding to the $state.params.chapterNb instead of having to write those ng-if for each one.

Is there any way to simplify this? Or maybe there is a specific feature for that purpose?


Solution

  • As suggested by Pankaj Parkar in his answer, using a template function helps here.

    With a few tweaks, I've been able to achieve loading the correct component based on $state.params, so here is my solution, for the record (look at first post for others files involved):

    import controller from './chapter.controller';
    
    const ChapterComponent = {
      controller,
      template: ($state) => {
        'ngInject';
        return `
          <chapter-0${$state.params.chapterNb}></chapter-0${$state.params.chapterNb}>
        `;
      }
    };
    
    export default ChapterComponent;