Angular2 – компонент в динамически созданный элемент

Я использую google maps javascript api, и я должен отображать угловой компонент в InfoWindow.

В моем проекте я загружаю google-карту api с Jsonp службы Jsonp . Чем доступен доступ к объекту google.maps.Map . Позже в компоненте я создаю несколько маркеров и прикрепляю к ним прослушиватель кликов:

Тип :

 let marker = new google.maps.Marker(opts); marker.setValues({placeId: item[0]}); marker.addListener('click', (ev: google.maps.MouseEvent) => this.onMarkerClick(marker, ev)); 

А затем в обработчике click я хочу открыть информационное окно, содержащее угловой компонент:

Тип :

 private onMarkerClick(marker: google.maps.Marker, ev: google.maps.MouseEvent) { var div = document.createElement(); this.placeInfoWindow.setContent(div); // Magic should happen here somehow // this.placeInfoWindow.setContent(''); this.placeInfoWindow.open(this.map, marker); } 

То, что я закончил, было некоторым ванильным JS:

Тип :

  private onMarkerClick(marker: google.maps.Marker, ev: google.maps.MouseEvent) { let div = document.createElement('div'); div.className = 'map-info-window-container'; div.style.height = '140px'; div.style.width = '240px'; this.placeInfoWindow.setContent(div); this.placeInfoWindow.open(this.map, marker); this.placesService.getPlace(marker.get('id')).subscribe(res => { this.decorateInfoWindow(div, res.name, marker); }, error => { this.decorateInfoWindow(div, ':( Failed to load details: ', marker); }); } private decorateInfoWindow(containerEl: HTMLElement, title?:string, marker?:google.maps.Marker) { let h3 = document.createElement('h3'); h3.innerText = title; containerEl.appendChild(h3); let buttonBar = document.createElement('div'); let editButton = document.createElement('button') editButton.innerText = "Edit"; editButton.addEventListener('click', ev => { this.editPlace(marker); }); buttonBar.appendChild(editButton); containerEl.appendChild(buttonBar); } 

Проблема, как я узнал, заключается в том, что единственным жизнеспособным способом создания динамических компонентов является использование Angulars ViewContainerRef :

  • Как разместить динамический компонент в контейнере

Но нет документов или примеров, описывающих, как создать ViewContainerRef из динамически созданного элемента.


Возможно ли заставить структуру обрабатывать DOM каким-то образом? Как указано во многих streamах: «Угловое не обрабатывает innerHTML или appendChild ». Это полный тупик?

Второе: возможно ли использование реализации Renderer ? (Не знакомый с этим), я видел этот эксперимент с Canvas Renderer Experiment и теоретически, я думаю, он будет работать и с картой Google, поскольку мы можем экстраполировать, что карта – это просто особый вид canvasа. Доступна ли она в последней версии или она изменилась? DomRenderer не находится в документах, однако его можно найти в источниках.

Главное правило состоит в том, чтобы динамически создавать компоненты, необходимые для его производства.

1) Добавить динамический компонент в массив entryComponents кроме включения в declarations :

 @NgModule({ ... declarations: [ AppInfoWindowComponent, ... ], entryComponents: [ AppInfoWindowComponent, ... ], }) 

Это подсказка для углового компилятора для создания ngfactory для компонента, даже если мы не используем наш компонент непосредственно внутри какого-либо шаблона.

2) Теперь нам нужно ввести ComponentFactoryResolver в наш компонент / службу, где мы хотим получить ngfactory. Вы можете подумать о ComponentFactoryResolver как о хранилище компонентов

app.component.ts

 import { ComponentFactoryResolver } from '@angular/core' ... constructor(private resolver: ComponentFactoryResolver) {} 

3) Пришло время получить AppInfoWindowComponent фабрику:

 const compFactory = this.resolver.resolveComponentFactory(AppInfoWindowComponent); this.compRef = compFactory.create(this.injector); 

4) Имея фабрику, мы можем свободно ее использовать, как хотим. Вот несколько примеров:

  • ViewContainerRef.createComponent(componentFactory,...) вставляет компонент рядом с viewContainer.

  • ComponentFactory.create(injector, projectableNodes?, rootSelectorOrNode?) Просто создает компонент, и этот компонент может быть вставлен в элемент, который соответствует rootSelectorOrNode

Обратите внимание, что мы можем предоставить узел или селектор в третьем параметре функции ComponentFactory.create . Это может быть полезно во многих случаях. В этом примере я просто создам компонент, а затем вставляю его в некоторый элемент.

Метод onMarkerClick может выглядеть так:

 onMarkerClick(marker, e) { if(this.compRef) this.compRef.destroy(); // creation component, AppInfoWindowComponent should be declared in entryComponents const compFactory = this.resolver.resolveComponentFactory(AppInfoWindowComponent); this.compRef = compFactory.create(this.injector); // example of parent-child communication this.compRef.instance.param = "test"; const subscription = this.compRef.instance.onCounterIncremented.subscribe(x => { this.counter = x; }); let div = document.createElement('div'); div.appendChild(this.compRef.location.nativeElement); this.placeInfoWindow.setContent(div); this.placeInfoWindow.open(this.map, marker); // 5) it's necessary for change detection within AppInfoWindowComponent // tips: consider ngDoCheck for better performance this.appRef.attachView(this.compRef.hostView); this.compRef.onDestroy(() => { this.appRef.detachView(this.compRef.hostView); subscription.unsubscribe(); }); } 

5) Несомненно динамически созданный компонент не является частью дерева обнаружения изменений, поэтому нам также необходимо позаботиться об обнаружении изменений. Это можно сделать с помощью ApplicationRef.attachView(compRef.hostView) как было написано в примере выше, или мы можем сделать это с помощью ngDoCheck ( пример ) компонента, где мы создаем динамический компонент ( AppComponent в моем случае)

app.component.ts

 ngDoCheck() { if(this.compRef) { this.compRef.changeDetectorRef.detectChanges() } } 

Этот подход лучше, потому что он обновит динамический компонент только при обновлении текущего компонента. С другой стороны ApplicationRef.attachView(compRef.hostView) добавляет детектор изменений в корень дерева детектора изменений, и поэтому он будет вызываться при каждом типе обнаружения изменений.

Пример плунжера


Советы:

Поскольку addListener работает за пределами угловой зоны 2, нам нужно выполнить экспликацию нашего кода внутри угловой зоны 2:

 marker.addListener('click', (e) => { this.zone.run(() => this.onMarkerClick(marker, e)); }); 
  • Каков правильный способ обмена результатами сетевого звонка с угловым Http в RxJs 5?
  • Отслеживание положения прокрутки и уведомление об этом других компонентах
  • Angular 4 - обновить метаtags динамически для Facebook (Открыть график)
  • Угловой 2 - Услуги, требующие других услуг до вызова метода
  • Как установить связь между компонентом в угловом?
  • Цепочка RxJS Наблюдаемые данные http в Angular2 с использованием TypeScript
  • Угловой: не удается найти отличающийся поддерживающий объект «объект объекта»
  • angular2 bootstrap с данными из ajax call (s)
  • Общий валидатор почты в Angular2
  • Угловой 2 - возврат данных непосредственно из наблюдаемого
  • Лучший способ импортировать Observable из rxjs
  • Interesting Posts

    Фрагмент в ViewPager с использованием FragmentPagerAdapter пуст во второй раз

    Chrome getUserMedia не запрашивает разрешения на локальном уровне

    Как я могу использовать брандмауэр Windows, чтобы разрешить только службе Windows Update выполнять исходящее соединение?

    Альтернатива getRunningTasks в Android L

    Использование котировок в getRuntime (). Exec

    Передача растрового изображения между двумя действиями

    Как я могу установить разные тексты подсказок для каждого элемента в списке?

    Что такое аутентификация на токенах?

    Как справиться с категориальными особенностями с помощью spark-ml?

    Что гарантируется относительно размера указателя функции?

    MySQL InnoDB не освобождает дисковое пространство после удаления строк данных из таблицы

    Каков наилучший метод проверки адреса электронной почты Java?

    Подождите, пока вызов Ajax завершится с помощью Selenium 2 WebDriver

    Vista без браузера

    Каково максимальное разрешение C # .NET Bitmap?

    Давайте будем гением компьютера.