使用 ViewContainerRef 探索Angular DOM操作
英文原版: _翻译:giscafer 每当我读到关于使用Angular DOM的操作时,我总是会看到其中的一个或几个类: 如果你学习过 新的 Angular 版本运行在不同的平台上——在浏览器上,在移动平台上,或者在 web worker 中。因此,需要在平台特定API 和框架接口之间进行抽象级别的抽象。从 Angular 来看,这些抽象的形式有以下的参考类型: @ViewChild在探索DOM抽象之前,让我们了解一下如何在组件/指令类( component/directive class)中访问这些抽象。Angular 提供了一个称为DOM查询的机制。它以 通常,这些装饰器与模板引用变量一起工作。模板引用变量(template reference variable) 仅仅是模板中的DOM元素的命名引用。您可以将其视为与 @Component({ selector: 'sample',template: ` <span #tref>I am span</span> ` }) export class SampleComponent implements AfterViewInit { @ViewChild("tref",{read: ElementRef}) tref: ElementRef; ngAfterViewInit(): void { // outputs `I am span` console.log(this.tref.nativeElement.textContent); } } ViewChild decorator 的基本语法如下: @ViewChild([reference from template],{read: [reference type]}); 在这个示例中,您可以看到,我将 好了,现在我们知道了如何查询引用,让我们开始探索它们。 ElementRef这是最基本的抽象概念。如果您观察它的类结构,您将看到它只包含与之关联的原生元素(native element)。它对于访问原生DOM元素非常有用,正如我们在这里看到的: // outputs `I am span` console.log(this.tref.nativeElement.textContent); 然而,这种用法却被 Angular 团队 所劝阻。它不仅会带来安全风险,而且还会在应用程序和呈现层之间产生紧密耦合,使得在多个平台上运行应用程序变得困难。我认为,它不是访问
@Component({ selector: 'sample',... export class SampleComponent{ constructor(private hostElement: ElementRef) { //outputs <sample>...</sample> console.log(this.hostElement.nativeElement.outerHTML); } 因此,虽然组件可以通过DI访问它的宿主元素,但 ViewChild decorator 通常会在其视图(模板)(view (template))中获得对DOM元素的引用。指令的副作用——他们没有任何视图模板(views),他们通常直接与他们所依附的元素一起工作。 TemplateRef对于大多数web开发人员来说,模板的概念应该是熟悉的。模板是一组DOM元素,在应用程序的视图中可以重用。在HTML5标准引入模板标签template之前,大多数模板都是在一个带有一些 <script id="tpl" type="text/template"> <span>I am span in template</span> </script> 这种方法当然有许多缺点,比如语义和手动去创建DOM模型的必要性。使用模板标签 <script> let tpl = document.querySelector('#tpl'); let container = document.querySelector('.insert-after-me'); insertAfter(container,tpl.content); </script> <div class="insert-after-me"></div> <template id="tpl"> <span>I am span in template</span> </template> Angular 拥抱HTML5的这种方法并实现 @Component({ selector: 'sample',template: ` <ng-template #tpl> <span>I am span in template</span> </ng-template> ` }) export class SampleComponent implements AfterViewInit { @ViewChild("tpl") tpl: TemplateRef<any>; ngAfterViewInit() { let elementRef = this.tpl.elementRef; // outputs `template bindings={}` console.log(elementRef.nativeElement.textContent); } } 框架从DOM中删除模板元素,并在其位置插入注释。这就是呈现时的样子: <sample> <!--template bindings={}--> </sample> 通过它本身, ViewRef
Angular 支持两种视图:
Creating embedded view (创建嵌入视图)模板仅包含视图的蓝图。可以使用前面提到的 ngAfterViewInit() { let view = this.tpl.createEmbeddedView(null); } Creating host view(创建宿主视图)当组件被动态实例化时,会创建宿主视图。使用 constructor(private injector: Injector,private r: ComponentFactoryResolver) { let factory = this.r.resolveComponentFactory(ColorComponent); let componentRef = factory.create(injector); let view = componentRef.hostView; } 在 Angular 中,每个组件都被绑定到一个注入器(injector)的特定实例,因此我们在创建组件时传递当前的注入器实例。另外,不要忘记必须将动态实例化的组件添加到模块或托管组件的 因此,我们已经看到了如何创建嵌入式视图和宿主视图。一旦创建了视图,就可以使用 ViewContainerRef表示一个容器,其中可以附加一个或多个视图。 这里要提到的第一件事是,任何DOM元素都可以用作视图容器。有趣的是,Angular 在元素内部没有插入视图,而是在元素绑定到 通常,一个好的候选对象可以标记一个 @Component({ selector: 'sample',template: ` <span>I am first span</span> <ng-container #vc></ng-container> <span>I am last span</span> ` }) export class SampleComponent implements AfterViewInit { @ViewChild("vc",{read: ViewContainerRef}) vc: ViewContainerRef; ngAfterViewInit(): void { // outputs `template bindings={}` console.log(this.vc.element.nativeElement.textContent); } } 正如其他DOM抽象一样, Manipulating views (操作视图)
class ViewContainerRef { ... clear() : void insert(viewRef: ViewRef,index?: number) : ViewRef get(index: number) : ViewRef indexOf(viewRef: ViewRef) : number detach(index?: number) : ViewRef move(viewRef: ViewRef,currentIndex: number) : ViewRef } 我们前面已经看到了如何从模板和组件手动创建两种视图。一旦我们有了视图,我们就可以使用insert方法将它 @Component({ selector: 'sample',template: ` <span>I am first span</span> <ng-container #vc></ng-container> <span>I am last span</span> <template #tpl> <span>I am span in template</span> </template> ` }) export class SampleComponent implements AfterViewInit { @ViewChild("vc",{read: ViewContainerRef}) vc: ViewContainerRef; @ViewChild("tpl") tpl: TemplateRef<any>; ngAfterViewInit() { let view = this.tpl.createEmbeddedView(null); this.vc.insert(view); } } 有了这个实现,生成的html就像这样: <sample> <span>I am first span</span> <!--template bindings={}--> <span>I am span in template</span> <span>I am last span</span> <!--template bindings={}--> </sample> 为了从DOM中删除一个视图,我们可以使用 Creating Views (创建视图)
class ViewContainerRef { element: ElementRef length: number createComponent(componentFactory...): ComponentRef<C> createEmbeddedView(templateRef...): EmbeddedViewRef<C> ... } 这些都是我们在上面手工完成的简单方便的包装。它们从模板或组件创建视图,并将其插入指定的位置。 ngTemplateOutlet 和 ngComponentOutlet虽然知道底层机制是如何工作的总是很好,但通常都希望有某种快捷方式。此快捷方式以两种指令形式出现: ngTemplateOutlet它将DOM元素标记为 @Component({ selector: 'sample',template: ` <span>I am first span</span> <ng-container [ngTemplateOutlet]="tpl"></ng-container> <span>I am last span</span> <template #tpl> <span>I am span in template</span> </template> ` }) export class SampleComponent {} 您可以看到,我们在组件类中不使用任何实例化代码的视图。非常方便。 ngComponentOutlet该指令类似于 <ng-container *ngComponentOutlet="ColorComponent"></ng-container> 总结现在,所有这些信息似乎都很容易消化,但实际上它是相当连贯的,并在通过视图操作DOM的过程中形成了一个清晰的理想模型。您可以通过使用 原文:https://github.com/giscafer/g... (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |