如何使用 angular-ui-router 在一个请求中延迟加载控制器和模板
How to Lazyload controller and template in one request using angular-ui-router
我正在尝试延迟加载组件。该组件是一个 html 片段,带有包含控制器的嵌入式脚本标签。
<script>
... controller code .....
</script>
<div>
... template ....
</div>
该片段是在一个 html 请求中生成的,所以我不能在状态定义中使用 templateUrl 和 componentURL。
我尝试使用 templateProvider 获取组件,而不是提取函数的脚本代码并使用对 controllerProvider 的引用进行注册。
我相信一定有比我想出的丑陋解决方案更好的方法来做到这一点。我对 controllerpovider 进行全局引用,然后使用 getComponent 服务通过 templateProvide 读取组件。接下来我提取脚本并对其进行评估,这也注册了控制器。
请参阅 plunker 了解我尝试解决此问题的方法。
.factory('getComponent', function($http, $q) {
return function (params) {
var d = $q.defer();
// optional parameters
$http.get('myComponent.html').then(function (html) {
// the component contains a script tag and the rest of the template.
// the script tags contain the controller code.
// first i extract the two parts
var parser = new window.DOMParser();
var doc = parser.parseFromString(html.data, 'text/html');
var script = doc.querySelector('script');
// Here is my problem. I now need to instantiate and register the controller.
// It is now done using an eval which of cours is not the way to go
eval(script.textContent);
// return the htm which contains the template
var html = doc.querySelector('body').innerHTML;
d.resolve(html);
});
return d.promise;
};
})
也许可以使用 templateProvider 和 controllerProvider 来完成,但我不确定如何通过一个 http 请求解决这两个问题。任何帮助/想法将不胜感激。
- 您在运行时无权访问 $controllerProvider,因此您无法注册命名控制器。
- UI-路由器不需要named/registered控制器功能。
{ controller: function() {} }
在状态定义中完全有效
- 但是,如果您需要注册控制器,您可以使用 ocLazyLoad 之类的工具来注册它。
- UI-路由器将控制器链接到模板,因此不需要
ng-controller
散布在 html。
下面是我如何破解这个的。 getController
工厂现在保留 "promises for components" 的缓存。我保留了您的评估和模板解析以将响应分成两部分。我用一个包含 ctrl/template.
的对象解决了这个承诺
组件工厂
.factory('getComponent', function($http, $q) {
var components = {};
return function (name, params) {
if (components[name])
return components[name];
return components[name] = $http.get(name + '.html').then(extractComponent);
function extractComponent(html) {
var parser = new window.DOMParser();
var doc = parser.parseFromString(html.data, 'text/html');
var script = doc.querySelector('script');
// returns a function from the <script> tag
var ctrl = eval(script.textContent);
// return the htm which contains the template
var tpl = doc.querySelector('body').innerHTML;
// resolve the promise with this "component"
return {ctrl: ctrl, tpl: tpl};
}
};
})
myComponent.html
<script>
var controller = function($scope) {
$scope.sayHi = function() {
alert(' Hi from My controller')
}
};
// This statement is assigned to a variable after the eval()
controller;
</script>
// No ng-controller
<div>
Lazyloaded template with controller in one pass.
<button ng-click="sayHi()">Click me to see that the controller actually works.</button>
</div>
状态定义中:
- 我创建了一个名为 'component' 的解决方案。该解析要求
getComponent
工厂获取名为 'myComponent' 的组件(提示:'myComponent' 可能是路由参数)
- 准备就绪后,解析将公开给 UI-路由器状态子树。
- 状态已激活
- 视图已初始化
- 控制器提供者和模板提供者将解析的
component
和 return controller/template 注入到 UI-Router。
气味测试
我应该提到,在单个 html 文件中获取控制器和模板并手动解析对我来说是错误的。
您可以追求的一些变化:
- 改进getComponent工厂;将组件拆分为 html/js 并分别获取每个组件。使用 $q.all 等待两次提取。
- 使用 ocLazyLoad 向 $controllerProvider 全局注册控制器
- 获取模板并将其存储在 $templateCache 中。
我正在尝试延迟加载组件。该组件是一个 html 片段,带有包含控制器的嵌入式脚本标签。
<script>
... controller code .....
</script>
<div>
... template ....
</div>
该片段是在一个 html 请求中生成的,所以我不能在状态定义中使用 templateUrl 和 componentURL。
我尝试使用 templateProvider 获取组件,而不是提取函数的脚本代码并使用对 controllerProvider 的引用进行注册。
我相信一定有比我想出的丑陋解决方案更好的方法来做到这一点。我对 controllerpovider 进行全局引用,然后使用 getComponent 服务通过 templateProvide 读取组件。接下来我提取脚本并对其进行评估,这也注册了控制器。
请参阅 plunker 了解我尝试解决此问题的方法。
.factory('getComponent', function($http, $q) {
return function (params) {
var d = $q.defer();
// optional parameters
$http.get('myComponent.html').then(function (html) {
// the component contains a script tag and the rest of the template.
// the script tags contain the controller code.
// first i extract the two parts
var parser = new window.DOMParser();
var doc = parser.parseFromString(html.data, 'text/html');
var script = doc.querySelector('script');
// Here is my problem. I now need to instantiate and register the controller.
// It is now done using an eval which of cours is not the way to go
eval(script.textContent);
// return the htm which contains the template
var html = doc.querySelector('body').innerHTML;
d.resolve(html);
});
return d.promise;
};
})
也许可以使用 templateProvider 和 controllerProvider 来完成,但我不确定如何通过一个 http 请求解决这两个问题。任何帮助/想法将不胜感激。
- 您在运行时无权访问 $controllerProvider,因此您无法注册命名控制器。
- UI-路由器不需要named/registered控制器功能。
{ controller: function() {} }
在状态定义中完全有效 - 但是,如果您需要注册控制器,您可以使用 ocLazyLoad 之类的工具来注册它。
- UI-路由器不需要named/registered控制器功能。
- UI-路由器将控制器链接到模板,因此不需要
ng-controller
散布在 html。
下面是我如何破解这个的。 getController
工厂现在保留 "promises for components" 的缓存。我保留了您的评估和模板解析以将响应分成两部分。我用一个包含 ctrl/template.
组件工厂
.factory('getComponent', function($http, $q) {
var components = {};
return function (name, params) {
if (components[name])
return components[name];
return components[name] = $http.get(name + '.html').then(extractComponent);
function extractComponent(html) {
var parser = new window.DOMParser();
var doc = parser.parseFromString(html.data, 'text/html');
var script = doc.querySelector('script');
// returns a function from the <script> tag
var ctrl = eval(script.textContent);
// return the htm which contains the template
var tpl = doc.querySelector('body').innerHTML;
// resolve the promise with this "component"
return {ctrl: ctrl, tpl: tpl};
}
};
})
myComponent.html
<script>
var controller = function($scope) {
$scope.sayHi = function() {
alert(' Hi from My controller')
}
};
// This statement is assigned to a variable after the eval()
controller;
</script>
// No ng-controller
<div>
Lazyloaded template with controller in one pass.
<button ng-click="sayHi()">Click me to see that the controller actually works.</button>
</div>
状态定义中:
- 我创建了一个名为 'component' 的解决方案。该解析要求
getComponent
工厂获取名为 'myComponent' 的组件(提示:'myComponent' 可能是路由参数) - 准备就绪后,解析将公开给 UI-路由器状态子树。
- 状态已激活
- 视图已初始化
- 控制器提供者和模板提供者将解析的
component
和 return controller/template 注入到 UI-Router。
- 控制器提供者和模板提供者将解析的
气味测试
我应该提到,在单个 html 文件中获取控制器和模板并手动解析对我来说是错误的。
您可以追求的一些变化:
- 改进getComponent工厂;将组件拆分为 html/js 并分别获取每个组件。使用 $q.all 等待两次提取。
- 使用 ocLazyLoad 向 $controllerProvider 全局注册控制器
- 获取模板并将其存储在 $templateCache 中。