Angular 2 中的动态路由加载失败。 (测试版)
Posted
技术标签:
【中文标题】Angular 2 中的动态路由加载失败。 (测试版)【英文标题】:Dynamic Route Loading in Angular 2 Fails. (Beta) 【发布时间】:2016-06-23 13:27:09 【问题描述】:我想从以 JSON 格式获取的服务中动态加载 @RouteConfig 的路由,
[
"path" : "/about" , "name" : "About" , "component" : "AboutComponent" ,
"path" : "/contact" , "name" : "Contact" , "component" : "ContactComponent"
]
以下是将其推入 RouteDefinition Array 的代码,
for (let i = 0; i < data.length; i++)
//console.log(data[i].path+" "+data[i].name+" "+data[i].component);
this.routeConfigArray.push( //routeConfigArray : RouteDefinition
'path': data[i].path,
'name': data[i].name,
'component': data[i].component
);
this._router.config(this.routeConfigArray); // THIS FAILS TO CONFIG PATHS ON ROUTER
'component':data[i].component
需要 Classname,因为它通过 String 类接收它。如何将包含类名的字符串转换为类??
我也尝试过使用 Route 类:
this.routeConfigArray.push(new Route(path: data[i].path, name: data[i].name, component:data[i].component));
控制台错误:
路由“/about”的组件未定义,或者不是类。 我尝试了很多方法,比如使用
eval("new "+data[i].component+"()); || new window[data[i].component] .
我被困在这个问题上,对如何解决这个问题感到非常困惑。
【问题讨论】:
你试过eval(data[i].component)
吗?问题在于,除了在外部数据上使用eval()
之外,它还需要该组件的import
语句以及import AboutComponent from "path/to/component/about.component";
。而且我不确定你怎么能做到这一点..
@PierreDuc 我确实有import AboutComponent from './about/about.component';
但是做var testClassName =eval(data[i].component);
会导致错误AboutComponent 没有定义
好吧,试试eval(data[i].component)
:)
对不起@PierreDuc,但它导致了同样的事情,假设我做一个var testClassName = new AboutComponent(); console.log(testClassName.constructor.name)
正确输出到 AboutComponent 而不是以前不想要的版本 string
啊,我明白为什么eval
不起作用了。如果您查看生成的js
,您可以看到它导入了AboutComponent
,可能类似于about_component_1.AboutComponent
。因此,如果您可以将 camelCase 转换为下划线。全部小写,并添加_1
。将该结果放入名为something
的变量中。你可以做eval(something[data[i].component])
。显然,编译后的 javascript 可能会发生变化,这可能是一个非常糟糕的方法,但您可以尝试 :D。虽然,编译器可能不会创建该变量,因为您并没有真正使用该组件
【参考方案1】:
更新到上述第一个场景: 以上动态加载了 UI 上的路由,但要做到这一点,我必须在我的 AppComponent 中提及它
import AboutComponent from '/path';
import ContactComponent from '/path';
//and then either include it in a directive or mention The component Name some place in the code
但这违背了“动态加载”的目的,因为我必须知道将请求哪些组件并且也不能延迟加载它们。假设我为 500 个组件执行此操作,我必须加载这 500 个组件,然后仅从上面提到的 JSON 中选择必须加载到 UI 上的 JSON。
解决方案(已完成并测试)==> 我现在不必在任何指令中提及我想在 任何导入语句 中加载的组件。 因此使其延迟加载并完全动态
如何 通过使用 AsyncRoute 类。
这是我的新 JSON //"route"是映射到[routerLink]="['/Home']" &"resource"是组件的实际路径
[
"path" : "/about" , "name" : "About" , "component" : "AboutComponent", "route": "/About" , "resource" : "./app/about/about.component" ,
"path" : "/contact" , "name" : "Contact" , "component" : "ContactComponent", "route": "/About" , "resource" : "./app/about/about.component" ,
"path" : "/blog" , "name" : "Blog" , "component" : "AboutComponent", "route": "/About" , "resource" : "./app/about/about.component" ,
"path" : "/news" , "name" : "News" , "component" : "AboutComponent", "route": "/About" , "resource" : "./app/about/about.component"
]
现在到代码,这里我获取这个 JSON 并将它添加到 routeConfigArray : RouteDefinition[] 并调用路由器的 .config(routeConfigArray)
let routes : any[] = data; // consist the json data in array
routes.forEach((route : any) =>
this.routeConfigArray.push(
new AsyncRoute(
path : route.path,
loader : () => System.import(route.resource).then(m => m[route.component]),
name : route.name
)
);
);
this._router.config(this.routeConfigArray);
这就是它的工作原理!!!
【讨论】:
我在我的答案中添加了一个附录。我相信这可能是您正在寻找的东西【参考方案2】:TypeScript 以某种方式将导入编译成 javascript,您可以尝试这样的方法来使用eval()
(畏缩)。如果 typescript 编译不同,这显然不起作用,但看看这是否可行:)
for (let i = 0; i < data.length; i++)
this.routeConfigArray.push( //routeConfigArray : RouteDefinition
path: data[i].path,
name: data[i].name,
component: getComponent(data[i].component)
);
this._router.config(this.routeConfigArray);
function getComponent(comp : string) : Function
//convert camelCase to underscore notation
let component : string = comp;
component = component[0].toLowerCase() + component.slice(1);
component = component.replace(/([A-Z])/g, function(match)
return '_' + match.toLowerCase();
);
component += '_1';
return eval(component[comp])
附录
除了您自己使用AsyncRoute
的解决方案外,我相信您实际上得到了一个很好的解决方案。也许如果您以某种方式放置所有页面,您可以从path
中提取resource
位置,但这不是必需的。 (我的意思是从path
字符串/about
到resource
字符串./app/about/about.component
使用小算法应该不难。但这可能需要更新。
无论如何,您可以使用AsyncRoute
尝试类似的操作
警告:前面有未经测试的代码
let routes : any[] = [
"path" : "/about" , "name" : "About" , "component" : "AboutComponent", "route": "/About" , "resource" : "./app/about/about.component" ,
"path" : "/contact" , "name" : "Contact" , "component" : "ContactComponent", "route": "/Contact" , "resource" : "./app/contact/contact.component"
];
routes.forEach((route : any) =>
this.routeConfigArray.push(
new AsyncRoute(
path : route.path,
loader : () => System.import(route.resource).then(m => m[route.component]),
name : route.name
)
);
);
this._router.config(this.routeConfigArray);
【讨论】:
@PratikKelwalkar 欢迎您。angular2
的快乐编码 :)
@PratikKelwalkar 做得好。只是一点提示。无需再添加CORE_DIRECTIVES
:) 这些会自动添加
是的 :) 有点跑题了,你什么时候期望角 2 的 RC?
嘿,随着 Angular RC 1 的发布,AsyncRoute 类已被删除,现在知道如何进行延迟加载了吗?
我们如何在 .ts 文件中导入系统组件?【参考方案3】:
您可以使用来自@Pierre 和@Pratik 的另一种方法,该方法基于返回类名称的方法:
Object.prototype.getName = function()
var funcNameRegex = /function (.1,)\(/;
var results = (funcNameRegex).exec((this).constructor.toString());
return (results && results.length > 1) ? results[1] : "";
;
在您的组件中,您可以像这样动态配置路由:
ngOnInit()
this.routes = [
path: '/test', component: 'OtherComponent', name: 'Test'
];
this.configureRoutes(this.routes);
this.router.config( this.routes);
configureRoutes(routes)
var potentialComponents = [ OtherComponent ];
routes.forEach((route) =>
route.component = potentialComponents.find((component) =>
return component.name === route.component;
);
);
这需要提前了解路由中可能涉及的潜在组件。
查看此 plunkr 进行演示:https://plnkr.co/edit/KKVagp?p=preview。
看到这个问题:
How do I get the name of an object's type in JavaScript?【讨论】:
太棒了! .. plunkr 示例帮助很大。使用 angular 2 ,带我回到学校! ... 嘿,根据每个人的输入...动态路由不会是完全动态的,因为必须使用 import ComponentName from 'path/to/component 或其他地方上面提到的potenttailComponents中的代码对吗?但是我几乎找到了一种方法,无需使用任何导入或在指令或其他任何地方提及组件,使用 AsyncRoute 我几乎做到了,但再次陷入困境,将发布代码,任何帮助将不胜感激 如何加载 2 条新路线?我有这个 plunkr:plnkr.co/edit/gEG6TJ?p=preview【参考方案4】:你太好了@PierreDuc,我只是在看正则表达式来构建相同的功能,我想指出一些编辑以使其处于工作状态......
for (let i = 0; i < data.length; i++)
this.routeConfigArray.push( //routeConfigArray : RouteDefinition
'path': data[i].path,
'name': data[i].name,
'component': getComponent(data[i].component).constructor
);
this._router.config(this.routeConfigArray);
function getComponent(comp : string) : Function
//convert camelCase to underscore notation
let component : string = comp;
component = component[0].toLowerCase() + component.slice(1);
component = component.replace(/([A-Z])/g, function(match)
return '_' + match.toLowerCase();
);
component += '_1.';
return eval("new "+component+comp+"()")
再次感谢老兄,它现在处于运行模式!!!呸!
【讨论】:
不客气!很高兴我能帮助你。虽然我希望eval(component[comp])
已经返回构造函数,并且首先使用 new
创建它然后获取构造函数是矫枉过正.. :)以上是关于Angular 2 中的动态路由加载失败。 (测试版)的主要内容,如果未能解决你的问题,请参考以下文章
使用启用 Html5 模式的 Angular Ui 路由器时页面重新加载失败