我有以下代码:
search.component.ts
import { CoreComponent } from './../../core/components/core.component';
import { SearchDropDownComponent } from './search.dropdown.component';
import { Component,Input,EventEmitter, Output, ViewChild} from "@angular/core";
@Component({
selector:'search',
templateUrl:'app/templates/search.component.html',
styleUrls:['app/templates/css/search.component.css']
})
export class SearchComponent
{
@Input() core : CoreComponent;
@Output() onSearch : EventEmitter<any> = new EventEmitter();
@ViewChild(SearchDropDownComponent) dropdown: SearchDropDownComponent;
private searchText:String="";
search(event:any) : void
{
if (+event.keyCode==13 || event.type=="click")
{
this.onSearch.emit(event);
return;
}
}
}
search.component.html
<div class="input-group" id="search">
<input class="form-control kblue-background-pale" [(ngModel)]="searchText" (keypress)=search($event) placeholder="Search" list="search-dropdown" type="text">
<search-dropdown [core]="this.core" [typed]="this.searchText"></search-dropdown>
<div class="input-group-btn">
<div class="btn-group" role="group">
<button (click)="search($event)" type="button" class="btn kblue-background-pale">
<span class="glyphicon glyphicon-search kblue" aria-hidden="true">
</span>
</button>
</div>
</div>
</div>
search.dropdown.component.ts
import { CoreBase } from './../../core/components/core.base';
import { ComponentFactoryService } from './../../services/services/component.fac';
import { SearchResultComponent } from './search.result.component';
import { CoreComponent } from './../../core/components/core.component';
import { Component,
Input,AfterViewInit ,
ContentChildren,ContentChild,
Output,Inject,forwardRef,OnChanges,ElementRef,ViewChild,ViewContainerRef,ViewChildren,QueryList } from "@angular/core";
@Component(
{
selector:'search-dropdown',
templateUrl:'app/templates/search.dropdown.component.html',
styleUrls:['app/templates/css/search.dropdown.component.css']
}
)
export class SearchDropDownComponent implements AfterViewInit , OnChanges
{
@Input() core : CoreComponent;
@ViewChild('searchresult', {read: ViewContainerRef}) p: ViewContainerRef;
@ViewChildren(SearchResultComponent) t: QueryList<any>;
@ViewChild('parent', {read: ViewContainerRef}) children: ViewContainerRef;
private MLENGTH=2;
private RLENGTH=0;
private searchLength=0;
constructor(private factory:ComponentFactoryService) {
}
@Input()
set typed(typed:string)
{
if (typed.length>this.searchLength)
{
if (typed.length>this.MLENGTH)
{
this.core.webservice(this.result,"communities",{search:typed});
}
this.searchLength=typed.length;
}
else
{
if (typed.length==this.RLENGTH)
{
this.p.clear();
this.searchLength=0;
}
}
console.log(this.p.length);
console.log(this.children);
// for(var index = 0; index < this.p.length; index++)
//{
// console.log(this.t);
// }
}
ngAfterViewInit() {
}
ngOnChanges()
{
console.log(this.p.element.nativeElement.childNodes);
console.log(this.children);
}
public result=(data:any)=>
{
data.forEach((item:any)=>
{
this.factory.getComponent(this.p,SearchResultComponent,item);
});
}
}
search.dropdown.component.html:
<datalist id="search-dropdown" #searchresult></datalist>
search.result.component.ts:
import { CoreBase } from './../../core/components/core.base';
import { Component,OnInit,TemplateRef} from "@angular/core";
@Component({
selector:'search-result',
templateUrl:'app/templates/search.result.component.html',
styleUrls:['app/templates/css/search.result.component.css']
})
export class SearchResultComponent extends CoreBase implements OnInit
{
private suggestions:String;
constructor()
{
super();
}
ngOnInit()
{
this.suggestions=this.parameters["search"];
}
}
search.result.component.html:
<option>{{suggestions}}</option>
component.fac.ts(组件工厂服务):
import { CoreBase } from './../../core/components/core.base';
import {
Injectable,
ReflectiveInjector,
ViewContainerRef,
ComponentFactoryResolver
} from '@angular/core';
@Injectable()
export class ComponentFactoryService
{
constructor(private componentFactoryResolver: ComponentFactoryResolver)
{
}
public getComponent(refDOM:ViewContainerRef, component:any,parameters:any)
{
let factory = this.componentFactoryResolver.resolveComponentFactory(component);
let injector = ReflectiveInjector.fromResolvedProviders([], refDOM.parentInjector);
let comp = factory.create(injector);
(<CoreBase>comp.instance).parameters=parameters;
comp.changeDetectorRef.detectChanges();
refDOM.insert(comp.hostView,0);
return comp.instance;
}
}
我想在Angular 2中开发一个搜索下拉建议小部件,当用户在输入框占位符中键入3个字符时,会向后端发出请求并返回json响应。每个搜索建议元素都在search.dropdown.component中动态加载,特别是在search.dropdown.component.html的#searchresult中。
组件search.result.component是动态注入的,但在外部呈现:
我想在datalist中注入动态组件,然后能够使用ViewChildren api访问属性建议search.result.component。
问题是搜索结果组件是在datalist之外呈现的。如果我在html中放置一个div或模板标签来渲染内部的动态元素,那么视图会正确呈现但是ViewChildren api 没有检索动态注入的组件。
如何使用在div标签内呈现的ViewChildren api组件进行访问?
谢谢
答案 0 :(得分:1)
所以你看到的是与角度路由器本身类似的东西,不是吗?看看这些来源:https://github.com/angular/angular/blob/master/modules/%40angular/router/src/directives/router_outlet.ts:
activate(...): void {
...
const component: any = <any>snapshot._routeConfig.component;
const factory = resolver.resolveComponentFactory(component);
...
this.activated = this.location.createComponent(factory, this.location.length, inj, []);
...
}
很快您就会有一个 ViewContainerRef ,它将当前组件引用为容器,并引用 ComponentFactoryResolver 。它们都是由构造函数注入的。
使用方法const factory
创建 componentFactory (上面的ComponentFactoryResolver.resolveComponentFactory(desiredComponent)
)。
然后使用方法this.activated
(此处为文档:https://angular.io/docs/ts/latest/api/core/index/ViewContainerRef-class.html#!#createComponent-anchor)将 desiredComponent (上面的ViewContainerRef.createComponent(componentFactory, index)
)添加到容器中。
所以在你的代码中,替换:
refDOM.insert(comp.hostView,0);
return comp.instance;
通过:
refDOM.createComponent(factory, refDOM.length, injector);
答案 1 :(得分:1)
有一个棘手的解决方案。 现在在Angular rc5之后,无法根据需要获得嵌套标记,但是如果你改变了:
<强> search.dropdown.component.html:强>
<datalist id="search-dropdown">
<template #searchresult />
</datalist>
你应该在html代码中找到一些谎言:
<data-list id="search-dropdown">
<!-- template -->
<search-result ... />
<search-result ... />
<search-result ... />
...
</data-list>
模板标签不是由角度渲染的,而是更改为注释(任何其他标签f.ex. div将显示为空div。
答案 2 :(得分:0)
我不明白你为什么使用@ViewChild ...你的search.dropdown.component.html
可能是这样的:
<datalist id="search-dropdown" #searchresult>
<div *ngFor="let result of results">
<search-result1 *ngIf="result.type==1" [suggestions]="result" ...></search-result1>
<search-result2 *ngIf="result.type==2" [suggestions]="result" ...></search-result2>
...
</div>
</datalist>
然后将您的sugestions作为组件中的输入进行分配以显示内容:
export class SearchResultComponent extends CoreBase implements OnInit
{
@Input() private suggestions:string;
...
并在search.dropdown.component.ts
:
export class SearchDropDownComponent implements AfterViewInit , OnChanges
{
private results: string[] = [];
onChange() {
updateResultsHere();
}
...