Javascript 从服务器加载html和控制器并创建动态状态UI-router
我正在寻找从服务器动态加载我的应用程序内容的解决方案 我的设想: 假设我们有2个用户(A和B),我的应用程序由不同的模块组成,比如购物清单和计算器,现在我的目标是用户从数据库登录到我的应用程序,我获得用户权限,根据他拥有的权限,我将从服务器加载视图的html和逻辑部分的控制器文件,在这样做的同时,我将创建html和ctrl所需的状态。因此,基本上我的应用程序是非常小的登录,其他一切都是从服务器上拉取决于用户权限 我使用的是:Javascript 从服务器加载html和控制器并创建动态状态UI-router,javascript,angularjs,requirejs,angular-ui-router,oclazyload,Javascript,Angularjs,Requirejs,Angular Ui Router,Oclazyload,我正在寻找从服务器动态加载我的应用程序内容的解决方案 我的设想: 假设我们有2个用户(A和B),我的应用程序由不同的模块组成,比如购物清单和计算器,现在我的目标是用户从数据库登录到我的应用程序,我获得用户权限,根据他拥有的权限,我将从服务器加载视图的html和逻辑部分的控制器文件,在这样做的同时,我将创建html和ctrl所需的状态。因此,基本上我的应用程序是非常小的登录,其他一切都是从服务器上拉取决于用户权限 我使用的是: 科尔多瓦 安格拉斯 离子骨架 为什么我需要它充满活力: 1) 有可能有
var rights= [A,B,C,D]
angular.forEach(rights, function (value, key) {
$http.get('http://myServer.com/templates/' + value + '.html').then(function (response) {
//HTML file for the whole module
splits = response.data.split('#');
//Array off HTMl strings
for (var l = 1; l <= splits.length; l++) {
//Putting all Html strings into templateCache
$templateCache.put('templates/' + value +'.html', splits[l - 1]);
}
}
});
这不起作用,因为这只是一个字符串
定义提供者:
.config(function ($stateProvider, $urlRouterProvider, $ionicConfigProvider, $controllerProvider) {
// turns of the page transition globally
$ionicConfigProvider.views.transition('none');
$stateProviderRef = $stateProvider;
$urlRouterProviderRef = $urlRouterProvider;
$controllerProviderRef = $controllerProvider;
$stateProvider
//the login state is static for every user
.state('login', {
url: "/login",
templateUrl: "templates/login.html",
controller: "LoginCtrl"
});
//all the other states are missing and should be created depending on rights
$urlRouterProvider.otherwise('/login');
});
用户界面路由器部分:
//Lets assume here the Rights Array contains more information like name, url...
angular.forEach(rights, function (value, key) {
//Checks if the state was already added
var getExistingState = $state.get(value.name)
if (getExistingState !== null) {
return;
}
var state = {
'lang': value.lang,
'params': value.param,
'url': value.url,
'templateProvider': function ($timeout, $templateCache, Ls) {
return $timeout(function () {
return $templateCache.get("templates" + value.url + ".html")
}, 100);
},
'ControllerProvider': function ($timeout, $templateCache, Ls) {
return $timeout(function () {
return $templateCache.get("controllers" + value.url + ".js")
}, 100);
}
$stateProviderRef.state(value.name, state);
});
$urlRouter.sync();
$urlRouter.listen();
到目前为止的情况:
我已经设法加载html文件并将它们存储在templateCache中,甚至加载它们,但前提是状态是预定义的。我在这里注意到的是,有时让我们说,当我从列表中删除一个项目并返回到视图时,该项目再次出现,可能这与缓存有关我不太确定
我已设法加载控制器文件并将控制器保存在templateCache中,但我真的不知道如何将$ControllerPrioviderRef.register与存储的字符串一起使用
创建状态确实有效,但控制器不适合,因此我无法打开任何视图
PS:我还研究了require.js和OCLazyLoad以及这个示例
更新:
好的,所以我设法加载了Html
,用控制器创建了状态
,一切似乎都很正常,只是控制器似乎根本不工作,没有错误,但似乎没有执行任何控制器逻辑。目前,从先前下载的文件中注册控制器的唯一解决方案是使用eval(),
,这与其说是一个合适的解决方案,不如说是一种黑客行为
代码如下:
.factory('ModularService', ['$http', ....., function ( $http, ...., ) {
return {
LoadModularContent: function () {
//var $state = $rootScope.$state;
var json = [
{
module: 'Calc',
name: 'ca10',
lang: [],
params: 9,
url: '/ca10',
templateUrl: "templates/ca/ca10.html",
controller: ["Ca10"]
},
{
module: 'SL',
name: 'sl10',
lang: [],
params: 9,
url: '/sl10',
templateUrl: "templates/sl/sl10.html",
controller: ['Sl10', 'Sl20', 'Sl25', 'Sl30', 'Sl40', 'Sl50', 'Sl60', 'Sl70']
}
];
//Load the html
angular.forEach(json, function (value, key) {
$http.get('http://myserver.com/' + value.module + '.html')
.then(function (response) {
var splits = response.data.split('#');
for (var l = 1; l <= value.controller.length; l++) {
$templateCache.put('templates/' + value.controller[l - 1] + '.html', splits[l - 1]);
if (l == value.controller.length) {
$http.get('http://myserver.com//'+value.module+'.js')
.then(function (response2) {
var ctrls = response2.data.split('##');
var fullctrl;
for (var m = 1; m <= value.controller.length; m++){
var ctrlName = value.controller[m - 1] + 'Ctrl';
$controllerProviderRef
.register(ctrlName, ['$scope',...., function ($scope, ...,) {
eval(ctrls[m - 1]);
}])
if (m == value.controller.length) {
for (var o = 1; o <= value.controller.length; o++) {
var html = $templateCache
.get("templates/" + value.controller[o - 1] + ".html");
var getExistingState = $state.get(value.controller[o - 1].toLowerCase());
if (getExistingState !== null) {
return;
}
var state = {
'lang': value.lang,
'params': value.param,
'url': '/' + value.controller[o - 1].toLowerCase(),
'template': html,
'controller': value.controller[o - 1] + 'Ctrl'
};
$stateProviderRef.state(value.controller[o - 1].toLowerCase(), state);
}
}
}
});
}
}
});
});
// Configures $urlRouter's listener *after* your custom listener
$urlRouter.sync();
$urlRouter.listen();
}
}
}])
.factory('ModularService',['$http',..,函数($http,…){
返回{
LoadModulerContent:函数(){
//var$state=$rootScope.$state;
var json=[
{
模块:“计算”,
名称:“ca10”,
朗:[],
参数:9,
url:“/ca10”,
templateUrl:“templates/ca/ca10.html”,
控制器:[“Ca10”]
},
{
模块:“SL”,
名称:“sl10”,
朗:[],
参数:9,
url:“/sl10”,
templateUrl:“templates/sl/sl10.html”,
控制器:['Sl10','Sl20','Sl25','Sl30','Sl40','Sl50','Sl60','Sl70']
}
];
//加载html
forEach(json,函数(值,键){
$http.get('http://myserver.com/“+value.module+”.html')
.然后(功能(响应){
var splits=response.data.split(“#”);
对于(var l=1;lOk,让我们从头开始
所有应用程序逻辑都应包含在服务器上,并通过REST、SOAP或类似方式通过API调用提供服务。这样做可以减少UI中内置的逻辑量,从而减少对客户端的压力。这基本上使客户端应用程序成为一个呈现代理,只包含b服务的数据和逻辑的模型和视图AckendAPI
正如foreyez在评论中所说,这不是任何现代(或半现代)设备的问题
如果你坚持不一次加载所有的布局,你当然可以将它们分割成部分,然后根据用户权限在登录后加载。这样做,你可以减少内存中的数据量,即使这种改进充其量是值得怀疑的。我开发了一个应用程序,将这些数据保存在m中这是我的建筑
文件夹结构:
WebApp
|---CommonModule
|---common-module.js //Angular Module Defination
|---Controllers //Generally Nothing, but if you have a plan to
//extend from one CommonController logic to several
//module then it is usefull
|---Services //Common Service Call Like BaseService for all $http
//call, So no Module Specific Service will not use
//$http directly. Then you can do several common
//things in this BaseService.
//Like Error Handling,
//CSRF token Implementation,
//Encryption/Decryption of AJAX req/res etc.
|---Directives //Common Directives which you are going to use
//in different Modules
|---Filters //Common Filters
|---Templates //Templates for those common directives
|---index.jsp //Nothing, Basically Redirect to
//Login or Default Module
|---scripts.jsp //JQuery, AngularJS and Other Framworks scripts tag.
//Along with those, common controlers, services,
//directives and filtes.
|---templates.jsp //Include all common templates.
|---ng-include.jsp //will be used in templates.jsp to create angular
//template script tag.
|---ModuleA
|---moduleA-module.js //Angular Module Definition,
//Use Common Module as Sub Module
|---Controllers
|---Services
|---Directives
|---Filters
|---Templates
|---index.jsp
|---scripts.jsp
|---templates.jsp
|---ModuleB
|--- Same as above ...
注意:大写字母表示文件夹。模块a旁边会有一个用于您案例的LoginModule,我想您也可以使用CommonModule
梅胡的发言如下
<a href="/ModuleA/">Module A</a> <!--Note: index.jsp are indexed file
//for a directive -->
<a href="/ModuleB/">Module B</a>
angular.module('ModuleA.controllers', []);
angular.module('ModuleA.services', []);
angular.module('ModuleA.directives', []);
angular.module('ModuleA.filters', []);
angular.module('ModuleA',
['Common',
'ModuleA.controllers' ,
'ModuleA.services' ,
'ModuleA.directives' ,
'ModuleA.filters'])
.config(['$routeProvider', function($routeProvider) {
//$routeProvider state setup
}])
.run (function () {
});
我可以建议您更改加载状态的方式吗?
编写一个脚本,返回一个json,其中包含用户可以访问的状态。
例如
资源/路由配置.yourLangage?user=user-id-12345
这将返回一个json文件,该文件取决于登录的用户。结构可以如下所示:
[
{
"name": "home",
"url": "/home",
"templateUrl": "views/home.html",
"controller": "HomeController",
"dependencies": ["scripts/home/controllers.js", "scripts/home/services.js", "scripts/home/directives.js"]
},
{
"name": "user",
"url": "/user",
"templateUrl": "views/user.html",
"controller": "UserController",
"dependencies": ["scripts/user/controllers.js", "scripts/user/services.js", "scripts/home/directives.js"]
}
]
然后,让我们编写一个服务来读取允许用户访问的状态:
app.factory('routingConfig', ['$resource',
function ($resource) {
return $resource('resources/routing-config.yourLangage', {}, {
query: {method: 'GET',
params: {},
isArray: true,
transformResponse: function (data) {
// before that we give the states data to the app, let's load all the dependencies
var states = [];
angular.forEach(angular.fromJson(data), function(value, key) {
value.resolve = {
deps: ['$q', '$rootScope', function($q, $rootScope){
// this will be resolved only when the user will go to the relative state defined in the var value
var deferred = $q.defer();
/*
now we need to load the dependencies. I use the script.js javascript loader to load the dependencies for each page.
It is very small and easy to be used
http://www.dustindiaz.com/scriptjs
*/
$script(value.dependencies, function(){ //here we will load what is defined in the dependencies field. ex: "dependencies": ["scripts/user/controllers.js", "scripts/user/services.js", "scripts/home/directives.js"]
// all dependencies have now been loaded by so resolve the promise
$rootScope.$apply(function(){
deferred.resolve();
});
});
return deferred.promise;
}]
};
states.push(value);
});
return states;
}
}
});
}]);
然后,让我们配置应用程序:
app.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', '$filterProvider', '$provide', '$compileProvider',
function ($stateProvider, $urlRouterProvider, $locationProvider, $filterProvider, $provide, $compileProvider) {
// this will be the default state where to go as far as the states aren't loaded
var loading = {
name: 'loading',
url: '/loading',
templateUrl: '/views/loading.html',
controller: 'LoadingController'
};
// if the user ask for a page that he cannot access
var _404 = {
name: '_404',
url: '/404',
templateUrl: 'views/404.html',
controller: '404Controller'
};
$stateProvider
.state(loading)
.state(_404);
// save a reference to all of the providers to register everything lazily
$stateProviderRef = $stateProvider;
$urlRouterProviderRef = $urlRouterProvider;
$controllerProviderRef = $controllerProvider;
$filterProviderRef = $filterProvider;
$provideRef = $provide;
$compileProviderRef = $compileProvider;
//redirect the not found urls
$urlRouterProvider.otherwise('/404');
}]);
现在让我们在应用程序中使用此服务。运行:
app.run(function ($location, $rootScope, $state, $q, routingConfig) {
// We need to attach a promise to the rootScope. This will tell us when all of the states are loaded.
var myDeferredObj = $q.defer();
$rootScope.promiseRoutingConfigEnd = myDeferredObj.promise;
// Query the config file
var remoteStates = routingConfig.query(function() {
angular.forEach(remoteStates, function(value, key) {
// the state becomes the value
$stateProviderRef.state(value);
});
// resolve the promise.
myDeferredObj.resolve();
});
//redirect to the loading page until all of the states are completely loaded and store the original path requested
$rootScope.myPath = $location.path();
$location.path('/loading'); //and then (in the loading controller) we will redirect to the right state
//check for routing errors
$rootScope.$on('$stateChangeError',
function(event, toState, toParams, fromState, fromParams, error){
console.log.bind(console);
});
$rootScope.$on('$stateNotFound',
function(event, unfoundState, fromState, fromParams){
console.error(unfoundState.to); // "lazy.state"
console.error(unfoundState.toParams); // {a:1, b:2}
console.error(unfoundState.options); // {inherit:false} + default options
});
});
最后,LoadingController:
app.controller('LoadingController', ['$scope', '$location', '$rootScope',
function($scope, $location, $rootScope) {
//when all of the states are loaded, redirect to the requested state
$rootScope.promiseRoutingConfigEnd.then(function(){
//if the user requested the page /loading then redirect him to the home page
if($rootScope.myPath === '/loading'){
$rootScope.myPath = '/home';
}
$location.path($rootScope.myPath);
});
}]);
通过这种方式,一切都是超级灵活和惰性加载的
我已经编写了3个不同的用户门户,我可以轻松地扩展到我想要的所有用户门户。我想我正在做你想做的事情
angular.module('ModuleA.controllers', []);
angular.module('ModuleA.services', []);
angular.module('ModuleA.directives', []);
angular.module('ModuleA.filters', []);
angular.module('ModuleA',
['Common',
'ModuleA.controllers' ,
'ModuleA.services' ,
'ModuleA.directives' ,
'ModuleA.filters'])
.config(['$routeProvider', function($routeProvider) {
//$routeProvider state setup
}])
.run (function () {
});
[
{
"name": "home",
"url": "/home",
"templateUrl": "views/home.html",
"controller": "HomeController",
"dependencies": ["scripts/home/controllers.js", "scripts/home/services.js", "scripts/home/directives.js"]
},
{
"name": "user",
"url": "/user",
"templateUrl": "views/user.html",
"controller": "UserController",
"dependencies": ["scripts/user/controllers.js", "scripts/user/services.js", "scripts/home/directives.js"]
}
]
app.factory('routingConfig', ['$resource',
function ($resource) {
return $resource('resources/routing-config.yourLangage', {}, {
query: {method: 'GET',
params: {},
isArray: true,
transformResponse: function (data) {
// before that we give the states data to the app, let's load all the dependencies
var states = [];
angular.forEach(angular.fromJson(data), function(value, key) {
value.resolve = {
deps: ['$q', '$rootScope', function($q, $rootScope){
// this will be resolved only when the user will go to the relative state defined in the var value
var deferred = $q.defer();
/*
now we need to load the dependencies. I use the script.js javascript loader to load the dependencies for each page.
It is very small and easy to be used
http://www.dustindiaz.com/scriptjs
*/
$script(value.dependencies, function(){ //here we will load what is defined in the dependencies field. ex: "dependencies": ["scripts/user/controllers.js", "scripts/user/services.js", "scripts/home/directives.js"]
// all dependencies have now been loaded by so resolve the promise
$rootScope.$apply(function(){
deferred.resolve();
});
});
return deferred.promise;
}]
};
states.push(value);
});
return states;
}
}
});
}]);
app.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', '$filterProvider', '$provide', '$compileProvider',
function ($stateProvider, $urlRouterProvider, $locationProvider, $filterProvider, $provide, $compileProvider) {
// this will be the default state where to go as far as the states aren't loaded
var loading = {
name: 'loading',
url: '/loading',
templateUrl: '/views/loading.html',
controller: 'LoadingController'
};
// if the user ask for a page that he cannot access
var _404 = {
name: '_404',
url: '/404',
templateUrl: 'views/404.html',
controller: '404Controller'
};
$stateProvider
.state(loading)
.state(_404);
// save a reference to all of the providers to register everything lazily
$stateProviderRef = $stateProvider;
$urlRouterProviderRef = $urlRouterProvider;
$controllerProviderRef = $controllerProvider;
$filterProviderRef = $filterProvider;
$provideRef = $provide;
$compileProviderRef = $compileProvider;
//redirect the not found urls
$urlRouterProvider.otherwise('/404');
}]);
app.run(function ($location, $rootScope, $state, $q, routingConfig) {
// We need to attach a promise to the rootScope. This will tell us when all of the states are loaded.
var myDeferredObj = $q.defer();
$rootScope.promiseRoutingConfigEnd = myDeferredObj.promise;
// Query the config file
var remoteStates = routingConfig.query(function() {
angular.forEach(remoteStates, function(value, key) {
// the state becomes the value
$stateProviderRef.state(value);
});
// resolve the promise.
myDeferredObj.resolve();
});
//redirect to the loading page until all of the states are completely loaded and store the original path requested
$rootScope.myPath = $location.path();
$location.path('/loading'); //and then (in the loading controller) we will redirect to the right state
//check for routing errors
$rootScope.$on('$stateChangeError',
function(event, toState, toParams, fromState, fromParams, error){
console.log.bind(console);
});
$rootScope.$on('$stateNotFound',
function(event, unfoundState, fromState, fromParams){
console.error(unfoundState.to); // "lazy.state"
console.error(unfoundState.toParams); // {a:1, b:2}
console.error(unfoundState.options); // {inherit:false} + default options
});
});
app.controller('LoadingController', ['$scope', '$location', '$rootScope',
function($scope, $location, $rootScope) {
//when all of the states are loaded, redirect to the requested state
$rootScope.promiseRoutingConfigEnd.then(function(){
//if the user requested the page /loading then redirect him to the home page
if($rootScope.myPath === '/loading'){
$rootScope.myPath = '/home';
}
$location.path($rootScope.myPath);
});
}]);
$stateProvider.state('index', {
abstract: true,
url: "/index",
views: {
'': {
templateUrl: "views/content.html" // a base template to have sections replaced via ui-view elements
}
},
...
});
$ocLazyLoadProvider.config({
loadedModules: ['futureStates'],
modules: [
{
name: 'module1',
files: ['module1/module.js']
},
{
name: 'module2',
files: ['module2/module.js']
}
]
});
function ocLazyLoadStateFactory($q, $ocLazyLoad, futureState) {
var deferred = $q.defer();
// this loads the module set in the future state
$ocLazyLoad.load(futureState.module).then(function () {
deferred.resolve();
}, function (error) {
deferred.reject(error);
});
return deferred.promise;
}
$futureStateProvider.stateFactory('ocLazyLoad', ['$q', '$ocLazyLoad', 'futureState', ocLazyLoadStateFactory]);
$futureStateProvider.futureState({
'stateName': 'index.module1', // the state name
'urlPrefix': '/index/module1', // the url to the state
'module': 'module1', // the name of the module, configured in ocLazyLoad above
'type': 'ocLazyLoad' // the future state factory to use.
});
$futureStateProvider.futureState({
'stateName': 'index.module2',
'urlPrefix': '/index/module2',
'module': 'module2',
'type': 'ocLazyLoad'
});
$futureStateProvider.addResolve(['$http', function ($http) {
return $http({method: 'GET', url: '/url'}).then(function (states) {
$futureStateProvider.futureState({
'stateName': 'index.module2',
'urlPrefix': '/index/module2',
'module': 'module2',
'type': 'ocLazyLoad'
});
});
}]);
$stateProvider.state('index.module1', {
url: "/module1",
abstract: true,
resolve: {
loadFiles: ['$ocLazyLoad', function($ocLazyLoad){
return return $ocLazyLoad.load(['list of all your required files']);
}]
}
})
$stateProvider.state('index.module1.sub1', {
url: "/sub1",
views: {
// override your ui-views in here. this one overrides the view named 'main-content' from the 'index' state
'main-content@index': {
templateUrl: "module1/views/sub1.html"
}
}
})