【问题标题】:AngularJS - UI-router - How to configure dynamic viewsAngularJS - UI-router - 如何配置动态视图
【发布时间】:2014-07-13 21:11:29
【问题描述】:

我很难找到任何有关通过数据库动态使用 ui-router 的文档。当然,一切都是硬编码的。

我的 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": "container@", "templateUrl": "/app/views/content1.html"},            
            {"name" :"left@", "templateUrl": "/app/views/left1.html" }
       ]
    },
    {
        "name": "about",
        "url": "/about",
        "abstract": false,
        "parent": "root",
        "views": [
             {"name": "container@", "templateUrl": "/app/views/content2.html"},            
             {"name" :"left@", "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
                     'left@':  { 'templateUrl': '/app/views/left1.html' },
                     'container@': { 'templateUrl': '/app/views/container1.html' },

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

我很难动态配置我的视图。有什么想法吗?


更新:

我根据 Radim Köhler 对任何感兴趣的人的回答做了一个Plunker。感谢您的帮助。

我认为 ui-router 是事实上的 Angular 路由器,并且通过动态化,它将使大型应用程序更易于管理。

【问题讨论】:

  • 您是否考虑过将数据作为变量传递到页面中直接加载的 js 文件中并避免异步调用来获取它?
  • 是的,但这会降低我想要的安全性。您的提议会在浏览器的缓存中留下痕迹。
  • 真的不明白为什么它比通过 ajax 调用更安全,尽管在检索数据后也可以手动引导角度
  • 如果我对您的理解正确,您建议我使用 db 中的数据创建一个 js 文件并从 js 文件中读取。为什么?为什么要添加资源/时间来打印可以从数据库中读取 str8 的内容?为什么在缓存中有一个js文件供任何人阅读?必须有一种方法可以循环遍历数据库中的 str8 视图。
  • 那你的具体问题是什么?我的建议是基于您无法解析 run() 并且配置将在您返回数据之前触发,除非您立即有可用数据或手动引导。没有什么能阻止任何人看到数据

标签: angularjs angular-ui-router


【解决方案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");    
    });
}]);

检查所有操作here

【讨论】:

  • 非常感谢,为什么你有一个未命名的 ui-view 以及其他 4 个命名的 ui-view?
  • 如果有帮助就太好了;)是的……那是……用于测试;)抱歉,它不应该在那里!好地方先生;)
  • 嘿,这是一个很好的解决方案,但如果我想进入当前状态,而不是 $state.go("home");我希望 ui-router 解析并找出当前状态是哪一个并去那里。有没有办法做到这一点?
  • 任何时候你都可以访问 $state.current。应该有当前的状态。您可以像 $state.current.name 一样使用它
  • 不,当前状态是 Object {name: "", url: "^", views: null, abstract: true}。我不知道我现在处于哪个状态,因为路由器只是在 $http 请求之后配置的......
【解决方案2】:

我必须附加一个改进的版本,它甚至可以做更多的事情。

所以,现在我们仍将动态加载状态 - 使用 $httpjson 并在 .run() 中定义状态

但现在我们可以通过 url 导航到任何动态状态(只需将其放在地址栏中)。

UI-Router 内置了魔法 - 请参阅文档的这一部分:

$urlRouterProvider

deferIntercept(defer)

禁用(或启用)延迟位置更改拦截。

如果您希望自定义同步 URL 的行为(例如,如果您希望推迟转换但保持当前 URL),请在配置时调用此方法。然后,在运行时,在您配置好自己的 $locationChangeSuccess 事件处理程序后调用 $urlRouter.listen()

引用sn-p:

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's 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's listener *after* your custom listener
  $urlRouter.listen();
});

所以updated plunker is here。我们现在甚至可以使用 .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's listener *after* your custom listener            
        $urlRouter.sync();
        $urlRouter.listen();
      });
}]);

查看updated plunker here

【讨论】:

  • 您好,谢谢您的解释,非常有用。但我现在想做的是在 json 文件中指定控制器。但它不会初始化控制器。有什么想法吗?
  • 我不完全确定,如果它是一个名称,这可能意味着“控制器也在 json 文件中”,这应该不是问题。如果它是实现,那么我试图深入描述如何在这里延迟加载东西stackoverflow.com/q/22627806/1679310...希望它至少给出一些方向...
  • 感谢您的快速回复。事实上,它也是一个存储在我的数据库中的字符串,或者我想我会实现它。我会看看你的其他答案。
  • 救命稻草,我为此苦苦挣扎,为什么用ui-router拥有动态状态并不容易...
【解决方案3】:

我研究了向ui.router动态添加路由的不同方法。

首先,我找到了带有ngRouteProvider 的存储库,其主要思想是解析$stateProvider 并在.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);
}

SectionsFactoryMenuFactory 是声明/加载所有状态的常用位置。

其他想法只是使用现有的角度应用程序阶段并通过其他提供程序扩展应用程序状态,例如:

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 阶段来扩展状态,而上面示例中的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

【讨论】:

    猜你喜欢
    • 2014-10-02
    • 1970-01-01
    • 2014-03-02
    • 1970-01-01
    • 2015-10-14
    • 2014-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多