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 字符串/aboutresource 字符串./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 路由器时页面重新加载失败

如何处理 angular2 延迟加载路由失败

Angular2:无路由的延迟加载和动态加载

Angular 中动态编译的延迟加载动态路由导致“不安全评估”错误

Angular 2 的异步加载路由数据和构建路由指令

Angular的内置模块