如何在不使用require.js的情况下启动模块后,如何将模块添加到我的应用程序中?

时间:2014-05-30 12:43:23

标签: angularjs

我的AngularJS应用程序有一个模块admin,我想让它只对Admin角色的人有用。在服务器上,我已将此模块的文件全部放在一个目录中,并且我将此web-config放在同一目录中。这是有效的,除非用户处于管理员角色,否则他们无法下载javascript文件:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
      <security>
          <authorization>
              <remove users="*" roles="" verbs="" />
              <add accessType="Allow" roles="Admin" />
          </authorization>
      </security>
  </system.webServer>
</configuration>

所以我的服务器端解决方案似乎已经解决了。但是我完全不知道在客户端上做什么,如何下载脚本并在引导后将模块添加到我的应用程序。这就是我所拥有的:

我使用web-config保护的admin目录中的文件如下所示:

admin.js

angular.module('admin', [])

homeController.js

angular.module('admin')
        .controller('AdminHomeController', ['$http', '$q', '$resource', '$scope', '_o', adminHomeController]);

function adminHomeController($http, $q, $resource, $scope, _o) {
    ....
    ... 
}

我的应用程序级文件如下所示:

app.js

var app = angular
    .module('app',
        ['ui.router', 'admin', 'home',])
    .run(['$rootScope', '$state', '$stateParams', '$http', '$angularCacheFactory', appRun])

function appRun($rootScope, $state, $stateParams, $http, $angularCacheFactory) {
    $rootScope.$state = $state;
    $rootScope.$stateParams = $stateParams;
}

app.config.js

app.config(['$controllerProvider', '$httpProvider', '$locationProvider', '$sceProvider', '$stateProvider', appConfig]);

function appConfig($httpProvider, $locationProvider, $sceProvider, $stateProvider) {

    // I added this to help with loading the module after
    // the application has already loaded
    app.controllerProvider = $controllerProvider;
    //
    $sceProvider.enabled(false);
    $locationProvider.html5Mode(true);
    var admin = {
        name: 'admin',
        url: '/admin',
        views: {
            'root': {
                templateUrl: '/Content/app/admin/partials/home.html',
            },
            'content': {
                templateUrl: '/Content/app/admin/partials/overview.html',
            },
        }
    };
    var adminContent = {
        name: 'admin.content',
        parent: 'admin',
        url: '/:content',
        views: {
            'root': {
                templateUrl: '/Content/app/admin/partials/home.html',
            },
            'content': {
                templateUrl: function (stateParams) {
                    return '/Content/app/admin/partials/' + stateParams.content + '.html';
                },
            }
        }
    };
    var home = {
        name: 'home',
        url: '/home',
        views: {
            'root': {
                templateUrl: '/Content/app/home/partials/home.html',
            },
            'content': {
                templateUrl: '/Content/app/home/partials/overview.html',
            },
        }
    };
    var homeContent = {
        name: 'home.content',
        parent: 'home',
        url: '/:content',
        views: {
            'root': {
                templateUrl: '/Content/app/home/partials/home.html',
            },
            'content': {
                templateUrl: function (stateParams) {
                    return '/Content/app/home/partials/' + stateParams.content + '.html';
                },
            }
        }
    }; 
    $stateProvider
        .state(admin)
        .state(adminContent)
        .state(home)
        .state(homeContent);  
}

当用户登录时,我知道它是否是Admin角色用户,因为我有一个安全令牌返回给我,显示:

{
"access_token":"abcdefg",
"token_type":"bearer",
"expires_in":1209599,
"userName":"xx",
"roles":"Admin",
".issued":"Fri, 30 May 2014 12:23:53 GMT",
".expires":"Fri, 13 Jun 2014 12:23:53 GMT"
}

如果是Admin角色用户,那么我想

  • 从服务器下载管理模块脚本:/Content/app/admin/admin.js和/Content/app/admin/homeController.js。我已经为$ http调用设置了这样的设置:$http.defaults.headers.common.Authorization = 'Bearer ' + user.data.bearerToken;所以在获取脚本时需要发送Bearer令牌:

  • admin模块添加到应用

有人可以就如何做这两件事给我一些建议。在阅读了关于require.js之后,我觉得我不想用它作为解决方案。我希望尽可能简单。

根据我的理解,直到AngularJS允许,然后我需要制作它以便我可以注入我的控制器。所以我已经将它添加到appConfig:

app.controllerProvider = $controllerProvider;

但是如何下载这两个javascript文件以及如何将这些文件添加到AngularJS中以便用户可以开始使用管理模块中控制器的功能?我看到了Angular团队正在使用的 $ script.js 。这是一个很好的解决方案,我如何能够实现这个以满足我相当简单的需求。

3 个答案:

答案 0 :(得分:1)

您可以将解析属性添加到管理路由。

var admin = {
    name: 'admin',
    url: '/admin',
    resolve: {
        isAdminAuth: function($q, User) {
            var defer = $q.defer();

            if (User.isAdmin) {
                defer.resolve();
            } else {
                defer.reject();
            }

            return defer.promise;
        }
    },
    views: {
        'root': {
            templateUrl: '/Content/app/admin/partials/home.html',
        },
        'content': {
            templateUrl: '/Content/app/admin/partials/overview.html',
        },
    }
};

你也可以链接它。

    resolve: {
        adminPermissions: function($q, User) {
            var defer = $q.defer();

            if (User.permissions.isAdmin) {
                defer.resolve(User.permissions.admin);
            } else {
                defer.reject();
            }

            return defer.promise;
        },
        hasAccessToHome: function($q, adminPermissions) {
            var defer = $q.defer();

            if (adminPermissions.hasAccessToHome) {
                defer.resolve(true);
            } else {
                defer.reject();
            }

            return defer.promise;
        },
    },

如果已解决,resolve属性的结果也将传递给控制器​​。如果被拒绝,路由将不会加载。你可以像这样访问它。

function adminHomeController($scope, adminPermissions, hasAccessToHome) {
    $scope.adminPermissions = adminPermissions;
}

您也可以手动引导应用:

<!doctype html>
<html>
<body>
<div ng-controller="WelcomeController">
  {{greeting}}
</div>

<script src="angular.js"></script>
<script>


  var isAdmin = true; <!-- injected variable from server here -->


  var app = angular.module('demo', [])
  .controller('WelcomeController', function($scope) {
      $scope.greeting = 'Welcome!';
  });
  angular.bootstrap(document, ['demo']);
</script>
</body>
</html>

[参考] - https://docs.angularjs.org/api/ng/function/angular.bootstrap

您可以使用jQuery发出请求,而不是注入变量或其他服务器端模板方法:

$.getJSON('/my/url', function(data) {
    if (data.isAdmin) {
        // bootstrap app with admin module
    } else {
        // bootstrap app without admin module
    }
});

以上是IE8 +兼容的示例替代上述(不是jQuery):

request = new XMLHttpRequest();
request.open('GET', '/my/url', true);

request.onreadystatechange = function() {
  if (this.readyState === 4){
    if (this.status >= 200 && this.status < 400){
      data = JSON.parse(this.responseText);

      if (data.isAdmin) {
        // bootstrap app with admin module
      } else {
        // bootstrap app without admin module
      }
    }
  }
};

request.send();
request = null;

[参考] - http://youmightnotneedjquery.com/#json

答案 1 :(得分:0)

我强烈建议使用一些模块加载器。

我知道您希望保持简单,但编写AMD(异步模块定义)或CommonJS模块将有助于减少与依赖关系管理相关的一些问题(例如以错误的顺序加载内容,覆盖库等)。

如果您真的坚持,可以使用jQuery.getScript()加载其他javascript源代码(假设您将jQuery作为依赖项)。

没有jQuery的纯javascript将要求您执行此answer中找到的loadScript示例。

在这两种情况下,都可以在控制器的回调集中完成;请注意,因为脚本将在全局范围内执行,因此如果您不小心,远程文件中的任何变量,函数都可能会覆盖已存在的内容。

如果您仍然对模块加载器感兴趣但又不喜欢require.js,请快速查看curl.js

答案 2 :(得分:0)

Ido Sela谈论Angularjs应用程序中的授权以及它们如何处理(或卸载)模块:http://youtu.be/62RvRQuMVyg?t=2m35s

我肯定会看到这个,因为它会为您提供一些有关如何有条件地处理授权和加载模块的最佳指导。 (这是我应该在我的原型中处理它的方式,并且在将来的版本中我将有一些时间来清理它。)

如果您确实需要在引导应用程序后进行授权,然后需要有条件地加载模块,您可能会考虑将此作为服务器问题。例如,使用某种脚本加载机制,只根据请求的授权呈现逻辑。

例如,在ASP.net MVC中,我会考虑通过控制器(而非静态内容)提供管理资源,并且只有在用户获得授权的情况下才会呈现实际脚本。

<强>更新

很抱歉没有考虑您的整个问题。听起来你已经确保了服务器端。所以现在模块加载是个问题。我仍然会考虑一个控制器来过滤您的每个授权请求(例如User.IsInRole("FooAdmin")或创建一个实际的过滤器来扩展您的使用)