Я хочу динамически создать шаблон. Это следует использовать для создания ComponentType
во время выполнения и размещения (даже замены) его где-то внутри компонента размещения.
До RC4 я использовал ComponentResolver
, но с RC5 я получаю следующее сообщение:
ComponentResolver is deprecated for dynamic compilation.
Use ComponentFactoryResolver together with @NgModule/@Component.entryComponents or ANALYZE_FOR_ENTRY_COMPONENTS provider instead.
For runtime compile only, you can also use Compiler.compileComponentSync/Async.
Я нашел этот документ ( Angular 2 Синхронное Динамическое Создание Компонентов )
И понимаю, что я могу использовать либо
- Вид динамического
ngIf
сComponentFactoryResolver
. Если я передам известные компоненты внутри@Component({entryComponents: [comp1, comp2], ...})
- я могу использовать.resolveComponentFactory(componentToRender);
- Реальная компиляция во время выполнения, с
Compiler
...
Но вопрос в том, как это использовать Compiler
? В записке выше сказано, что я должен позвонить: Compiler.compileComponentSync/Async
- так как?
Например. Я хочу создать (на основе некоторых условий конфигурации) этот вид шаблона для одного вида настроек
<form>
<string-editor
[propertyName]="'code'"
[entity]="entity"
></string-editor>
<string-editor
[propertyName]="'description'"
[entity]="entity"
></string-editor>
...
а в другом случае этот ( string-editor
заменяется на text-editor
)
<form>
<text-editor
[propertyName]="'code'"
[entity]="entity"
></text-editor>
...
И так далее (другое число / дата / ссылка editors
по типам свойств, некоторые свойства пропущены для некоторых пользователей ...) . то есть это пример, реальная конфигурация может генерировать гораздо больше разных и сложных шаблонов.
Шаблон меняется, поэтому я не могу использовать ComponentFactoryResolver
и пропустить существующие ... Мне нужно решение с Compiler
.
источник
$compile
самом деле может сделать то, что эти методы не могут - я создаю приложение, в котором я просто хочу скомпилировать HTML, который поступает через стороннюю страницу и вызовы ajax. Я не могу удалить HTML со страницы и поместить его в свой собственный шаблон. ВздохОтветы:
РЕДАКТИРОВАТЬ - связано с 2.3.0 (2016-12-07)
Подобная тема обсуждается здесь Эквивалент $ compile в Angular 2 . Нам нужно использовать
JitCompiler
иNgModule
. Узнайте больше оNgModule
в Angular2 здесь:В двух словах
Есть рабочий плункер / пример (динамический шаблон, динамический тип компонента, динамический модуль
JitCompiler
, ... в действии)Принцип:
1) создать шаблон
2) найти
ComponentFactory
в кеше - перейти к 7)3) - создать
Component
4) - создать
Module
5) - скомпилировать
Module
6) - вернуть (и кэш для дальнейшего использования)
ComponentFactory
7) использовать Target и
ComponentFactory
создать экземпляр динамическогоComponent
Вот фрагмент кода (более подробно здесь ) - наш пользовательский Builder возвращает только что построенный / кэшированный
ComponentFactory
и заполнитель представления Target, потребляемый для создания экземпляраDynamicComponent
Вот и все - в двух словах. Чтобы получить более подробную информацию .. читайте ниже
,
TL & DR
Наблюдайте за поршнем и возвращайтесь, чтобы прочитать подробности, если какой-то фрагмент требует более подробного объяснения.
,
Подробное объяснение - Angular2 RC6 ++ & компоненты среды выполнения
Ниже описания этого сценария мы будем
PartsModule:NgModule
(держатель мелких кусочков)DynamicModule:NgModule
, который будет содержать наш динамический компонент (и ссылатьсяPartsModule
динамически)Component
тип (только если шаблон изменился)RuntimeModule:NgModule
. Этот модуль будет содержать ранее созданныйComponent
типJitCompiler.compileModuleAndAllComponentsAsync(runtimeModule)
чтобы получитьComponentFactory
DynamicComponent
- задания заполнителя View Target иComponentFactory
@Inputs
на новый экземпляр (переключатель отINPUT
доTEXTAREA
редактирования) , потребляют@Outputs
NgModule
Нам нужны
NgModule
с.Там будет один модуль для всех небольших компонентов, например
string-editor
,text-editor
(date-editor
,number-editor
...)Вторым будет модуль для нашей динамической обработки вещей. Он будет содержать компоненты хостинга и некоторые провайдеры .. которые будут одиночными. Для этого мы опубликуем их стандартным способом - с
forRoot()
Наконец, нам понадобится adhoc, модуль времени выполнения ... но он будет создан позже, как часть
DynamicTypeBuilder
работы.Четвертый модуль, прикладной модуль, является тем, кто объявляет поставщиков компиляторов:
Читайте (читайте) гораздо больше о NgModule там:
Шаблон строитель
В нашем примере мы обработаем детали этого вида сущности
Чтобы создать
template
, в этом плункере мы используем этот простой / наивный строитель.Хитрость здесь в том, что он создает шаблон, который использует некоторый набор известных свойств, например
entity
. Такое свойство (-ies) должно быть частью динамического компонента, который мы создадим следующим.Чтобы сделать это немного проще, мы можем использовать интерфейс для определения свойств, которые может использовать наш конструктор шаблонов. Это будет реализовано нашим динамическим типом компонента.
ComponentFactory
строительЗдесь важно помнить:
Итак, мы касаемся сути нашего решения. Строитель будет 1) создавать
ComponentType
2) создаватьNgModule
3) компилироватьComponentFactory
4) кэшировать его для последующего повторного использования.Зависимость, которую мы должны получить:
И вот фрагмент, как получить
ComponentFactory
:И вот два метода, которые представляют действительно крутой способ создания декорированных классов / типов во время выполнения. Не только,
@Component
но и@NgModule
Важный:
ComponentFactory
используется компонентом хостингаПоследняя часть - это компонент, который содержит цель для нашего динамического компонента, например
<div #dynamicContentPlaceHolder></div>
. Мы получаем ссылку на него и используемComponentFactory
для создания компонента. Это в двух словах, и вот все части этого компонента (если нужно, откройте плункер здесь )Давайте сначала подведем итоги операторов импорта:
Мы просто получаем, шаблонов и компонентов сборщиков. Далее идут свойства, которые нужны для нашего примера (подробнее в комментариях)
В этом простом сценарии у нашего хостинг-компонента их нет
@Input
. Так что не надо реагировать на изменения. Но, несмотря на этот факт (и чтобы быть готовым к предстоящим изменениям) - нам нужно ввести некоторый флаг, если компонент уже (во-первых) инициирован. И только тогда мы можем начать магию.Наконец, мы будем использовать наш компоновщик компонентов, и он только что скомпилирован / кэширован
ComponentFacotry
. Нашему целевому заполнителю будет предложено создать экземпляр дляComponent
этой фабрики.небольшое расширение
Кроме того, нам нужно сохранить ссылку на скомпилированный шаблон .. чтобы иметь возможность корректно использовать
destroy()
его всякий раз, когда мы его изменим.сделано
Это в значительной степени это. Не забудьте уничтожить все, что было построено динамически (ngOnDestroy) . Также обязательно кешируйте динамически
types
и,modules
если единственное отличие, это их шаблон.Проверьте все это в действии здесь
источник
type.builder.ts
что вы указали, я бы хотел, чтобы любой пользователь понимал, как поместить все это в контекст ... Надеюсь, что это может быть полезно;)РЕДАКТИРОВАТЬ (26/08/2017) : Приведенное ниже решение хорошо работает с Angular2 и 4. Я обновил его, добавив переменную шаблона и обработчик кликов, и протестировал его с Angular 4.3.
Для Angular4 ngComponentOutlet, как описано в ответе Офира, является гораздо лучшим решением. Но сейчас он еще не поддерживает входы и выходы . Если [этот PR] ( https://github.com/angular/angular/pull/15362] будет принят, это будет возможно через экземпляр компонента, возвращаемый событием create.
Ng-dynamic-component может быть лучшим и самым простым решение в целом, но я еще не проверял это.
Ответ @Long Field на месте! Вот еще один (синхронный) пример:
Жить по адресу http://plnkr.co/edit/fdP9Oc .
источник
ngAfterViewInit
вызов сconst template
не будет работать. Но если ваша задача заключалась в том, чтобы сократить описанный выше детально описанный подход (создать шаблон, создать компонент, создать модуль, скомпилировать его, создать фабрику .. создать экземпляр) ... вы, вероятно, сделали этоДолжно быть, я пришел на вечеринку поздно, ни одно из решений здесь не показалось мне полезным - слишком грязное и казалось слишком обходным.
То , что я в конечном итоге делает это с помощью
Angular 4.0.0-beta.6
«s ngComponentOutlet .Это дало мне самое короткое и простое решение, записанное в файле динамического компонента.
my-component
- компонент, в котором отображается динамический компонентDynamicComponent
- компонент, который должен быть динамически собран, и он рендерится внутри my-componentНе забудьте обновить все угловые библиотеки до ^ Angular 4.0.0
Надеюсь это поможет. Удачи!
ОБНОВИТЬ
Также работает для угловых 5.
источник
2019 июнь ответ
Отличные новости! Похоже, что пакет @ angular / cdk теперь имеет первоклассную поддержку порталов !
На момент написания статьи я не находил вышеуказанные официальные документы особенно полезными (особенно в отношении отправки данных и получения событий от динамических компонентов). В итоге вам необходимо:
Шаг 1) Обновите свой
AppModule
Импортируйте
PortalModule
из@angular/cdk/portal
пакета и зарегистрируйте ваши динамические компоненты внутриentryComponents
Шаг 2. Вариант A: если вам НЕ нужно передавать данные и получать события от ваших динамических компонентов :
Увидеть это в действии
Шаг 2. Вариант B: если вам действительно необходимо передавать данные и получать события от ваших динамических компонентов :
Увидеть это в действии
источник
Portal
подход отличается отngTemplateOutlet
иngComponentOutlet
? 🤔Я решил сжать все, что я узнал, в один файл . Здесь есть что взять, особенно по сравнению с до RC5. Обратите внимание, что этот исходный файл включает в себя AppModule и AppComponent.
источник
У меня есть простой пример, чтобы показать, как сделать угловой динамический компонент 2 RC6.
Скажем, у вас есть динамический шаблон HTML = template1 и вы хотите динамическую загрузку, сначала оберните в компонент
здесь template1 как html, может содержать компонент ng2
Начиная с rc6, @NgModule должен обернуть этот компонент. @NgModule, как и модуль в anglarJS 1, разделяет другую часть приложения ng2, поэтому:
(Здесь импортируйте RouterModule, так как в моем примере есть некоторые компоненты маршрута в моем html, как вы увидите позже)
Теперь вы можете скомпилировать DynamicModule как:
this.compiler.compileModuleAndAllComponentsAsync(DynamicModule).then( factory => factory.componentFactories.find(x => x.componentType === DynamicComponent))
И нам нужно положить выше в app.moudule.ts, чтобы загрузить его, пожалуйста, смотрите мой app.moudle.ts. Для получения дополнительной и полной информации проверьте: https://github.com/Longfld/DynamicalRouter/blob/master/app/MyRouterLink.ts и app.moudle.ts
и посмотрите демо: http://plnkr.co/edit/1fdAYP5PAbiHdJfTKgWo?p=preview
источник
В Angular 7.x я использовал Angular-элементы для этого.
Установите @ angular-elements npm i @ angular / elements -s
Создать вспомогательный сервис.
Обратите внимание, что пользовательский тег элемента должен отличаться от углового селектора компонента. в AppUserIconComponent:
и в этом случае имя пользовательского тега я использовал «значок пользователя».
или вот так:
(в шаблоне):
Обратите внимание, что во втором случае вы должны передать объекты с помощью JSON.stringify и после этого снова проанализировать его. Я не могу найти лучшего решения.
источник
document.createElement(tagName);
Решил это в Angular 2 Final, просто используя директиву dynamicComponent из ng-dynamic .
Использование:
Где шаблон - это ваш динамический шаблон, а контекст может быть установлен для любой динамической модели данных, с которой вы хотите связать свой шаблон.
источник
Я хочу добавить несколько подробностей к этой замечательной публикации Радима.
Я взял это решение, немного поработал над ним и быстро столкнулся с некоторыми ограничениями. Я просто обрисую их, а затем дам решение.
Я сделал еще один вопрос, основанный на этом посте, о том, как добиться этих ограничений, который можно найти здесь:
рекурсивная динамическая компиляция шаблонов в angular2
Я просто обрисую ответы на эти ограничения, если вы столкнетесь с той же проблемой, что и я, поскольку это сделает решение более гибким. Было бы здорово, если бы первоначальный плункер тоже обновлялся.
Чтобы включить вложение динамической детализации друг в друга, вам нужно добавить DynamicModule.forRoot () в операторе импорта в type.builder.ts
Кроме того, было невозможно использовать
<dynamic-detail>
внутри одной из частей редактор строк или текстовый редактор.Чтобы включить это вам нужно изменить
parts.module.ts
иdynamic.module.ts
Внутри
parts.module.ts
вам нужно будет добавитьDynamicDetail
вDYNAMIC_DIRECTIVES
Кроме того,
dynamic.module.ts
вам нужно удалить dynamicDetail, так как они теперь являются частью частейРаботающий модифицированный поршень можно найти здесь: http://plnkr.co/edit/UYnQHF?p=preview (я не решил эту проблему, я всего лишь мессенджер :-D)
Наконец, было невозможно использовать шаблоны в деталях, созданных на динамических компонентах. Решением (или обходным путем. Я не уверен, является ли это угловой ошибкой или неправильным использованием фреймворка) было создание компилятора в конструкторе вместо его внедрения.
Затем используйте
_compiler
для компиляции, тогда templateUrls также включены.Надеюсь, это поможет кому-то еще!
С наилучшими пожеланиями Мортен
источник
Следуя отличному ответу Radmin, для всех, кто использует angular-cli версии 1.0.0-beta.22 и выше, нужен небольшой твик.
COMPILER_PROVIDERS
больше не может быть импортирован (подробности см. в angular-cli GitHub ).Таким образом, обходной путь здесь заключается в том, чтобы вообще не использовать
COMPILER_PROVIDERS
иJitCompiler
вproviders
разделе, а использоватьJitCompilerFactory
из '@ angular / compiler' вместо этого вот так внутри класса построителя типов:Как видите, он не является инъекционным и поэтому не имеет никаких зависимостей от DI. Это решение также должно работать для проектов, не использующих angular-cli.
источник
Я сам пытаюсь понять, как я могу обновить RC4 до RC5, и, таким образом, я наткнулся на эту запись, и новый подход к созданию динамических компонентов все еще остается для меня загадкой, поэтому я не буду предлагать что-либо о распознавателе фабрики компонентов.
Но я могу предложить немного более четкий подход к созданию компонентов в этом сценарии - просто используйте переключатель в шаблоне, который создаст редактор строк или текстовый редактор в соответствии с некоторыми условиями, например так:
И, кстати, «[» в выражении [prop] имеет значение, это указывает на одностороннюю привязку данных, поэтому вы можете и даже должны опустить их в случае, если вы знаете, что вам не нужно связывать свойство с переменной.
источник
switch
/case
содержит несколько решений. Но представьте, что сгенерированный шаблон может быть очень большим ... и отличаться для каждой сущности, различаться по безопасности, различаться по статусу сущности, по каждому типу свойства (число, дата, ссылка ... редакторы) ... В таком случае, Решив это в HTML-шаблон сngSwitch
создаст большой, очень-очень большойhtml
файл.Это пример динамических элементов управления формы, сгенерированных с сервера.
https://stackblitz.com/edit/angular-t3mmg6
Этот пример динамических элементов управления Form находится в компоненте add (здесь вы можете получить Formcontrols с сервера). Если вы видите метод addcomponent, вы можете увидеть элементы управления Forms. В этом примере я не использую угловой материал, но он работает (я использую @ work). Это цель для угловых 6, но работает во всех предыдущих версиях.
Нужно добавить JITComplierFactory для AngularVersion 5 и выше.
Спасибо
Виджай
источник
Для этого конкретного случая лучше использовать директиву для динамического создания компонента. Пример:
В HTML, где вы хотите создать компонент
Я бы подошел и разработал директиву следующим образом.
Таким образом, в ваших компонентах
ng-container
будут доступны текст, строка, дата, что угодно - независимо от того, какую конфигурацию вы передаете в HTML в элементе.Конфигурация,
yourConfig
может быть одинаковой и определять ваши метаданные.В зависимости от вашей конфигурации или типа ввода директива должна действовать соответствующим образом, и из поддерживаемых типов она будет отображать соответствующий компонент. Если нет, то будет записано сообщение об ошибке.
источник
Основываясь на ответе Офира Стерна, вот вариант, который работает с AoT в Angular 4. Единственная проблема, с которой я столкнулся, это то, что я не могу внедрить какие-либо службы в DynamicComponent, но я могу с этим смириться.
примечание: я не тестировал с Angular 5.
Надеюсь это поможет.
Ура!
источник