Angular: 动态加载组件的模板

创建于 2017-03-18  ·  194评论  ·  资料来源: angular/angular

我正在提交一个
[X] 功能请求

我有一个提供一些通用业务逻辑的组件。 让我们说:

  • CRUD 功能
  • 产品管理(产品目录)
  • 跨部门业务管理(每个部门单独布局)
  • ...

根据条件,我想显示特定于某种产品、与用户部门等相关的布局(模板)。
现在我无法即时加载模板来执行此操作。
我已经阅读了这里和 stackoverflow 上的所有问题,以了解这样做的可能方法。

最实用的方法是 ngTemplateLayout。 但在现实生活中,如果您有几十种不同的布局类型,它会破坏您的模板文件并使其无法维护。

如果您使用 angular 团队最推荐的方式 dynamiccomponentloader ,即使封装了组件和模块创建的功能,它也会为动态生成组件增加巨大的代码开销。 此外,它不允许真正的通用解决方案,因为在动态创建的 ngModule 中,您必须提供所有导入、导出、提供程序……以使组件工作。 因此,如果您不想在通用组件构建器中产生巨大的开销,则必须为表单类型的每种“类型”实现一个通用组件构建器。 这是不切实际的,并增加了在下一个角度发布后重新编码的危险

预期行为
提供一种为组件动态加载模板的方法。 因为它在 Angular 1 中可用
请不要在这个问题上教条主义,并考虑在 Angular 2 中支持现实世界的需求
谢谢

core feature

最有用的评论

我知道模板现在不能是动态的,正如我所概述的那样,这很痛苦。 我不想用相同的逻辑实现两个、更多或数百个组件。
这就是我请求此功能的原因!

所有194条评论

为什么不只拥有两个组件,然后根据您需要的情况加载任何一个组件。 例如: AdminDisplayComponentNotAdminDisplayComponent ?。
无法使模板动态化,模板必须绑定到组件

我知道模板现在不能是动态的,正如我所概述的那样,这很痛苦。 我不想用相同的逻辑实现两个、更多或数百个组件。
这就是我请求此功能的原因!

@KarlXOL我的意思是不可能在不破坏 AOT 的情况下使其动态化。
至于您的问题的解决方案,如果您想共享公共代码,那为什么不使用服务呢? 或者,如果您认为服务不会创建组件,则从它继承

@DzmitryShylovich :我知道您不想提供此功能。 但正如我试图解释这样做的所有其他方法一样,都是拐杖,不切实际。
如果我通过使用 angular 特性来做到这一点,我还必须使用同样破坏 AOT 功能的编译器。 对? 所以呢。
请不要教条化并重新考虑您的方法。 我/我们正在提供解决方案,需要一个支持我们这样做的平台。

@Toxicable :如果您有一个关于如何在服务中封装例如 CRUD 功能的示例,我想看看。 即使这是可能的,也需要为每个例如产品实现一个组件。

@KarlXOL我不明白为什么不能将 CRUD 功能实现为服务。 可能这是最好的方法和常见的模式。

如果我理解正确,您根本不关心 AOT,并且希望在运行时加载 Compiler 模块的 JIT 模式下完成所有操作。 那么你现在就可以做到。 在https://www.ag-grid.com/ag-grid-angular-aot-dynamic-componentshttps://eyalvardi.wordpress.com/2016/09/04/injecting-components-in查看 eaxmple http://blog.assaf.co/angular-2-harmony-aot-compilation-with-lazy-jit-2。

@mlc-mlapis :当然,实际的数据操作(新建、更新等)在服务中。 但是它们是由作为组件一部分的用户交互(单击,...)触发的。

您所指的链接仅显示了如何处理多个组件。 这就是重点,我不想为红色、绿色、蓝色设置多个组件。 我只需要一个组件并更改模板/布局。 在我的功能请求中,我解释了原因

@KarlXOL但是这个例子正是你想要的,是或否(理解你的观点)? https://eyalvardi.wordpress.com/2016/09/04/injecting-components-in-runtime

是的,点击,......在那里,但不明白你为什么提到它们,因为你的组件通过 DI 注入服务(它应该是一个单例)并将提供必要的接口。

当然,实际的数据操作(新建、更新等)都在服务中。 但是它们是由作为组件一部分的用户交互(单击,...)触发的。

@mlc-mlapis 这是我在请求(“dynamiccomponentloader”)中提到的可能解决方案之一。 这种方法的缺点是
1) 每个具有动态内容的组件都需要很多额外的代码行
2)动态模块创建需要适应(导入)以使其工作(指令)
因此,您拥有一个开销巨大的动态模块,或者您实现了多个动态模块构建器

总而言之,这个解决方案是重量级的、非常低级的、有风险的,id 需要随着 angular 的发展而适应,......并且它与 Angular 1 为这个要求提供的精益、快速的方法没有竞争力。
试想一下,您想为属于不同部门的用户呈现不同的“标题”,仅举一个简单的例子。

这就是为什么我要求该功能即时更改/加载模板。 框架应该处理这些低级的东西,而不是业务应用程序。

你的意思现在很清楚了。 我可以想象这种简化并具有如此高水平的功能。 还需要说明的是,这仅适用于 JIT 模式。

实际上,Angular 中没有任何attribute会在 JIT 和 AOT 模式之间区分其功能。 有stableexperimentaldepreciated

另一个问题是这些功能在时间线中的优先级以及 Angular 核心团队的立场。

@mlc-mlapis 感谢您的反馈。 我很高兴我能够传达信息:-)

我从这里的许多其他问题中了解到,stackoverflow 非常需要此功能,因为它是实际业务应用程序中非常常见的要求。 在过去,甚至在未来。

@DzmitryShylovich我从 ng-descendant 提案中看不出这将是我请求的解决方案。

我建议您重新考虑 Angular 架构方法,使其更灵活,更适用于大型企业应用程序场景。 对于 Angular 框架和团队来说,为组件动态加载模板并不是不可能完成的任务。

@KarlXOL企业应用场景是AOT存在的原因。 😄 动态模板只是快捷方式和cheaper解决方案。 因此,关于重新思考的建议可能是不切实际的。

@mlc-mlapis 我期待这样的答案。 我越是想知道为什么 angular 团队没有提供企业所需的东西。 使应用程序更快、更便宜是企业环境中的关键。 但是您确认这不是您支持 angular 2 的内容。

@KarlXOL出于好奇,您是否有关于 Google 以及如何开发其全球应用程序的任何信息?

@mlc-mlapis 不确定。 是 Angular 1 还是 Angular 2??

@KarlXOL因为这不是企业唯一需要的东西,仅仅因为它是您想做的事情并不意味着它是每个人都需要的东西。 他们最近所做的一切都是让应用程序更快更容易制作应用程序,只需看看从 v2 -> v4 的捆绑缩减。
我建议重新考虑您如何尝试实现目标,在 AngularJS 中工作的方法并不总是在 Angular 中工作的最佳方法。

抄送@robwormald

@KarlXOL不,我的意思是互联网上的大部分made-in谷歌。 看: https :

@KarlXOL这是我制作的一个小例子https://plnkr.co/edit/kz2XKSKWSWZhPoncDQhG?p=preview
您要做的就是提供 UI/config,然后连接到方法中以访问后端

@Toxicable我很欣赏你的笨拙。 我的实现看起来像这样。 应用程序的这一部分与我在最初的帖子中解释的实际问题无关。

@Toxicable你是对的。 我的帖子关于服务不准确。

@KarlXOL我没有得到你想要的。 我不明白如何能够即时交换模板比使用这样的通用设置更好。 因为有了这个,你只需要定义一个只有 20 LOC 的组件

@mlc-mlapis 感谢您在 Web 开发方面的课程。 正如你提到的,它是关于谷歌如何开发网络框架的。 我不得不承认,我的要求是从如何为企业提供业务解决方案以及如何利用工具和框架来做到这一点的角度出发的。 除了您提到的开发框架之外,还有一些其他要求和需求。 如果您也能给这些要求一些估价,我将不胜感激。 最后,我们都将从彼此中受益更多,而 angular 2 可能是一个更强大的框架。

@Toxicable您的示例不是关于动态交换模板,而是用于提供数据访问服务交换模板的所有其他实现(实际上不支持)都是通过动态模块/组件创建完成的,这要复杂得多,而且成本更低-水平角度实现。

@KarlXOL是的,我知道,我的示例根本不是关于换出模板。 你说:

如果您有一个关于如何在服务中封装例如 CRUD 功能的示例,我想看看。 即使这是可能的,也需要为每个例如产品实现一个组件。

这就是我所做的。 我为您的问题提供了一个解决方案,而不是以您想要的方式解决它,但它仍然是一个解决方案。 但是,我的观点仍然存在; 如果可能的话,我看不到通过交换模板获得什么,在这两种情况下,您仍然必须编写模板代码,那么您究竟从大量的工作中获得了什么甚至需要在框架中实现类似的东西?

@Toxicable准确。 你明白了。 我只需要编写模板代码。 所有其他代码保持不变!! 想象一下主数据管理的一般实现,包括上面的服务。 只需更改模板即可实现另一个表(业务对象)。 至少这在 Angular 1 中是可能的。

@KarlXOL如果您在大范围内应用该原则,这实际上是一个问题。 因为很多代码是在运行时编译的,你无法进行适当的测试,所以你只能在运行时发现很多隐藏的错误。 这是不可接受的。 您只能在小范围内使用该概念,但仍然会面临很多不可预测性。

只需更改模板即可实现另一个表(业务对象)。

@mlc-mlapis 当然,你是对的。 但是因此您必须从 angular 2 中删除编译器,因为原则上我已经可以实现我想要做的事情。 简直太复杂了。

我过分强调您的论点,但它也包括您正在使用应用程序处理的所有类型的数据(数据库等)。 您无法测试所有内容,并且总会有一些事情留给应用程序经理负责。
回到我们的讨论,在我看来,需要的是实用主义和架构愿景的良好结合。 愿景是给定的,这是肯定的。 一些实用主义也可能是 angular 2 的胜利。我的要求不是异国情调,很多人都需要。

@KarlXOL我同意你的一些论点。 但另一方面,我也看到了你应该重新思考你的应用程序概念的地方,因为你会得到更好的应用程序。

我们的讨论可能已经结束,因为从这个角度来看,Angular 核心团队的立场很重要。 我只是想表达一些背景论据,为什么现在 AOT 是首选方式。

我同意 KarlXOL 的选择。 在我公司的需求中,有很多UI需要动态配置。 它在 angular 1 中运行良好。当我将 angular1 迁移到 angular2 时,它变得很糟糕。 最后,我找到了 DynamicComponentLoader 并使用它来解决问题。 但随着最近越来越多的客户抱怨,表现并不令人满意。

我在我的公司进行了讨论。 我公司的大多数开发人员都认为 Angular2 做了很多静态(可控)的事情。 它限制了开发人员使用动态事物,例如动态模板、动态更改语言等。我个人认为它会失去灵活性。 我还强烈推荐 angular 2 提供这些更容易接近的动态功能。

为什么需要不同的模板而不是不同的组件? 也许您错过了可以在 Angular 中继承组件? 例如,请参见此处https://medium.com/@amcdnl/inheritance -in-angular2-components-206a167fc259

谢谢你的推荐。 但解决方案不是我想要的。 我的场景是视图模板由设计人员或非开发人员动态配置,并保存在数据库中。 大多数情况下逻辑是固定的。 只是想要重新设计布局或添加/减去字段的要求。
这是 CRM/ERP 领域中非常常见的行为。 像odoo一样拖放字段为不同的行业生成不同的表格。

好的。 然后,您唯一需要做的就是将您描述的用户定义模板编译为新组件……当然是在服务器上……这会创建一个包含该新组件的新模块……将标志保存到一些配置说......这个细节应该由那个新组件呈现......并在适当的时候按需加载新模块。

@mlc-mlapis
如果您仔细阅读此功能请求和线程,您会发现它提供了一个更好的解决方案作为您提议的解决方案,并且还避免了此解决方案方法所涉及的痛苦和混乱。
@robert-luoqing 很好地解释了非常常见的场景和对这个功能的需求。

@KarlXOL我知道。 唯一不明白的是,自定义模板的唯一JiT动态编译今天已经可用并且它一直在这里......但上面的公式说缺少这个功能。

很明显,他们当时不能使用 AoT,他们可能明白为什么不能。

因此,桌面上唯一的东西是some more user friendly Angular APIs在 JiT 模式下进行那些动态模板编译,对吗? 即使不兼容 AoT 模式。

@mlc-mlapis
我需要提供有效的、可维护的解决方案来支持业务需求。 Angular 2 是一个框架,它通过提供非常复杂的软件技术为此奠定了基础。 这就是我(仍在)使用它的原因。
另一方面,angular 缺少一些“用户友好性”来支持非常常见的要求。 例如我们在这里讨论的那个。 错误消息(开发和运行时)、UI 框架等也存在改进空间。

能够提供解决方案对我来说比你一直在考虑的更重要:AoT、静态类型检查、摇树……!!! (例如 JIT 通常足够快)
我知道这当然很重要,但有时我的印象是您忘记了“普通”开发人员需要什么。

好的,那我们就互相理解了。 其他事情是优先事项和对不同类型项目重要和不重要的一般意见,因为有数百种可能的情况。

这个功能会很棒。 我对我正在做的 ERP 项目有类似的要求,不幸的是,仅仅因为它,我不能像我想的那样使用 Angular。

我也赞同这个功能。
我们有一个企业内容管理系统,我们可以在其中提供数据库中的页面。 最终用户可以使用 HTML 文本编辑器修改 HTML 文件,并且可以执行 n 项任务。
我们想使用 Angular 重新开发我们的客户端应用程序,但是这个功能阻止了我们。

@asadmalik3您可以拥有动态 HTML,只是无法轻松使用动态模板。 回想一下,模板只是 JS 代码,而不是 HTML,要在 AOT 中运行它们,它们需要被编译。
因此,对于 CMS 类型系统,如果您允许用户在不添加任何新的 Angular 组件、管道、指令或服务的情况下编辑 HTML,那么您可以 100% 做到这一点。 您甚至可以在运行时使用现有组件,并将它们替换为应用程序中已编译的工作组件。 这方面的例子是 angular.io 参见https://github.com/angular/angular/blob/master/aio/src/app/layout/doc-viewer/doc-viewer.component.ts
Angular 中的事情从 AngularJS 变得更好了,像 AOT 这样的东西你的应用程序会更快,并提供更好的用户体验,但在某些情况下,你只需要从另一个角度考虑问题,而不是期望它完全一样工作就像以前一样。

@Toxicable 动态创建组件的性能影响是什么? 我们开发了一个动态创建组件的示例,并希望在将其用于生产之前评估其性能。

首先要考虑的是依赖关系。 动态创建具有不同依赖项的组件可能会导致问题。
其次,当您在浏览器中编译 Angular 代码时,您将使用 100% 的用户处理器。
第三,你将永远厌倦缓慢而庞大的 JIT Compiler
您可以通过在 JIT 模式下运行您的应用程序来看到这一点。

@Toxicable只是想知道我们如何将依赖项传递给动态创建的组件?
它是创建它的组件的责任吗? 你能告诉我一个像样的例子吗?

我们有同样的需求。 简单地说,我们允许客户以他们想要的任何方式组织数据的可视化显示。 我们使用相同的角度组件,我们只需要为每个客户端重新组织 HTML。 我们有 100 个拥有 10-20 个角色的客户端,每个角色都可以有独特的布局,相同的角度组件。 但是我们有数百个使用常见 Angular 组件的独特 HTML 模板。 这并不少见。 我们已经研究过使用 Angular 动态创建表单,这可能有效,但是缓存和准备好 HTML 模板会快得多。 我们将着眼于动态创建不仅是 HTML,而且是放入一个必须动态派生的组件中,但我不希望它会执行。 因此,如果 HTML 模板包含相同的、精确的 Angular 工件(依赖项、组件等),我应该能够更换模板以获得新的布局似乎是合理的。

@asadmalik3您将父Injector传递给动态创建的组件,以便它们可以使用任何已经存在的服务。 如果你想拥有新的服务,那么你需要加载一个模块

@glwbkfs你可以完全按照你已经提到的去做,angular.io 就是这样做的。 他们使用相同的 Angular 组件,只是在每个内容页面中以不同的方式组织,在运行时这些组件的 html 被替换为预先存在的运行组件。
一个例子可以在这里看到https://github.com/angular/angular/blob/master/aio/src/app/layout/doc-viewer/doc-viewer.component.ts
认为这个库也可以做同样的事情https://github.com/laco0416/ng-dynamic
基本上只要你不想要任何动态创建的东西,那么你就完全在 Angular 可以提供的范围内

@Toxicable我在 doc-viewer 组件中看到的预渲染 html 是从服务器返回的。 我们不想在服务器上保留预渲染的 html,我们想要获取 html 模板并在我们的组件中渲染它。 我怎样才能做到这一点?

@asadmalik3我不明白你说的预渲染 html 是什么意思?
输入文档只是您可以在任何 html 页面上找到的普通 html。
由于您可能会误解这是如何工作的,因此我做了一个关于如何在此处应用的小演示
https://plnkr.co/edit/B5aIMZ7rYCLn1WizqUnW?p=preview

如果我从服务器返回的 html 中有类似 {{title}} 的内容,angular 将无法使用您的技术解决它。

@asadmalik3不,我从来没有说过会。
做类似{{title}}事情需要编译模板,我说

基本上只要你不想要任何动态创建的东西,那么你就完全在 Angular 可以提供的范围内

这意味着如果你想编译任何它不起作用的东西,它只适用于预先存在的组件

你们很有趣,如果我必须从服务器提供静态 html,我为什么要选择 angular? 以我的拙见,您不应该与任何人分享这个文档查看器示例,因为它没有用。 在任何企业业务应用程序中,没有人会“永远”在服务器上拥有预渲染模板 (html)。 就像页面总是在不同客户端的不同数据上呈现。

我有一种强烈的感觉,继续前进,你们必须提供一些“简单”和“可接受”的方式来从服务器获取模板并在运行时编译它们。 我不希望我的应用程序 100% 超快,我会对 90% 的性能感到满意,但它应该为我提供灵活性。

@Toxicable看到这个 plunker https://embed.plnkr.co/jkWaZnfaH8bD7787BM5W/
我有产品清单和客户清单。 产品列表显示在顶部,然后是客户列表。

我的一位客户想更改订单,客户想先查看客户列表,然后再查看产品列表。 在这种情况下,我会很高兴地要求客户使用网络编辑器或类似的东西修改 html。

但是现在,如果我必须像上面解释的那样更改顺序,似乎我必须创建一个新组件来实现这一点。

所以我将有 1 个组件首先显示产品,然后是客户,第二个组件首先显示客户,然后是产品。

@asadmalik3我认为完全没有理由需要动态。 要求客户编辑 HTML?
只是为了改变顺序? 为什么?
只需制作一个 UI 元素(例如选择框)来更改顺序并将其保存到数据库中,当他们再次打开它时,加载此排序选择并完成。

这与更改数据顺序无关,而是与您为客户提供的灵活性有关。 想想当您允许您的客户编辑您的页面 html 时出现的那些可能性。 你允许他们做几乎任何事情。 如果我们的一位客户想在“客户”部分的顶部放置图片广告怎么办? 另一个想在产品部分之前显示一些重要的信息或说明? 一切都在那里,他们可以为所欲为。

最重要的是,每个客户都希望个性化他们的主题并希望根据他们的需要显示数据,我们的一些客户想要盒装布局,有些客户希望以表格结构显示数据,我们的一些客户希望在每个客户旁边显示图像产品等。我们不会坐下来进行所有这些更改,我们提供灵活性,客户可以自己完成所有这些更改。 灵活性是这里的关键。

你的意思是像这篇文章中的那样吗?
https://medium.com/@DenysVuika/dynamic -content-in-angular-2-3c85023d9c36

我读了这篇文章; “运行时编译”正是我所需要的。 但是正如KarlXOL所说,解决方案太复杂太重(编译模块需要引用其他第三个必要模块等)。 当我测试时,它也不适用于 AOT。 “我个人需要一个直接而简单的解决方案,我们可以以某种方式动态设置组件模板,但不会破坏 AOT 行为”。 这就是我要的。

BTW:我不认为“动态内容”不仅仅是模块中已经存在的内容组件,应用程序只是通过用户选择引用和显示它。 但内容组件在运行时之前是未知的。 它由用户输入或远程配置生成。

+1

能够在组件装饰器中提供 promise 或 observable 作为模板会很好。 然后我们可以有后端提供的模板。

你好,

我们目前也在寻找某种在运行时编译自定义组件的方法。 我们的用例是预览 HTML 电子邮件,其中包含使用 Angular 4 表单编辑的大量数据。

目前我们使用包含完整网页的 mustache 模板,然后我们替换所有变量,将内容放入 iframe 并更新 iframe 尺寸。

这工作正常,但会导致闪烁,因为整个 iframe 内容一次又一次地被替换。

HTML 模板是用户从预先存在的选择(由第三方提供)中选择的或由用户直接编辑的,因此我们无法使用 AOT 编译它们。

我也对其他解决方案感兴趣,目前我们正在考虑主要绕过 Angular 并将{{var_name}}替换<span id="var_name"></span> ,然后使用纯 js 替换内容。

另一方面,这迫使我们实现 Angular 旨在解决的问题,因此拥有并选择这样做会很棒,即使这意味着我们将失去 AOT 的能力。

也许这应该是一个单独的项目,以便我们可以使用 AOT 对我们的应用程序进行编码,然后添加 @angular/dynamic-templates 来添加我们所追求的功能,但让应用程序的其余部分作为 AOT 版本工作。 (起初它只是重新打包 JIT 以使其更加用户友好,但如果付出更多努力,可能会演变成完全不同的东西)

你怎么认为?

你好。 我有一个稍微不同的用例,但被告知我的功能请求与此问题重复。

在我的公司,我们有一个应用程序,允许消费者搜索可通过公共国家登记册获得的财产数据。 如果用户选择了一个属性,我们将显示包含可用数据的报告。 本报告由第三方生成,纯html(只是一张大表格)。 报告中的某些元素包含指向用户可以购买的其他信息的“原型链接”。 这些“原始链接”被清楚地标记/易于选择,并包含生成链接所需的所有信息作为属性(基本上是各种文档的唯一标识符)。 我们的应用程序目前支持 2 种语言,因此 JiT 编译将花费很长时间。

我目前的解决方案是将经过消毒的 html 绑定到组件的 innerHtml。 在 AfterViewInit 阶段,组件遍历此 html 并用锚元素替换 protolink。 一切正常,我想(有点hacky,但我对此还算满意)。 但是,如果我可以使用 routerLinks,感觉会更好(我可能对这些事情有点自闭)。
导航只发生在 singlePage 应用程序中,并且页面刷新只是因为让我成为一个悲伤的熊猫。

理想情况下,我想我有办法以某种方式声明一个组件和指令(如 routerlink)的白名单,我可以用它来编译一些受信任的 html。 必须可以通过 AoT 编译来做到这一点。 感觉这应该可以通过一些 we_advise_you_not_to_do_this_in_production-kinda 方式实现,但我还没有找到方法。

这对我的应用程序 atm 来说并不重要(可能唯一会记录差异的人是我),我对 angular-2 变成的辉煌框架感到非常高兴:-)。

@eliasre ...以及如何使用与实际http://angular.io站点相同的原理,其中纯 HTML 也以某种方式进行解析。 在此过程中,会查找已知组件的字典,并在这些位置动态实例化 Angular 原生组件。 所以你可以使用它们中的任何一个......以及它们所有的内部功能等等......

@mlc-mlapis 这似乎正是我正在寻找的 100%! 我知道这不是堆栈溢出,但是您能否指出一些有关如何使用它的文档的方向?

我觉得我每天都在学习有关 angular 2 的全新知识。 通常,我学到的东西似乎记录在英雄之旅指南的前 50 页中:P

@Toxicable哇,看起来很酷。 但它适用于内容投影吗? 我有这样的标记(来自后端)应该动态添加

<component1 smth="smth">
  <component2>item 1</component2>
  <component2>item 2</component2>
  <component3 smth="smth">
    <component4>smth</component4>
  </component3>
</component1>

并且目前被迫使用 JiT(在大多数代码中使用 aot)在运行时编译它

@artaommahe ... component1不能直接使用 ... 但是如果您使用带有 ^^^ 之类的模板的包装器 ...但我从未尝试过。

但是如果您使用带有模板的包装器,例如

这个 makup 是未知的,直到它来自后端,有数百种变化

@artaommahe ......啊,我明白了。 但是仍然可以通过解析和动态组件实例化,甚至可以管理您的案例 ^^^ ......你只需通过另一种方式进行间接内容投影......通过你自己的代码,我认为......

@Toxicable@artaommahe :与“...stackblitz...”演示相关。 这是旧东西,无法用于现实生活中的业务应用程序。 仅插入普通 html 的代码和开销过多。 动态组件加载器解决方案也是如此。 此外,在没有数据绑定的情况下插入 HTML 并不是我们在日常生活中处理的用例。

就目前而言:没有解决方案可以以 2-4 的角度动态加载组件的布局 (HTML)。

@KarlXOL对不起,您一定对演示的工作方式有一些误解。

这是旧东西

是什么让旧的东西变得糟糕?

仅插入普通 html 的代码和开销过多

您只实现一次,然后像往常一样使用 HTML,没有开销,所以我不知道您的意思。 甚至还有 npm 包可以为你做这件事

现实生活中的商业应用

angular.io 使用它,所以我不知道你从哪里得到这些信息。

插入没有数据绑定的 HTML

数据绑定确实适用于此

就目前而言:没有解决方案可以以 2-4 的角度动态加载组件的布局 (HTML)。

就在这里

@Toxicable :不,它不适用于数据绑定。 使用模板中的 {{myvar}} 更新您的演示,并向我展示它是如何工作的。 提前致谢

@KarlXOL您不能直接插入到 HTML 中,如果不编译 html,那将永远无法工作,这不是演示的重点,您可以在组件中进行数据绑定。 重点是展示您如何拥有动态布局并以您想要的任何方式重用您的组件,通过重用您不需要任何重新编译的组件。
您要求的是动态编译的模板,这在这种情况下没有意义

@Toxicable所以目前除了在运行时使用 JiT 编译带有内容投影的标记之外,我的情况没有办法? (我看不出有任何方法可以随时更改数百个随机 makrups 以进行预编译)

@Toxicable您能否善待并编辑演示以包含一个文本字段,该文本字段将其值绑定到名为testname的变量,并使my-component组件使用testname作为它显示的name参数值。 基本上我需要将参数传递给动态创建的组件,并在每次testname文本字段中的值更新时更新它。

我已经尝试了几十种不同的方法,但无法让它发挥作用。 如果我们不能直接在 html 中获取{{myvar}} ,我们需要一个名为<display-value [value]="bind_to_var_name_x_or_y" /> 。 这可能吗?

@swftvsn ...当你创建一个组件的新实例时,你会得到它的引用......例如_cmpRef 。 然后通过_cmpRef.instance您可以访问该组件的@Input()@Output ,例如...( @Input() myprop: string;@Output() messages: EventEmitter<string> = new EventEmitter<string>(); ):

this._cmpRef.instance.myprop = 'xxx';

或者

this._cmpRef.instance.messages.subscribe(this.messageSubscription);
...
messageSubscription = (_message: string) => {
     ...
}

HTML 对象的动态加载应该是 Angular 2 的基本特性,但不幸的是,以性能的名义,它不可用。 以后会不会有这个功能呢?

@Toxicable感谢您的演示,动态事件绑定到 HTML 对象不起作用,例如 KeyPress、ngOnChanges 到输入对象

感谢@mlc-mlapis 的输入!

我现在已经探索了一天的可能性,我需要做的事情可能可以使用动态组件来完成。

我在追兔子洞的时候想到的两件事:

  1. 官方文档可能会好一点,我觉得它不如其他Angular文档那么好和详细
  2. 尽管大多数用例可以使用当前的 API 来完成,但我再次觉得它可以更容易和更直观

社区被这个很棒的框架宠坏了,因此我们倾向于期望一切都简单而美好;)

@swftvsn ... 有关于https://angular.io/guide/dynamic-component-loader的官方文档

不过有一个问题 - 我必须让胡子模板生效,而且我似乎得到了这样的东西:

<table>
  {{#each rows}}
    <tr>
      <td>{{someprop}}</td>
    </tr>
  {{/#each}}
</table>

我已经解决了动态组件部分,即使在嵌套内容或循环时,但我似乎找不到解决这个问题的好方法,因为我正在用组件替换 {{}} 内容,但是组件不能留下任何自己的痕迹,因为这真的弄乱了表格。 现在 replace=true 不再起作用,我对这个有点难住了。 指令也不会起作用,因为没有什么可以附加的。

有任何想法吗?

@mlc-mlapis 我实际上认为官方文档迎合了简单的情况,但没有回答困难的部分。 就像消化 mustachio 模板或其他第三方生成的 html 并用实时 Angular 组件替换部分内容一样。

我知道速度是第一名,其余的都在之后,Angular 的 AOT 性质让很多事情变得困难。 如果你问我,这是一个不错的选择,但完美的世界仍然允许 JIT(轻松)与 AOT 在开发人员认为合适的地方并列。

@swftvsn ... 对于这些情况attribute component非常适合,例如<tr mycolumns></tr> ,其中mycolumns只是带有模板(用于列)或应用相同模式的普通组件也适用于<td mycolumn></td> ... 但对于动态组件,它也是不可用的概念,因为您总是创建一个 trace ... 嵌套元素,这在表结构中是不允许的。

我不确定,但我认为唯一的机会就是将整个表格包装为一个动态组件,该组件可以包含任何子组件,例如<tr mycolumns></tr>重复使用*ngFor ...

这里也需要动态模板。 我们根本不关心启动性能,因为我的应用程序将每隔几天启动一次。

动态模板为应用程序架构添加了一个额外的可扩展层。

基本上这个想法是,通过这种可扩展性,我们可以重新获得失去的灵活性。 在 SPA 之前的日子里,我们(就像在 Web 开发人员中一样)将并且可以使用后端技术将所有依赖注入和动态类加载/动态程序集加载以“一切都是插件”的概念来组织我们的应用程序。 前端只是其中的一部分,当我们动态加载处理订单的部分代码时,这部分代码还将为其功能提供 Web 前端。

使用 SPA,我们获得了类似桌面的性能和感觉,但我们失去了这种灵活性。

使用动态模板对于这种功能至关重要。 浏览器平台具有为此所需的所有灵活性,以及​​其非常不幸的功能,例如 AOT(这很重要,但对其他人来说)有点削弱角度的潜力。

Angular 将在几周内发布 Angular Elements,我希望它能解决这个问题。 请查看以下链接以获取 Angular Elemetns。

https://docs.google.com/presentation/d/1jiXHYwfe1iSUiVLdKLFhSPRHLI_FmIvrI60QTpP6KLk/edit#slide =id.g26d86d3325_0_0

Angular 将在几周内发布 Angular Elements,我希望它能解决这个问题。 请查看以下链接以获取 Angular Elemetns。

但这与随机模板组装/编译无关,当您拥有动态 UI 并且需要从后端的随机标记组装此 UI 时,如何解决这种情况? 它不能被预编译或创建为组件并且变化非常频繁

这个功能在他们 2018 年的路线图中,我刚刚在 twitter 上收到了 Brad Green (@bradlygreen) 的回复。 :)

@nsksaisaravana你能分享推文的链接吗?

@asadmalik3请查看下面的屏幕截图或浏览https://twitter.com/bradlygreen并查看 StackOverflow 报告中 Angular 流行度推文评论的急剧增长。

image

@nsksaisaravana @asadmalik3此功能(组件的动态加载模板)是否在 2018 年的路线图中? 你有没有提到 2018 年路线图的链接?

@sunilpes ...但它现在可用...您只需要使用Angular编译器,因此您可以组合AOT模式(通常用于应用程序核心)+ JIT模式(用于那些动态部分)。

阅读: https :

@mlc-mlapis 我经历了 #20875 问题。 我已经尝试使用compiler模块使用compileModuleAndAllComponentsSync方法动态加载模块(运行时),它可以工作。 但我的问题是:
compiler模块是 Angular(低级 API)的内部模块。 在即将到来的主要版本中,它是否也能按预期工作?
我们正在尝试基于这个模块(编译器)构建我们的工具来动态加载动态模块。

@sunilpes ...但它现在可用...您只需要使用Angular编译器,因此您可以组合AOT模式(通常用于应用程序核心)+ JIT模式(用于那些动态部分)。

由于这个https://github.com/angular/angular/issues/19902无法使用它对于大模板来说非常慢

@sunilpes ...嗯,这就是问题所在。 实际上,我认为版本 6 将允许与 5 相同的使用。有一个新的视图引擎正在开发中......代号ivy ......这将开启非常有趣的新可能性......但它是关于第 7 版的......到目前为止,无法说出任何具体的内容。 😄

Angular Ivy 渲染器预览——Angular 的新视图引擎!!!

https://github.com/robwormald/ivy-code-size

image

有关此功能何时可用的任何更新?

@scottseeker ...如果目前的可能性还不够,你必须等待常春藤。

@mlc-mlapis 我们正在等待此功能。 此功能具有开发运行时可插拔模块的巨大潜力。 让我们看看它是怎么回事...

@KarlXOL如果这对您来说仍然有意义:使用 templateUrl = yourNewHTML... 创建一个 v2Component 并扩展您的 v1Component

@KarlXOL如果这对您来说仍然有意义:使用 templateUrl = yourNewHTML... 创建一个 v2Component 并扩展您的 v1Component

有很多情况下标记来自后端(例如带有许多不同练习的课程内容)并且经常通过单独的管理仪表板进行更改

我也很喜欢这个功能。
能够在运行时更改组件“angular-html”。

@mastronardif +1

我有一个任务,我需要渲染从服务器提供的仲裁模板以显示自定义表单。 必须在运行时创建和编译组件才能启用模板中的绑定。

实现这一目标的步骤:

  • 创建父组件,该组件将在其 ViewContainerRef 中创建和呈现动态编译的子组件。
  • 使用编译器令牌将编译器注入其中。
  • 使用 Component 和 NgModule 装饰器(它们只是引擎盖下的函数)来定义组件和将声明它的模块。
  • 编译两者。 这将为您提供可用于创建组件实例的组件工厂。
  • 使用组件工厂在视图容器内创建组件。
  • 可选:为组件输入分配值(如果有)。
import {AfterViewInit, Compiler, CompilerFactory, Component, NgModule, ViewChild, ViewContainerRef} from '@angular/core';
import {CommonModule} from '@angular/common';
import {JitCompilerFactory} from '@angular/platform-browser-dynamic';

@Component({
    selector: 'app-parent',
    template: '<div #container></div>'
})
class ParentComponent implements AfterViewInit {
    @ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef;
    constructor(private compiler: Compiler) {}

    ngAfterViewInit() {
        // Must clear cache.
        this.compiler.clearCache();

        // Define the component using Component decorator.
        const component = Component({
            template: '<div>This is the dynamic template</div>',
            styles: [':host {color: red}']
        })(class {});

        // Define the module using NgModule decorator.
        const module = NgModule({
            declarations: [component]
        })(class {});

        // Asynchronously (recommended) compile the module and the component.
        this.compiler.compileModuleAndAllComponentsAsync(module)
            .then(factories => {
                // Get the component factory.
                const componentFactory = factories.componentFactories[0];
                // Create the component and add to the view.
                const componentRef = this.container.createComponent(componentFactory);
            });
    }
}

要记住的一些事情:

  • 请记住在编译器上调用 clearCache。 如果不这样做,您将只能渲染一次组件。 当你第二次尝试编译它时,Angular 会抱怨同一个组件存在于两个不同的模块中。
  • 您不必在组件和模块声明中使用匿名类。 我实际上使用了我在应用程序中已有的类来声明组件。
  • 如果应用程序是使用 AOT 编译器编译的,则编译器通常不可用。 为了解决这个问题,我们必须明确提供编译器:
export function createCompiler(compilerFactory: CompilerFactory) {
    return compilerFactory.createCompiler();
}

@NgModule({
    imports: [
        CommonModule
    ],
    exports: [],
    providers: [
        // Compiler is not included in AOT-compiled bundle.
        // Must explicitly provide compiler to be able to compile templates at runtime.
        {provide: COMPILER_OPTIONS, useValue: {}, multi: true},
        {provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]},
        {provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory]}    ],
    declarations: []
})
export class CoreModule {}

@alarm9k ... 是的,这种使用 JIT 编译器并将其与 AOT 核心应用程序结合的方式是开放的并且有效......我们将看到 Ivy 将打开哪些动态事物......即使具有卓越的性能。

@alarm9k我有非常相似的要求,去年尝试过同样的方法,但永远无法让它发挥作用。 我认为我缺少的是对模块的更改。 所以我正在快速尝试它,我从你的代码片段中唯一无法弄清楚的是这个“createQuestionFunction”函数的来源:

const componentFactory = this.createQuestionContent(factories.componentFactories[0]);

@BKHines感谢您注意到这一点。 我不顾一切地从我的应用程序中复制/粘贴了它。 固定的。

@alarm9k哈哈,这就是我临时设置的; 我只是没有运行它。 如果我刚刚运行它,我会看到它工作的! 好东西人! 谢谢!

@alarm9k真的很有趣。 谢谢! 您是否有任何关于不将编译器树从您的包中剔除的成本的统计数据?

screenshot_20181103_224754

差异是 329 kB。

差异是 329 kB。

你错过了 gziping

@alarm9k是否可以向这样创建的组件注入服务? 当我尝试在组件构造函数中注入ChangeDetectorRefElementRef时,我不断收到Can't resolve all parameters for ComponentClass: (?, ?).谢谢

再会! 还面临着需要从外部数据源替换当前组件中的模板。 现在,当然,我正在尝试通过动态创建组件并尝试将当前逻辑传递给它们来解决这个问题。 但是能够准确地改变模式会很棒。 我真的很期待这个功能。 我想大概评估一下这个功能出现的时间。 谢谢。

@alarm9k效果很好,除了ng build --prod 。 没有错误,但是当我加载网站时,控制台显示:

ERROR 错误:“运行时编译器未加载
倪 /main.f449713c3fb3867ad6bf.js:1:68849
compileModuleAndAllComponentsAsync /main.f449713c3fb3867ad6bf.js:1:69182
ngAfterViewInit /6.c40d8b2dc5100b1af302.js:1:560
Xs /main.f449713c3fb3867ad6bf.js:1:126416
Qs /main.f449713c3fb3867ad6bf.js:1:126176
Zs

@SamuelMarks这很可能是因为您没有在模块中提供编译器。

需要包含在什么地方?

你的意思是在父@NgModuleimports: []中? - 哪个模块?

@SamuelMarks您是否将此代码包含在您的模块中? 几个月前我遇到了类似的错误,当时我无法让它工作,直到@alarm9k的例子我才发现编译器不在运行时(尽管错误中现在有明显的消息!大声笑)

export function createCompiler(compilerFactory: CompilerFactory) { return compilerFactory.createCompiler(); }

@SamuelMarks您必须在模块的 providers 数组中包含编译器。 请参阅我上面的代码示例。

在 Angular 世界中,“提供”意味着向模块的提供者添加一些东西。

从父组件捕获动态组件中的更改的任何提示? 我有一个组件 A,它有属性 A。 组件 A 从 HTTP 调用返回的 HTML 字符串创建动态组件,即组件 B。 组件 B 内部是一堆组件 C,它们是项目内部已建立的组件。 组件 C 的输入属性之一是属性 A。我可以将 PropertyA 的值传递给组件 C,方法是将其放在 ComponentB 的实例上:

componentB.instance.propertya = this.propertya;

然后组件 C 在初始化时显示正确的值。 但是,当我更改组件 A 上的属性 A 时,它不会反映在组件 C 上。我将属性 A 移至公共服务并且运行良好(因此我可以坚持使用),但我很好奇如何您可以在动态组件和动态组件中的组件中更改此属性吗? 我尝试创建一个输入/输出变量并订阅输出事件,但那里也没有骰子。 当单击按钮更改属性 A 时,也尝试了以下方法。结果相同。

componentB.changeDetectionRef.detectChanges();

这是一个简单的 GitHub 存储库,以防万一没有任何意义: https :

@alarm9k @BKHines谢谢,差点就

未捕获(承诺):错误:加载块根根模块失败。

import { Compiler, CompilerFactory, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';

import { ThisComponent } from './this.component';
import { theseRoutes } from './these.routes';

export function createCompiler(compilerFactory: CompilerFactory): Compiler {
  return compilerFactory.createCompiler();
}

@NgModule({
  declarations: [ThisComponent],
  imports: [
    CommonModule, RouterModule, RouterModule.forChild(theseRoutes)
  ],
  providers: [{ provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory] }]
})
export class ThisModule {}

使用 Ivy 时, @alarm9k提供的上述示例在 Angular 8 中不起作用。

我收到以下错误:

错误错误:未捕获(承诺):TypeError:Services.createRootView 不是函数
类型错误:Services.createRootView 不是函数
在 ComponentFactory_.push../node_modules/@angular/core/__ivy_ngcc__/fesm5/core.js.ComponentFactory_.create (core.js:18750)
在 ViewContainerRef_.createComponent (core.js:16282)
在 parent.component.ts:35
在 ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:391)
在 Object.onInvoke (core.js:24359)
在 ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:390)
在 Zone.push../node_modules/zone.js/dist/zone.js.Zone.run (zone.js:150)
在 zone.js:910
在 ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:423)
在 Object.onInvokeTask (core.js:24350)
在 resolvePromise (zone.js:852)
在 zone.js:917
在 ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:423)
在 Object.onInvokeTask (core.js:24350)
在 ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:422)
在 Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:195)
在drainMicroTaskQueue (zone.js:601)

@Igneous01你在测试什么版本的 Angular?

@Igneous01 @petebacondarwin我遇到了同样的错误,当enableIvy: true

项目是用 Angular CLI 8.1.3 生成的:

Angular CLI: 8.1.3
Node: 10.15.0
OS: win32 x64
Angular: 8.1.3
... animations, cli, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.801.3
@angular-devkit/build-angular     0.801.3
@angular-devkit/build-optimizer   0.801.3
@angular-devkit/build-webpack     0.801.3
@angular-devkit/core              8.1.3
@angular-devkit/schematics        8.1.3
@ngtools/webpack                  8.1.3
@schematics/angular               8.1.3
@schematics/update                0.801.3
rxjs                              6.4.0
typescript                        3.4.5
webpack                           4.35.2

打包 JSON 文件:

"dependencies": {
    "@angular/animations": "~8.1.1",
    "@angular/common": "~8.1.1",
    "@angular/compiler": "~8.1.1",
    "@angular/core": "~8.1.1",
    "@angular/forms": "~8.1.1",
    "@angular/platform-browser": "~8.1.1",
    "@angular/platform-browser-dynamic": "~8.1.1",
    "@angular/router": "~8.1.1",
    "bootstrap": "^4.3.1",
    "rxjs": "~6.4.0",
    "tslib": "^1.9.0",
    "zone.js": "~0.9.1"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.801.1",
    "@angular/cli": "~8.1.1",
    "@angular/compiler-cli": "~8.1.1",
    "@angular/language-service": "~8.1.1",
    "@types/node": "~8.9.4",
    "@types/jasmine": "~3.3.8",
    "@types/jasminewd2": "~2.0.3",
    "codelyzer": "^5.0.0",
    "jasmine-core": "~3.4.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~4.1.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "~2.0.1",
    "karma-jasmine": "~2.0.1",
    "karma-jasmine-html-reporter": "^1.4.0",
    "protractor": "~5.4.0",
    "ts-node": "~7.0.0",
    "tslint": "~5.15.0",
    "typescript": "~3.4.3"
  }

错误:

ERROR Error: Uncaught (in promise): TypeError: Services.createRootView is not a function
TypeError: Services.createRootView is not a function
    at ComponentFactory_.create (core.js:26827)
    at dynamic-loader.service.ts:92
    at ZoneDelegate.invoke (zone-evergreen.js:359)
    at Object.onInvoke (core.js:34209)
    at ZoneDelegate.invoke (zone-evergreen.js:358)
    at Zone.run (zone-evergreen.js:124)
    at zone-evergreen.js:855
    at ZoneDelegate.invokeTask (zone-evergreen.js:391)
    at Object.onInvokeTask (core.js:34190)
    at ZoneDelegate.invokeTask (zone-evergreen.js:390)
    at resolvePromise (zone-evergreen.js:797)
    at zone-evergreen.js:862
    at ZoneDelegate.invokeTask (zone-evergreen.js:391)
    at Object.onInvokeTask (core.js:34190)
    at ZoneDelegate.invokeTask (zone-evergreen.js:390)
    at Zone.runTask (zone-evergreen.js:168)
    at drainMicroTaskQueue (zone-evergreen.js:559)
    at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:469)
    at invokeTask (zone-evergreen.js:1603)
    at HTMLButtonElement.globalZoneAwareCallback (zone-evergreen.js:1629)

@vunb - 请注意,您应该只在next版本的 Angular 上测试 IVY。 例如9.0.0-next.0

导出函数 createCompiler(compilerFactory: CompilerFactory) {
返回 compilerFactory.createCompiler();
}

非常感谢!

@alarm9k

首先,感谢您上面提到的出色解决方案。 到目前为止,它对我非常有帮助。

以这种方式创建模块/组件时,我在实现多个组件并在它们之间进行路由时遇到了一些麻烦。 你对实施这个有什么建议吗?

例如,

const myModule = NgModule({
            imports: [
                RouterModule.forChild([
                    {
                        path: "",
                        component: component1,
                        pathMatch: "full"
                    },
                    {
                        path: "component2",
                        component: component2,
                        pathMatch: "full"
                    }
                ])
            ],
            declarations: [component1, component2],
            exports: [component1, component2]
        })(class {});

不起作用。 但是排除导入使得 component1 默认加载。

如果你需要动态模板,那你就做错了!

@djleonskennedy这是一个荒谬的声明,您甚至没有尝试备份。 如果您不需要那个很好的功能,请保持移动。

我有一个任务,我需要渲染从服务器提供的仲裁模板以显示自定义表单。 必须在运行时创建和编译组件才能启用模板中的绑定。

实现这一目标的步骤:

  • 创建父组件,该组件将在其 ViewContainerRef 中创建和呈现动态编译的子组件。
  • 使用编译器令牌将编译器注入其中。
  • 使用 Component 和 NgModule 装饰器(它们只是引擎盖下的函数)来定义组件和将声明它的模块。
  • 编译两者。 这将为您提供可用于创建组件实例的组件工厂。
  • 使用组件工厂在视图容器内创建组件。
  • 可选:为组件输入分配值(如果有)。
import {AfterViewInit, Compiler, CompilerFactory, Component, NgModule, ViewChild, ViewContainerRef} from '@angular/core';
import {CommonModule} from '@angular/common';
import {JitCompilerFactory} from '@angular/platform-browser-dynamic';

@Component({
    selector: 'app-parent',
    template: '<div #container></div>'
})
class ParentComponent implements AfterViewInit {
    @ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef;
    constructor(private compiler: Compiler) {}

    ngAfterViewInit() {
        // Must clear cache.
        this.compiler.clearCache();

        // Define the component using Component decorator.
        const component = Component({
            template: '<div>This is the dynamic template</div>',
            styles: [':host {color: red}']
        })(class {});

        // Define the module using NgModule decorator.
        const module = NgModule({
            declarations: [component]
        })(class {});

        // Asynchronously (recommended) compile the module and the component.
        this.compiler.compileModuleAndAllComponentsAsync(module)
            .then(factories => {
                // Get the component factory.
                const componentFactory = factories.componentFactories[0];
                // Create the component and add to the view.
                const componentRef = this.container.createComponent(componentFactory);
            });
    }
}

要记住的一些事情:

  • 请记住在编译器上调用 clearCache。 如果不这样做,您将只能渲染一次组件。 当你第二次尝试编译它时,Angular 会抱怨同一个组件存在于两个不同的模块中。
  • 您不必在组件和模块声明中使用匿名类。 我实际上使用了我在应用程序中已有的类来声明组件。
  • 如果应用程序是使用 AOT 编译器编译的,则编译器通常不可用。 为了解决这个问题,我们必须明确提供编译器:
export function createCompiler(compilerFactory: CompilerFactory) {
    return compilerFactory.createCompiler();
}

@NgModule({
    imports: [
        CommonModule
    ],
    exports: [],
    providers: [
        // Compiler is not included in AOT-compiled bundle.
        // Must explicitly provide compiler to be able to compile templates at runtime.
        {provide: COMPILER_OPTIONS, useValue: {}, multi: true},
        {provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]},
        {provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory]}    ],
    declarations: []
})
export class CoreModule {}

我有这个解决方案的问题,我无法添加服务
image
image
image

然后我做了 ng serve --aot 并且我收到了这个错误,如何在运行时组件上注入数据服务?
image

@jalbatross @CharlesElGriego伙计们,我很高兴它帮助了某人(很抱歉它对其他人不起作用)。 在我研究并找到解决方案的那个特定项目中,我有一个非常狭窄的用例。 在那之后我几乎立即切换到另一个项目(我是承包商),而且我已经一年多没有使用 Angular(不幸的是,我没有半小时的空闲时间做一些实验)。 很遗憾, @jalbatross ,我无法为您提供与 Angular 相关的建议。 但是,我强烈支持这样一种观点,即如果使用特定工具很难完成某件事,那么我很有可能使用了错误的工具。 巧合的是,我在另一个项目中有一个类似的任务(运行时的任意模板编译),但这次我避免使用 Angular 来完成这项工作。 相反,我使用了 React+JSX+Babel:

import * as Babel from '@babel/standalone';

const compiled = Babel.transform(
    '<div>Content</div>',
    {presets: ['react']}
).code;

如果您的模板绝对需要采用 Angular 语法,那么我没有给您任何建议。 但是,如果您对格式很灵活,您可能会考虑,我不知道……微前端的疯狂想法并在 Angular 的海洋中创建一个小岛 React(preact?)?

@CharlesElGriego您的图像不足以理解。 如果你真的想认真谈谈,在Github上创建尽可能简单的复制项目,只关注一个问题,可以克隆和运行。

@CharlesElGriego
您可以做的是从您创建组件的上下文中传入服务。 然后将该变量分配给动态创建的组件的实例。

   // This is from the parent component
   constructor(private service1: service1) {

   } 
    ngAfterViewInit() {
        // Must clear cache.
        this.compiler.clearCache();

        // Define the component using Component decorator.
        const component = Component({
            template: '<div>This is the dynamic template</div>',
            styles: [':host {color: red}']
        })(class {
              // Makes service1 available in the created controller scope
              private dynamicAssignedService: service1;
              constructor() {
                console.log("Got service: ", dynamicAssignedService);
               }

             });

        // Define the module using NgModule decorator.
        const module = NgModule({
            declarations: [component]
        })(class {});



        // Services you need here 
        let sharedService = this.service1;

        // Asynchronously (recommended) compile the module and the component.
        this.compiler.compileModuleAndAllComponentsAsync(module)
            .then(factories => {
                // Get the component factory.
                const componentFactory = factories.componentFactories[0];
                // Create the component and add to the view.
                const componentRef = this.container.createComponent(componentFactory);

                // Assign the service to the component instance
                componentRef.instance.dynamicAssignedService = sharedService;
            });
    }
}

你好! 非常感谢@jalbatross和@alarm9k 的帮助
我还根据您的回答创建了一个 repo
https://github.com/CharlesElGriego/angular-aot-dynamic-components

@CharlesElGriego谢谢。 我之前可能应该提到过,但我不久前写了一篇关于这个案例的文章。 还有一个指向该示例的 repo 的链接。
https://www.linkedin.com/pulse/compiling-angular-templates-runtime-dima-slivin/

@jalbatross,@CharlesElGriego,@ alarm9k,

您是否也设法找到了使用templateUrl而不是template

我无法使用templateUrl运行它,因为 angular 无法加载标记。 如何以编程方式强制执行此操作?

ERROR Error: Uncaught (in promise): Error: Component 'Foobar' is not resolved:
 - templateUrl: ./foobar.component.html
Did you run and wait for 'resolveComponentResources()'?

任何帮助将非常感激!

等待 Angular 9 (Ivy),这将是解决我两年前发布的这个问题的真正和最佳解决方案。
我的问题实际上是对“高阶和动态”组件的请求,现在可以在 Angular 9 (Ivy) 中轻松实现——这在 AngularJS 中也是显而易见的。

@knafteN老实说,我怀疑这是可能的。 我没有查看过负责该问题的 Angular 代码,但我怀疑模板 URL 在构建时解析,并且该过程本身与模板编译是分开的,因此与编译器无关。

什么样的用例需要您在运行时使用模板 URL 来编译模板?

感谢您的评论@KarlXOL@alarm9k

我正在寻找某种机制,使我能够为同一组件使用不同的 html 模板。 目前我只使用一个带有 switchCase 语句的模板,但这种方法相当丑陋,并且不必要地膨胀了代码。
我需要这个,因为我有两个客户,他们想要应用他们自己的风格指南和主题。 逻辑保持不变。

这种行为已经(以某种方式)在https://github.com/NativeScript/NativeScript 中实现了component.htmlcomponent.tns.html )。 这个 tns.html 文件是通过约定加载的,但我不知道在我的 angular 项目中自己在哪里以及如何创建这种行为。
参考: https :

等待 Angular 9 (Ivy),这将是解决我两年前发布的这个问题的真正和最佳解决方案。
我的问题实际上是对“高阶和动态”组件的请求,现在可以在 Angular 9 (Ivy) 中轻松实现——这在 AngularJS 中也是显而易见的。

嗨,你有关于 Angular 9 如何处理这个问题的文档吗? 谢谢

@jalbatross,@CharlesElGriego,@ alarm9k,

您是否也设法找到了使用templateUrl而不是template

我无法使用templateUrl运行它,因为 angular 无法加载标记。 如何以编程方式强制执行此操作?

ERROR Error: Uncaught (in promise): Error: Component 'Foobar' is not resolved:
 - templateUrl: ./foobar.component.html
Did you run and wait for 'resolveComponentResources()'?

任何帮助将非常感激!

我对 css 样式有同样的问题

@knafteN @CharlesElGriego正如@KarlXOL 所提到的,您应该从HOC的角度(高阶组件)考虑这一点。 Ivy 提供了很快创建 HOC(返回新组件的组件)的可能性,这在实际的 Angular API 中是不可能的,或者它非常复杂。

将任何 HTML 代码与现有组件类型/实例合并的想法在运行时没有意义。 请记住,主要的架构原则之一是能够预测运行时的行为并保证应用程序的稳定性。 从外部某处获取任何 HTML,并在没有任何受控方式的情况下使用它只是一个盲目的实验,可能在开发模式下可用,但肯定不能在生产环境中使用。

从外面的某个地方获取任何 HTML,并在没有任何控制的情况下使用它只是一个盲目的实验,可能在开发模式下可用,但肯定不能在生产环境中使用

你是说没有有效的用例在运行时编译任意 HTML? 如果这就是你的意思,那么......好吧,有很多用例。 它们中的大多数与用户创建并提交到您的系统的 HTML 模板有关。

@alarm9k 是的,我知道你的意思。 这实际上是主要问题。 您可以通过多种方式设计界面; 其中之一也是接受用户直接推送的 HTML 代码。 您可以通过不同的界面以不同的方式做同样的事情,但结果相同且更安全。

如果您正在谈论解析任意 HTML,您还必须记住编译和绑定到代码的其他部分。 为此,您还需要一个运行时编译器,有了它,我们又回到了 Angular 的实际版本中。

HOC 以不同的方式工作。 它是 API,它允许您在运行时动态地、逐步、部分地以受控方式执行此操作,因此您不需要编译器。

HOC 以不同的方式工作。 它是 API,它允许您在运行时动态地、逐步、部分地以受控方式执行此操作,因此您不需要编译器。

那么这是创建动态组件的新方法吗? 我需要从无头 CMS 管理器获取 CMS Html

@CharlesElGriego HOC API 尚未发布。 在新的 Ivy 渲染器的基础上,它处于设计阶段,因此无法准确说明它将如何工作以及所有可用的可能性。 我只是想表达,它不会仅仅采用任何 HTML 并将其推送到组件类型/实例中来更改其 UI。

那么目前仅使用一个.ts -file 就不可能实现以下拆分?

  • app.component.ts (如果某些条件 -> 使用红色否则使用蓝色)
  • app.component.red.html
  • app.component.blue.html

它不必在运行时,我只想要代码拆分的动态行为。

@knafteN可能是为了一些额外的情况,因为templateUrl接受三元运算符,例如:

templateUrl: config.type === 1 ? 'red.html' : 'blue.html',

但是必须静态分析条件,因为 Angular 编译器在编译阶段不运行任何代码。 但这并不能解决动态行为,它仍然是在 AOT 过程中静态评估的。

@mlc-mlapis 谢谢,您的变体可以工作,但是使用表达式而不是常量会破坏 IDE 对 html 文件的支持,因为 vscode/webstorm 无法评估 templateUrl 并且无法知道哪个 html 文件用于组件:(

@knafteN 是的,这是另一种类型的问题,与 Angular 本身没有直接关系,但从开发人员的角度来看仍然存在限制。 之所以存在,是因为这种情况很少使用,因此尚未在那些编辑器中实现。 所以现在,如何在运行时使用动态 HTML 模板的唯一现实方法是将 JitCompiler 注入 AOT 应用程序并动态编译它,即使出于性能和安全原因也不推荐这种方式,并等待新的 HOC API在 Angular 9.x 中(因为它肯定不会在 9.0 的第一个版本中可用)。

Angular 团队是否会在/如果此功能恢复时提供任何指导? 虽然居高临下的评论(如果你需要这个功能,那么你不知道如何编码)可能会让作者感觉良好 - 他们并没有真正回答问题。 我们为实施用户提供自定义页面的能力 - 现在我们没有多年来一直是我们竞争优势的功能!

我需要此功能用于完全不同且有效的用例(恕我直言)。 我正在开发一个产品。 我自己开发的框架在服务器端创建表单,渲染也在那里完成(或者我们可以称之为后端)。 这对我来说有几个好处,我们可以不讨论这些。

现在,在用户想要搜索的页面上说产品和业务逻辑是他们可以根据他们在系统中的权限在搜索条件中设置不同的元素。 即,一个用户可能能够按价格范围进行过滤,而另一个用户则不能,而第二个用户可能能够按“系统中添加的日期”进行过滤,而第一个用户则不能。

在这种情况下,我想从后端加载此搜索条件表单并将其元素绑定到模型。 这同样适用于我在 2012 年左右开发的 Angular 1.x。 指令模板 url 是 http url,后端用于根据登录用户的权限返回表单。 现在,使用 Angular 似乎不可能。
我也尝试过使用 React,但它又是不可能的。 (出于生产中的性能原因,我不想使用 babel/standalone)。
我很惊讶在目前最流行的前端技术中遇到了这样的死胡同。

回到 AngularJS (1.x) 似乎很难证明。

@harishrohaj不直接修补 HTML 代码是大势所趋(不仅在 Angular 本身中),因为测试此类场景的可能性非常有限(应用程序根本不知道要修补什么)并且所有那些不可预测的动态事物都会减少此类应用程序的预期稳定性。

@mlc-mlapis 我理解你的意思。
实际上,它在早期(在 AngularJS 中)一直是一种有效的方式/功能,因此作为用户考虑它有点困难。
但是现在我正在考虑这个想法,我认为我可以通过创建一些组件来解决我的问题,该组件可以根据其中所需元素的一些信息创建表单并将元素绑定到模型。

@harishrohaj 是的,我知道从 AngularJS 切换到 Angular,尤其是头部如何自动查看分析模型,并不容易。 我认为你现在走在正确的道路上。 👍

@mlc-mlapis - 它不仅仅是从 AngularJS 切换过来的……这个功能Angular 5 之前可用,然后被删除。 并且可能有也可能没有“不直接修补 HTML 代码的总体趋势”(我根本没有看到这种趋势)——这是当今大多数 ISV 产品需要和拥有的功能。

所以真正的问题是这个功能是否存在于 9 中(并且没有记录),它是否很快就会回来,或者有人想出了一些 CKEditor 类型的插件。

@virshu它仍然具有与以前相同的功能。 这意味着您仍然可以在 AOT 捆绑应用程序中加载 JitCompiler,然后动态编译模块和 HTML 模板。 由于上述原因,仍然不建议这样做。

游戏中的另一件事可能是 Ivy 中的一些新功能(目前仍无法用于生产并为内部使用而实现)和 API,它们允许使用 HoC 概念或更灵活的动态创建组件,Angular 团队正在这些功能上在职的。

@mlc-mlapis - 今天是该线程启动 3 周年。 从一开始,您就尝试将一个非常具体的问题(是否可以动态加载模板)更改为基于意见的讨论(不推荐;这是不好的做法;无论如何)。

鉴于您明显不正确的陈述(“它仍然具有与以前相同的功能。”)很难将您的其他评论视为表面价值。 所以,如果可能的话,希望 Angular 团队的成员提供事实信息

德库吉 :)

@virshu作为这个线程的发起者,我完全同意你的看法。 时间飞逝 :-)
我仍然希望在即将到来的 ivy-releases 中有一些东西可以为我们提供解决方案

@virshu嗯,我说了什么错误或误导? 你的配方是:

此功能在 Angular 5 之前可用,然后被删除

我只是说It's still here the same functionality as before. It means that you can still load JitCompiler ...这意味着它没有被删除,它像以前一样在这里,因为 AOT 的概念从 Angular 2 诞生的第一刻起就在这里。

如果你想说动态直接 HTML 补丁在 AngularJS 中是可用的,但它在 Angular 中根本没有,那么你是对的,但那又是另一回事了。

我也是。

2020 年 3 月 19 日星期四上午 1:55 KarlXOL [email protected]写道:

@virshu https://github.com/virshu作为这个线程的发起者,我
完全同意你的看法。 时间飞逝 :-)
我仍然希望在即将到来的 ivy-releases 中会有一些东西
为我们提供解决方案


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/angular/angular/issues/15275#issuecomment-601002102
或取消订阅
https://github.com/notifications/unsubscribe-auth/AAFMV7QNGXRPZZIHZSQSQ2TRIGXUJANCNFSM4DEFOTBA
.

+1

我已经关注这个线程很长时间了,即使我想看到一个带有动态 html 的组件,我也不怕改变我的想法,让它变得“更 Angular”。 在理想的情况下,我希望从这个线程中得到一些类似于使用动态 html 是最简单方法的情况的最佳实践

让我们想象一个例子(一个更广泛的例子,不是动态html的hello world,其他用户可以添加更多细节)。 :思维:
_我创建了一个证券交易所系统,其中每个货币标签(例如澳元)、指数(道琼斯)、汇率(欧元/美元)或指向用户或文章的链接(@user 或 #article_id)都会导致这些标签转换成具有不同功能的 Angular 组件。 文章将由用户自己编辑并存储在数据库中。 系统应该显示加载了所有组件的文章。_

从逻辑上讲,简单的解决方案是从数据库加载动态内容的组件。
Angular 团队的某个人(_抱歉,这超出了我的可能性_)能否在 Angular 博客上写一篇文章(因为很长时间没有发布文章),如何使用当前的 Angular 和动态模板来实现此解决方案(_something like最好但不推荐的方法_),那么为什么这样的解决方案是错误的(我预计会出现糟糕的测试,更糟糕的效率......)以及我们如何更改(设计?)应用程序以符合 Angular设计(如果建议使用 Angular 显示这样的应用程序)。

根据 Angular 的说法,观看这个线程的理想结果(因为,正如这里提到的,Angular 总是使这成为可能)对我来说是一个如何动态完成(错误方式)以及如何正确编码的示例

使用 angular 从版本 2 开始在企业项目中,“动态模板加载”等功能有 0 个用例 最后关闭此问题

使用 angular 从版本 2 开始在企业项目中,“动态模板加载”等功能有 0 个用例 最后关闭此问题

嗨,我认为你错了...在之前的评论中,我为此添加了一个“解决方案”,我知道这不是最好的解决方案,但我不得不采用这种方法,因为现在无法使用 Angular 实现 Headless CMS。 .. 我的模板 HTML 来自 CMS (Umbraco)

@CharlesElGriego你的解决方案是什么? 你能解释一下你的计划,我们可以一起做公关吗?

@CharlesElGriego你的解决方案是什么? 你能解释一下你的计划,我们可以一起做公关吗?

哦,没有 PR,它不是核心解决方案......它是 Jit Compiler 的解决方案,这是我能找到的唯一满足我的需求的解决方案。 但我仍在寻找使用 aot 的解决方案,我可以将其用于 Headless CMS

https://github.com/CharlesElGriego/angular-aot-dynamic-components

@CharlesElGriego针对您的情况的解决方案,是为您实现 Headless CMS 数据结构的构建器,通过使用角度组件,组件不应具有动态模板,因为组件会出现不一致的行为!
@all最好开始编程,而不是不喜欢针对此功能的帖子

哎呀, @djleonskennedy - 不喜欢是由于您反复尝试将一个清晰而实用的问题变成毫无意义的基于意见的讨论! 请得到一个线索。 Если 14 человек говорят что ты пьян, надо идти спать!

@virshu请缩小你的脸

请先生们,这样的答案不会让我们前进。 我看到如果没有 Angular 核心团队的回应,我们不会采取行动。 如果有人参与了与他们无关的对话,我提前道歉,但我认为@kara可以为我们提供专注于 IVY 部分的建议, @maxkoretskyi@shmool有关于 angular 和@bradlygreen说(在 2018 年的帖子中)动态模板也是 IVY 的好处之一。

如果您可以评论此线程(关于您在 ng-conf 上的职责),我将尝试以简化的方式总结此讨论。

  • 我们希望为 angular 应用程序中的组件使用动态模板(从应用程序用户可以编辑的数据库中检索)
  • 如果我没记错的话,当前的选项是添加一个编译器(并且可能将 angular 元素包装到另一个应用程序中) - 这是一个有点笨拙的解决方案,但没有官方示例
  • 如果我们需要使用 IVY 实现动态创建的模板,您能告诉我们推荐/正确的方法是什么吗?
  • 如果这是不可能的,我们应该如何修改此类应用程序的设计以匹配角度方法?

在 angular 博客上写一篇关于这个主题的文章肯定是个好主意。 (举例说明如何使用 IVY 正确执行此操作,和/或如何修改我们的应用程序以使其在应用程序设计方面做得更好以避免此类要求,如果您认为这种方法永远不合理。)

非常感谢您的回复

大家好,Michiel 向我指出了 Manfred撰写的关于 IVY HOC的这篇

代码的重要部分:

import '@angular/compiler';

@Component({ template: '' }) 
class HigherOrderComponent { [...] }

ɵcompileComponent(HigherOrderComponent, { template: '<b>Hello</b>' });

你可以看看: https :

@mlc-mlapis 在 'ng build --prod' 时会起作用吗? 另外,我看到您的示例渲染了一次名称,但从输入更改时无法更新它。 有什么我想念的吗?

@atiris我喜欢你提出问题的方式。 与其争论我们想要的实现是否正确,不如让开发人员向我们展示解决问题的正确方法。 对你有好处,希望我们很快就会看到进展(取决于 HOC 是否是答案)。

@jqsjqs这只是一个关于内部 API 的简单示例,由于实际的向后兼容性,尚未公开。 我没有关于时间表的确切信息何时可以作为公共 API 以及以何种确切形式提供。 此外,此处未应用 NgZone,这就是更改模型时视图不会更新的原因。 只有视图 -> 模型方向有效。

如果有人仍然不确定“现实世界用例”是什么,我遇到了一个,需要一个答案:
我为一家创建软件的公司工作,该软件可在同一台服务器上同时处理多个租户。 我被要求制作一个可以从 http 请求中提取模板的角度组件。 我们需要这个,因为我们允许我们的客户自定义视图(重新排序 div,更改显示的属性等)。 通过请求加载模板的能力使我们能够灵活地创建默认版本,然后被“主题”版本(如果存在)覆盖。
完全理解模板的替代版本确实可能会破坏应用程序,这是已知且可接受的风险。

@Nixon-Joseph因此,您要么需要该组件的 API(例如基于 JSON),以允许客户修改默认模板行为(该组件内部支持此 API),要么您需要定义一组绑定输入属性(使用分离变化检测的优点)或基于\和 \用于模板的单独部分。

与 Angular 核心团队架构观点相比,你这句话Fully understand that the alternate version of the template could indeed break the application, and that is a known and accepted risk.可能是关键的误解。 从他们的角度来看,从性能/稳定性/测试的角度来看,引入任何不能得到认真保证的东西是不可接受的。 即使您声明这是您可以接受的风险,也不会影响最终决定。

@Nixon-Joseph - 您可能有兴趣查看我们在为 angular.io 提供支持的应用程序中采用的方法。 每个页面内容都被动态加载(通过 HTTP)并实例化到 DOM 中,使用 Angular Elements 允许将某些动态行为包含在其他静态内容中。 见https://github.com/angular/angular/blob/master/aio/src/app/layout/doc-viewer/doc-viewer.component.ts#L131 -L153

@mlc-mlapis 我感谢您的回复。
@pedroclayman这看起来很有趣,它是否允许 angular 与动态内容交互,就好像它是组件的一部分一样? 本质上,我需要呈现一个功能齐全的交互式组件。 我看过你提供的东西,看起来很有希望,但我没有看到任何明显的(至少对我来说)特别指出我们需要的东西。

我实际上找到了一个对我们有用的解决方案,唯一的缺点是我们必须禁用该项目的 AoT 编译。
对于那些在我之后想要同样的事情的人 - 这是我最终做的事情的快速概述。
首先,在这里找到了我的解决方案的基础: https :

import { Component, NgModule, Compiler, ViewContainerRef, Type } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { SharedModule } from '../modules/shared.module';

export class DynamicComponentHelper {
    public static addComponent<ViewModelType = any>(compiler: Compiler, container: ViewContainerRef, template: string, viewModel?: ViewModelType, components: Array<Type<any>> = []): void {
        @Component({ template: template })
        class DynamicComponent {
            public vm: ViewModelType = viewModel;

            constructor() { }
        }
        components.push(DynamicComponent);
        @NgModule({
            imports: [BrowserModule, SharedModule],
            declarations: components
        })
        class DynamicComponentModule { }

        const mod = compiler.compileModuleAndAllComponentsSync(DynamicComponentModule);
        const factory = mod.componentFactories.find((comp) =>
            comp.componentType === DynamicComponent
        );
        const component = container.createComponent(factory);
    }
}

然后我有一个组件像这样调用它......

export interface VM { text: string; }

@Component({
    selector: 'app-component',
    template: `<ng-template #dynamicComponent></ng-template>`
    ...
})
export class VariationsComponent implements OnInit, AfterViewInit {
    @ViewChild('dynamicComponent', { read: ViewContainerRef }) _container: ViewContainerRef;

    private vm: VariationsComponentVM = { text: 'Hello World' }

    private viewInitialized: boolean = false;
    private componentTemplate: string;

    constructor(private compiler: Compiler) { }

    ngAfterViewInit(): void {
        this.viewInitialized = true;
        this.setUpDynamicComponent();
    }
    ngOnInit(): void {
        this.httpService.getComponentTemplate().subscribe(template => {
            this.componentTemplate = template;
            this.setUpDynamicComponent();
        });
    }

    private setUpDynamicComponent(): void {
        if (this.viewInitialized === true && this.componentTemplate) {
            DynamicComponentHelper.addComponent(this.compiler, this._container, this.componentTemplate, this.vm, [NestedComponent]);
        }
    }
}

然后正在使用的实际模板可能很简单......

<div>{{ vm.text }}</div>
<app-nested [input]="vm.text"></app-nested>

最重要的是,所有内置的角度模板都可以工作,所有指令和一切。 再一次,唯一的缺点是你必须失去 AoT。 是否可以/是否可以在组件级别禁用 AoT? 如果我们可以在 AoT 中编译大部分项目,但保留一个已定义的项目,那就太好了。

现在我的构建命令必须是:
ng build ProjectName --aot=false

ng build ProjectName --prod --aot=false --build-optimizer=false

@Nixon-Joseph 是的,这是我很久以前创建的历史演示。 一般来说,你应该能够像往常一样在 AOT 模式下构建你的应用程序,此外,你必须注入 JitCompiler(这个主题在一些带有演示代码的 Angular GitHub 问题中讨论)并使用它来动态编译临时模板。 所以最后你可以有一个 AOT + JIT 的组​​合解决方案。 它仍然不理想,即使出于性能/安全性/稳定性的原因,Angular 核心团队也不推荐它,但它正在工作。 我还建议投资于组件可定制界面的一些扩展可能性,并且不允许在没有任何控制的情况下修补 HTML 代码。 JitCompiler 能够从其原则上阻止使用一些无效的模板,但是,如何支持任何客户仍然是一种过于模糊的方式。

@mlc-mlapis 感谢您的评论。 我很想将我在.render函数中看到的内容与我们正在做的事情结合起来。 但我认为我对何时/如何进行还没有足够的把握。 我的主要目标是希望能够继续使用 AoT,并通过检索到的模板加载动态组件。 在生产模式下,如果我不使用AoT,项目(所有文件加起来)几乎是2M,但是使用AoT,它在800K以下。 如果我试图AOT编译我的解决方案,它抛出的错误@Component的部分addComponent
您是否可以提供一个快速示例,说明您提出的解决方案是什么样的? 我很乐意将这个对话从这个步骤中拉开,这样我们就不会不必要地膨胀它。

@Nixon-Joseph从 angular/angular-cli#17663 开始,这是 CLI 中`buildOptimizer`的实际问题,它仍然允许 AOT 编译,但没有完成摇树优化。 复制演示是https://github.com/jcgillespie/ng-dynamic-optimized-repro ,它可以帮助您实现您的概念,即使没有在实际最新版本的 Angular 中进行构建优化。

@Nixon-Josephangular/angular-cli#17663 开始,这是 CLI 中`buildOptimizer`的实际问题,它仍然允许 AOT 编译,但没有完成摇树优化。 复制演示是https://github.com/jcgillespie/ng-dynamic-optimized-repro ,它可以帮助您实现您的概念,即使没有在实际最新版本的 Angular 中进行构建优化。

@mlc-mlapis
这就是 angular 现在的问题。 每个功能请求都会引发关于为什么可以完成某些事情而更多时候无法完成的低级和低级技术原因的永无止境的讨论。
对这次情绪爆发感到抱歉。 但是我们开发人员正在等待期待已久的功能(像这样),这些功能会变慢,而且通常不会出现。
例如,其他功能是延迟加载等方面的改进。 与其他平台相比,angular 似乎正在失去优势

我和其他许多人可能会非常欣赏有关计划中的角度特性的有意义的路线图。
谢谢

如果有人仍然不确定“现实世界用例”是什么,我遇到了一个,需要一个答案:
我为一家创建软件的公司工作,该软件可在同一台服务器上同时处理多个租户。 我被要求制作一个可以从 http 请求中提取模板的角度组件。 我们需要这个,因为我们允许我们的客户自定义视图(重新排序 div,更改显示的属性等)。 通过请求加载模板的能力使我们能够灵活地创建默认版本,然后被“主题”版本(如果存在)覆盖。
完全理解模板的替代版本确实可能会破坏应用程序,这是已知且可接受的风险。

嗨,我的 GitHub 上有一个解决方案,它也适用于 AOT :)
https://github.com/CharlesElGriego/angular-aot-dynamic-components

@KarlXOL我理解你。 但我也认为你对 Angular 整体而言是部分不公平的。 当然有一些复杂的部分可以设计得更好,但我也很清楚这些领域的突破性变化更成问题,应该以超高的注意力创造。

还有一个主要的 Angular 基石之一,编译器的静态分析,它直接反对模板的动态匿名修补。 一直以来,我们中的许多人都在思考和寻找一种巧妙的解决方案,既能满足双方的需求,又不会做出严重和危险的妥协。

@mlc-mlapis
感谢您的回答。 我非常感谢您和您的团队在 angular 方面投入的时间和精力,为我们提供了一个全面的、“感觉良好”的平台和环境。 非常感谢!
但另一方面,在过去的大约 6 个月里,我感觉到您的团队正在为 angular、平台和工具的复杂性、依赖性……而苦苦挣扎,而不是能够专注于推动平台向前发展。
开发人员日常业务需求的功能输出正在放缓。 仅以 Ivy 为例,它直到现在还只是在幕后工作,直到现在还没有进入开发人员工具箱以改进应用程序开发的过程或质量。

我在过去打赌 angular,我想在未来这样做。

如果有人仍然不确定“现实世界用例”是什么,我遇到了一个,需要一个答案:
我为一家创建软件的公司工作,该软件可在同一台服务器上同时处理多个租户。 我被要求制作一个可以从 http 请求中提取模板的角度组件。 我们需要这个,因为我们允许我们的客户自定义视图(重新排序 div,更改显示的属性等)。 通过请求加载模板的能力使我们能够灵活地创建默认版本,然后被“主题”版本(如果存在)覆盖。
完全理解模板的替代版本确实可能会破坏应用程序,这是已知且可接受的风险。

嗨,我的 GitHub 上有一个解决方案,它也适用于 AOT :)
https://github.com/CharlesElGriego/angular-aot-dynamic-components

@CharlesElGriego谢谢! 它看起来很有希望,并且非常接近我已经所在的位置。 我应该能够修改我的解决方案以更接近您的解决方案并很快尝试它。 我看到你在 repo 中有一个问题,提到它没有在生产中编译 aot。 我会看看它对我有什么作用,然后从那里开始。

@KarlXOL @mlc-mlapis 我绝对可以在这里看到你的两个观点,我很感激正在进行的讨论。 不过,我会说,在这一点上,我更站在@KarlXOL一边。 虽然我完全理解并尊重使 Angular 尽可能安全和稳定的尝试,但拥有功能(如所要求的功能)并没有什么坏处,只需将它们放在一边,并附上建议不要使用的文档- 在这里列出所有好的理由。
这将涵盖两种情况,不是吗? 除非有人使用该功能,否则他们不会失去任何稳定性。 但如果他们选择使用它,那么他们就必须承认它违反了建议。

这会被记录在案吗? 我想为 wordpress 创建一个客户端,例如允许用户将 html 放在像博客这样的页面上。 但是允许一些模型绑定会很有趣。 这个解决方案是好还是hacky?
https://stackoverflow.com/questions/46576727/angular-compile-and-create-components-at-runtime

我认为使用 angularjs 会很容易。 我寻找客户端渲染只是为了避免服务器脚本注入。

对于任何感兴趣的人,我想解释一下我是如何在我的项目中解决这个问题的。 我遇到了完全相同的问题:HTML 和 CSS 来自 Web API,完全动态。 如果您想在运行时编译 Angular 模块和组件,则不能同时使用 AOT 和buildOtimizer: true ,因为构建优化器会将编译器从源代码中剥离出来。

我完全摆脱了 Angular 泡沫,因为似乎没有真正为此类动态用例提供任何支持。 我刚刚实现了 Web 组件。 不是 Angular Elements 或任何其他框架,只是到处运行的普通浏览器 Web 组件。 我收到 HTML 和 CSS 以及一些绑定信息。 Web 组件能够打开一个新的 shadow DOM 以隔离应用程序和组件样式。 这样传入的 CSS 或 HTML 不会破坏您的应用程序。

最后,我实现了我想要的目标:AOT + Angular 中的构建优化器、动态 HTML 和 CSS,在运行时来自 Web API,包括数据绑定。 所有这一切都无需将整个 Angular 编译器打包到运行时中。 而且它也快得多,因为我只是从运行时中删除了 2mb 不需要的 js。

对于任何感兴趣的人,我想解释一下我是如何在我的项目中解决这个问题的。 我遇到了完全相同的问题:HTML 和 CSS 来自 Web API,完全动态。 如果您想在运行时编译 Angular 模块和组件,则不能同时使用 AOT 和buildOtimizer: true ,因为构建优化器会将编译器从源代码中剥离出来。

我完全摆脱了 Angular 泡沫,因为似乎没有真正为此类动态用例提供任何支持。 我刚刚实现了 Web 组件。 不是 Angular Elements 或任何其他框架,只是到处运行的普通浏览器 Web 组件。 我收到 HTML 和 CSS 以及一些绑定信息。 Web 组件能够打开一个新的 shadow DOM 以隔离应用程序和组件样式。 这样传入的 CSS 或 HTML 不会破坏您的应用程序。

最后,我实现了我想要的目标:AOT + Angular 中的构建优化器、动态 HTML 和 CSS,在运行时来自 Web API,包括数据绑定。 所有这一切都无需将整个 Angular 编译器打包到运行时中。 而且它也快得多,因为我只是从运行时中删除了 2mb 不需要的 js。

你好,你有这样的例子吗? 谢谢

干得好! 我目前没有时间解释每一个细节,所以这里是一个简短的故事:

init()update()被 Angular 调用。 span 标签数组是“数据绑定”文本。 肯定有一些模板解析在组件外部进行,所以我可以对值进行循环并将它们放入跨度中,但是通过以下示例,您应该了解基本概念。 并且不要忘记在窗口上注册自定义 Web 组件...

export class TemplateContentWebComp extends HTMLElement {

  private tplDiv: HTMLDivElement;
  private tplSpans: Array<HTMLSpanElement> = new Array<HTMLSpanElement>();

  public init(templateCss: string, templateHtml: string): void {
    const template: HTMLTemplateElement = document.createElement('template');

    const css: string = `<style>:host { flex: 1; display: flex; flex-direction: column; } .tpl { flex: 1; box-sizing: border-box; }${!String.isNullOrWhiteSpace(templateCss) ? (' ' + templateCss) : String.empty()}</style>`;
    const html: string = `<div class="tpl">${!String.isNullOrWhiteSpace(templateHtml) ? (' ' + templateHtml) : String.empty()}</div>`;

    template.innerHTML = `${css} ${html}`;

    this.attachShadow({ mode: 'open' });
    this.shadowRoot.appendChild(template.content.cloneNode(true));

    this.tplDiv = this.shadowRoot.querySelector('div.tpl');

    const spans: NodeListOf<HTMLSpanElement> = this.shadowRoot.querySelectorAll(`span[data-var]`);

    if (spans != null && spans.length > 0) {
      spans.forEach(span => this.tplSpans.push(span));
    }
  }

  public update(isEditable: boolean, values: Array<string>): void {
    if (this.tplDiv != null) {
      if (isEditable) {
        this.tplDiv.removeAttribute('tpldisabled');
      } else {
        this.tplDiv.setAttribute('tpldisabled', '');
      }
    }

    for (let i = 0; i < this.tplSpans.length; i++) {
      this.tplSpans[i].innerHTML = values[i];
    }
  }
}

阅读此评论我想我们也可以在这里关闭这个问题😞

https://github.com/angular/angular-cli/issues/17663#issuecomment -705737272

嗨,我一直在寻找一种将多个模板加载到 1 个单个组件中的方法。 感谢@alarm9k的帖子,我能够找到一种方法来做到这一点。 当然,这种方式只是暂时的,因为它需要编译器,因此无法在生产模式下启动它。

所以几个月来我一直在寻找一种方法来做到这一点,但在生产模式下。
这实际上没有帮助,因为我找不到其他任何东西,所以作为最后的手段,我在 StackOverflow 上问它,为这个问题添加了一些赏金,最后添加我得到了一个能够完美地做到这一点的答案。

当然,如果你想要单独的样式表,你可以在从数据库加载它后将样式表添加到 html 模板,它会工作得很好(可能不是最好的,但它可以工作)。 请不要把这个答案归功于我,因为所有的功劳都归于回答我问题的人。

https://stackoverflow.com/questions/63998467/angular-multiple-templates-in-one-component-based-on-id-with-template-store

@BillyCottrell这也取决于你提前知道的事情。 如果您提前知道所有模板,您始终可以运行node脚本(或类似脚本)将它们全部插入。 可能有一些方法可以直接用ng来做这件事,绕过它暴露的 webpack 的一点点......

@SamuelMarks是的,但是当您从服务器请求模板时可以使用它,因为示例中的 innerHTML 将异步工作,因此您不需要事先知道模板,即使您知道,您仍然可以预先加载它们使其工作得更快,因为不需要从服务器请求它们。 甚至可以将它与计时器和开关模板结合使用。 无论您知道什么,这只是一种仍然加载模板的简单方法,如果您愿意,可以在之后刷新它,以便您可以显示不同的模板或模板的更新版本。

我的意思是您可以轻松创建一个模板服务,其中包含您已知的模板。 将这些模板添加到其中,如果需要,您可以选择已知模板之一,也可以根据需要从服务器添加新模板。
唯一需要做的就是加载具有预先已知模板和来自服务器的自制模板的服务。 如果您无法从服务器加载模板,那么这很好,因此您可以回退到已知模板之一。 所以没有必要再乱来了。

是的,预制模板是这里的关键。 如果您了解 AOT,那么您可以享受一些乐趣,例如前面提到的https://github.com/SamuelMarks/ng-md-components (让您使用降价模板)。 升级其依赖项并添加对多个模板组合在一起的支持很简单(假设有人关心;只需指出)。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

robwormald picture robwormald  ·  3评论

Hongbo-Miao picture Hongbo-Miao  ·  3评论

pkozlowski-opensource picture pkozlowski-opensource  ·  3评论

gugamm picture gugamm  ·  3评论

escardin picture escardin  ·  3评论