Подтвердить что ты не робот

AngularJS - UI-router - Как настроить динамические представления

Мне трудно найти любую документацию по использованию ui-router динамически через базу данных. Par для курса, все жестко закодировано.

Мой Json:

[
   {
       "name": "root",
       "url": "/",
       "parent": "",
       "abstract": true,
       "views": [
            {"name": "header", "templateUrl": "/app/views/header.html"},            
            {"name" :"footer", "templateUrl": "/app/views/footer.html" }
       ]
   },
    {
        "name": "home",
        "url": "",
        "abstract" : false,
        "parent": "root",
        "views": [
            {"name": "[email protected]", "templateUrl": "/app/views/content1.html"},            
            {"name" :"[email protected]", "templateUrl": "/app/views/left1.html" }
       ]
    },
    {
        "name": "about",
        "url": "/about",
        "abstract": false,
        "parent": "root",
        "views": [
             {"name": "[email protected]", "templateUrl": "/app/views/content2.html"},            
             {"name" :"[email protected]", "templateUrl": "/app/views/left2.html" }
            ]
    }
]

Мое приложение:

'use strict';

var $stateProviderRef = null;
var $urlRouterProviderRef = null;

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


 app.factory('menuItems', function ($http) {
    return {
      all: function () {
        return $http({
            url: '/app/jsonData/efstates.js',
            method: 'GET'
        });
    }
  };
 });


  app.config(function ($locationProvider, $urlRouterProvider, $stateProvider) {
    $urlRouterProviderRef = $urlRouterProvider;
    $stateProviderRef = $stateProvider;
    $locationProvider.html5Mode(false);
    $urlRouterProviderRef.otherwise("/");
  });


  app.run(['$q', '$rootScope', '$state', 'menuItems',
  function ($q, $rootScope, $state, menuItems) {
      menuItems.all().success(function (data) {
          angular.forEach(data, function (value, key) {                
              $stateProviderRef.state(name = value.name, {
                  "url": value.url,
                  "parent" : value.parent,
                  "abstract": value.abstract,
                  "views": {
                     // do not want the below hard coded, I want to loop
                     // through the respective json array objects and populate state & views 
                     // I can do this with everything but views.

                     // first loop
                     'header': { 'templateUrl': '/app/views/header.html' },
                     'footer': { 'templateUrl': '/app/views/footer.html' },

                     // second loop
                     '[email protected]':  { 'templateUrl': '/app/views/left1.html' },
                     '[email protected]': { 'templateUrl': '/app/views/container1.html' },

                     // third loop
                     '[email protected]':  { 'templateUrl': '/app/views/left2.html' },
                     '[email protected]': { 'templateUrl': '/app/views/container2.html' },
                }
            });
        });
        $state.go("home");
    });
 }]);

Мне сложно динамически настраивать мои представления. Любые идеи?


UPDATE:

Я сделал Plunker за Radim Köhler ответ для всех, кого это интересует. Я ценю помощь.

Я думаю, что ui-router является маршрутизатором defacto для angular, и, будучи динамичным, он упростит управление большим приложением.

4b9b3361

Ответ 1

Существует plunker, показывающий, как мы можем динамически настраивать представления. Обновленная версия .run() будет выглядеть так:

app.run(['$q', '$rootScope', '$state', '$http',
  function ($q, $rootScope, $state, $http) 
  {
    $http.get("myJson.json")
    .success(function(data)
    {
      angular.forEach(data, function (value, key) 
      { 
          var state = {
            "url": value.url,
            "parent" : value.parent,
            "abstract": value.abstract,
            "views": {}
          };

          // here we configure the views
          angular.forEach(value.views, function (view) 
          {
            state.views[view.name] = {
              templateUrl : view.templateUrl,
            };
          });

          $stateProviderRef.state(value.name, state);
      });
      $state.go("home");    
    });
}]);

Проверьте, что все в действии здесь

Ответ 2

Мне нужно добавить улучшенную версию, которая даже способна сделать больше.

Итак, теперь мы будем динамически загружать состояния - используя $http, json и определяем состояния в .run()

Но теперь мы можем перейти к любому динамическому состоянию с url (просто поместите его в адресную строку).

Магия встроена в UI-Router - см. эту часть документа:

$urlRouterProvider

deferIntercept(defer)

Отключает (или позволяет) отложить перехват изменения местоположения.

Если вы хотите настроить поведение синхронизации URL-адреса (например, если вы хотите отложить переход, но сохраните текущий URL-адрес), вызовите этот метод во время настройки. Затем во время выполнения вызовите $urlRouter.listen() после того, как вы настроили свой собственный обработчик событий $locationChangeSuccess.

Цитированный фрагмент:

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

app.config(function($urlRouterProvider) {

  // Prevent $urlRouter from automatically intercepting URL changes;
  // this allows you to configure custom behavior in between
  // location changes and route synchronization:
  $urlRouterProvider.deferIntercept();

}).run(function($rootScope, $urlRouter, UserService) {

  $rootScope.$on('$locationChangeSuccess', function(e) {
    // UserService is an example service for managing user state
    if (UserService.isLoggedIn()) return;

    // Prevent $urlRouter default handler from firing
    e.preventDefault();

    UserService.handleLogin().then(function() {
      // Once the user has logged in, sync the current URL
      // to the router:
      $urlRouter.sync();
    });
  });

  // Configures $urlRouter listener *after* your custom listener
  $urlRouter.listen();
});

Итак, обновленный плунжер здесь. Теперь мы можем использовать даже .otherwise() для перехода к состоянию, определенному в последнее время, или перейти туда по URL:

Фаза .config()

app.config(function ($locationProvider, $urlRouterProvider, $stateProvider) {

    // Prevent $urlRouter from automatically intercepting URL changes;
    // this allows you to configure custom behavior in between
    // location changes and route synchronization:
    $urlRouterProvider.deferIntercept();
    $urlRouterProvider.otherwise('/other');

    $locationProvider.html5Mode({enabled: false});
    $stateProviderRef = $stateProvider;
});

Фаза .run()

app.run(['$q', '$rootScope','$http', '$urlRouter',
  function ($q, $rootScope, $http, $urlRouter) 
  {
    $http
      .get("myJson.json")
      .success(function(data)
      {
        angular.forEach(data, function (value, key) 
        { 
          var state = {
            "url": value.url,
            "parent" : value.parent,
            "abstract": value.abstract,
            "views": {}
          };

          angular.forEach(value.views, function (view) 
          {
            state.views[view.name] = {
              templateUrl : view.templateUrl,
            };
          });

          $stateProviderRef.state(value.name, state);
        });
        // Configures $urlRouter listener *after* your custom listener            
        $urlRouter.sync();
        $urlRouter.listen();
      });
}]);

Проверьте обновленный plunker здесь

Ответ 3

Я исследовал различные подходы для динамических маршрутов добавления к ui.router.

Прежде всего, я нашел repositori с ngRouteProvider и основной идеей этого, разрешает $stateProvider ans сохранить его в закрытии на .config, а затем использовать это повторение в любое другое время для добавления новых маршрутов на лету. Это хорошая идея, и она может быть улучшена. Я считаю, что лучшее решение состоит в том, чтобы разделить ответственность за сохранение ссылок на поставщиков и манипулирование ими в любое время и в любом месте. Посмотрите пример refsProvider:

angular
    .module('app.common', [])
        .provider('refs', ReferencesProvider);

const refs = {};

function ReferencesProvider() {
    this.$get = function () {
        return {
            get: function (name) {
              return refs[name];
        }
    };
};

this.injectRef = function (name, ref) {
        refs[name] = ref;
    };
}

Использование refsProvider:

angular.module('app', [
    'ui.router',
    'app.common',
    'app.side-menu',
])
    .config(AppRouts)
    .run(AppRun);

AppRouts.$inject = ['$stateProvider', '$urlRouterProvider', 'refsProvider'];

function AppRouts($stateProvider, $urlRouterProvider, refsProvider) {
    $urlRouterProvider.otherwise('/sign-in');
    $stateProvider
        .state('login', {
            url: '/sign-in',
            templateUrl: 'tpl/auth/sign-in.html',
            controller: 'AuthCtrl as vm'
        })
        .state('register', {
            url: '/register',
            templateUrl: 'tpl/auth/register.html',
            controller: 'AuthCtrl as vm'
        });

    refsProvider.injectRef('$urlRouterProvider', $urlRouterProvider);
    refsProvider.injectRef('$stateProvider', $stateProvider);

}

AppRun.$inject = ['SectionsFactory', 'MenuFactory', 'refs'];

function AppRun(sectionsFactory, menuFactory, refs) {
    let $stateProvider = refs.get('$stateProvider');
    // adding new states from different places
    sectionsFactory.extendStates($stateProvider);
    menuFactory.extendStates($stateProvider);
}

SectionsFactory и MenuFactory являются обычными местами для объявления/загрузки всех состояний.

Другая идея: просто использовать существующие фазы приложения angular и расширять приложения приложения дополнительными провайдерами, например:

angular.module('app.side-menu', []);
    .provider('SideMenu', SideMenuProvider);

let menuItems = [
    {
        label: 'Home',
        active: false,
        icon: 'svg-side-menu-home',
        url: '/app',
        state: 'app',
        templateUrl: 'tpl/home/dasboard.html',
        controller: 'HomeCtrl'
    }, {
        label: 'Charts',
        active: false,
        icon: 'svg-side-menu-chart-pie',
        url: '/charts',
        state: 'charts',
        templateUrl: 'tpl/charts/main-charts.html'
    }, {
        label: 'Settings',
        active: false,
        icon: 'svg-side-menu-settings',
        url: '/'
    }
];
const defaultExistState = menuItems[0].state;

function SideMenuProvider() {
    this.$get = function () {
        return {
            get items() {
                return menuItems;
            }
        };
    };
    this.extendStates = ExtendStates;
}

function ExtendStates($stateProvider) {
    menuItems.forEach(function (item) {
        if (item.state) {
            let stateObj = {
                url: item.url,
                templateUrl: item.templateUrl,
                controllerAs: 'vm'
            };
            if (item.controller) {
                stateObj.controller = `${item.controller} as vm`;
            }
            $stateProvider.state(item.state, stateObj);
        } else {
            item.state = defaultExistState;
        }
    });
}

В этом случае нам не нужно использовать фазу .run для состояний extenfing, а AppRouts из приведенного выше примера изменится на:

AppRouts.$inject = ['$stateProvider', '$urlRouterProvider', 'SideMenuProvider'];

function AppRouts($stateProvider, $urlRouterProvider, SideMenuProvider) {
    $urlRouterProvider.otherwise('/sign-in');
    $stateProvider
        .state('login', {
            url: '/sign-in',
            templateUrl: 'tpl/auth/sign-in.html',
            controller: 'AuthCtrl as vm'
        })
        .state('register', {
            url: '/register',
            templateUrl: 'tpl/auth/register.html',
            controller: 'AuthCtrl as vm'
        })

    SideMenuProvider.extendStates($stateProvider);
}

Также у нас есть доступ ко всем пунктам меню в любом месте: SideMenu.items