Angular2:渲染/重新加载组件的模板

时间:2017-04-13 15:06:25

标签: angular typescript angular2-template

理想情况下,我需要重新加载/重新呈现我的组件模板,但如果有更好的方法,我会很乐意实现它。

所需行为

所以,我有一个菜单元素的组件。当(在另一个组件中)我点击了一个IBO (某种&#39;客户端&#39;,按照说)点击了我需要添加< / em>(我使用*ngIf)菜单中的新选项将是 IBO详细信息和子列表。

IBOsNavigationElement组件(菜单组件):

@Component({
    selector: '[ibos-navigation-element]',
    template: `
        <a href="#"><i class="fa fa-lg fa-fw fa-group"></i> <span
                class="menu-item-parent">{{'IBOs' | i18n}}</span>
        </a>
        <ul>
            <li routerLinkActive="active">
                <a routerLink="/ibos/IBOsMain">{{'IBOs Main' | i18n}} {{id}}</a>
            </li>
            <li *ngIf="navigationList?.length > 0">
                <a href="#">{{'IBO Details' | i18n}}</a>
                <ul>
                    <li routerLinkActive="active" *ngFor="let navigatedIBO of navigationList">
                        <a href="#/ibos/IboDetails/{{navigatedIBO['id']}}">{{navigatedIBO['name']}}</a>
                    </li>
                </ul>
            </li>
        </ul>
    `
})
export class IBOsNavigationElement implements OnInit {
    private navigationList = <any>[];


    constructor(private navigationService: NavigationElementsService) {
        this.navigationService.updateIBOsNavigation$.subscribe((navigationData) => {
                this.navigationList.push(navigationData);
                log.d('I received this Navigation Data:', JSON.stringify(this.navigationList));
            }
        );
    }

    ngOnInit() {
    }
}

最初,navigationList将为空[],因为用户没有点击任何IBO (客户端),但只要点击了IBO,列表将被填充,因此,我需要在菜单中显示新选项。

使用此代码,当我点击IBO时,会创建<li>元素及其子元素,但是:我只看到<li>元素。 < / p>

问题:

菜单选项已生成,但未由布局样式执行。需要使用所有元素进行初始化,以了解如何显示菜单选项。

我需要重新加载该组件的模板才能正确显示菜单。

注意:

如果我使用不带*ngIf的模板,效果很好,但我会在第一时间看到一个没有意义的 IBO详细信息选项,因为初始化时没有点击过IBO

template: `
        <a href="#"><i class="fa fa-lg fa-fw fa-group"></i> <span
                class="menu-item-parent">{{'IBOs' | i18n}}</span>
        </a>
        <ul>
            <li routerLinkActive="active">
                <a routerLink="/ibos/IBOsMain">{{'IBOs Main' | i18n}} {{id}}</a>
            </li>
            <li>
                <a href="#">{{'IBO Details' | i18n}}</a>
                <ul>
                    <li routerLinkActive="active" *ngFor="let navigatedIBO of navigationList">
                        <a href="#/ibos/IboDetails/{{navigatedIBO['id']}}">{{navigatedIBO['name']}}</a>
                    </li>
                </ul>
            </li>
        </ul>
    `

期望输出:

在点击任何内容之前(在初始化时):

enter image description here

点击IBO (客户端)后

enter image description here

更新1:

澄清我的意思:

  

菜单选项已生成,但未由布局样式

执行

如果,我的菜单组件初始化时没有*ngIf

<li>
    <a href="#">{{'IBO Details' | i18n}}</a>
        <ul>
            <li routerLinkActive="active" *ngFor="let navigatedIBO of navigationList">
                <a href="#/ibos/IboDetails/{{navigatedIBO['id']}}">{{navigatedIBO['name']}}</a>
            </li>
        </ul>
</li>

然后布局样式可以根据以下结构生成菜单输出(参见图像)

<li>
   <a>
   <ul>
      <li *ngFor>
         <a>
      </li>
   </ul>
</li>

因此添加+符号和子菜单行为等

但是,如果它在没有所有元素的情况下进行了初始化(*ngIffalse<li>并且它的子节点未呈现,则布局不会将它们考虑在内以绘制/创建菜单)这些元素在>渲染后添加,然后它们将存在于源代码中,但我们无法在菜单中看到它们这是因为:

  • 没有创建+
  • 没有子菜单行为

3 个答案:

答案 0 :(得分:4)

Angular有两种变化检测策略:

当您在页面上有多个元素或希望对渲染过程有更多控制权时,OnPush策略可以获得更好的性能。

要使用此策略,您必须在组件中声明它:

@Component({
    selector: '[ibos-navigation-element]',
    template: `...`,
    changeDetection: ChangeDetectionStrategy.OnPush
})

并注入构造函数:

constructor(
    private changeDetector: ChangeDetectorRef,
) {}

如果要触发更改检测,则可以重新呈现组件 (在您的情况下,在将新的IBO /客户端添加到模型之后),请致电:

this.changeDetector.markForCheck();

查看官方教程中的实时演示:http://plnkr.co/edit/GC512b?p=preview

如果问题不是关于更改检测,而是与CSS / SCSS样式有关, 请记住,在Angular 2中,每个组件都有自己的CSS类和 它们不是由“儿童”元素“继承”的。他们是完全孤立的 彼此。一种解决方案可能是创建全局CSS / SCSS样式。

答案 1 :(得分:2)

我终于开始工作了!

所以,我的问题是:

当生成新的HTML元素(使用*ngIf时,它们不会显示,因为它们没有处理相同其他菜单元素的方式。

所以我问如何使用所有'new'元素重新加载或重新呈现模板......但是我没有找到重新加载组件或组件模板的位置。相反,我将处理菜单的逻辑应用于我更新的模板。

(如果你想要短篇故事版,请在底部阅读摘要

所以,我潜入了模板最深刻的逻辑并创建了一个指令来呈现菜单:

MenuDirective(指令)

@Directive({
    selector: '[menuDirective]'
})
export class MenuDirective implements OnInit, AfterContentInit {

  constructor(private menu: ElementRef,
            private router: Router,
            public layoutService: LayoutService) {
    this.$menu = $(this.menu.nativeElement);
  }

  // A lot of boring rendering of layout

  ngAfterContentInit() {
        this.renderSubMenus(this.$menu);
    }

  renderSubMenus(menuElement) {
            menuElement.find('li:has(> ul)').each((i, li) => {
                let $menuItem = $(li);
                let $a = $menuItem.find('>a');
                let sign = $('<b class="collapse-sign"><em class="fa fa-plus-square-o"/></b>');
                $a.on('click', (e) => {
                    this.toggle($menuItem);
                    e.stopPropagation();
                    return false;
                }).append(sign);
            });
    }
}

所以在这里我创建了一个菜单指令,它根据现有的html元素呈现菜单的布局。而且,正如您所看到的,我隔离了处理菜单元素的行为添加+图标,创建子菜单功能等......:renderSubMenus()

renderSubMenus()的行为如何:

循环遍历作为参数传递的nativeElement的DOM元素,并应用逻辑以正确的方式显示菜单。

<强> menu.html

<ul menuDirective>    
    <li ibos-navigation-element></li>
    <li>
        <a href="#"><i class="fa fa-lg fa-fw fa-shopping-cart"></i> <span
                            class="menu-item-parent">{{'Orders' | i18n}}</span></a>
        <ul>
            <li routerLinkActive="active">
                <a routerLink="/orders/ordersMain">{{'Orders' | i18n}}</a>
            </li>
        </ul>
    </li>        
</ul>

这就是我建立菜单的方式。

现在让我们看一下IBOsNavigationElement组件,它包含在菜单中,其属性为[ibos-navigation-element]

IBOsNavigationElement(component)

@Component({
    selector: '[ibos-navigation-element]',
    template: `
        <a href="#"><i class="fa fa-lg fa-fw fa-group"></i> <span
                class="menu-item-parent">{{'IBOs' | i18n}}</span>
        </a>
        <ul class="renderMe">
            <li routerLinkActive="active">
                <a routerLink="/ibos/IBOsMain">{{'IBOs Main' | i18n}} {{id}}</a>
            </li>
            <li *ngIf="navigationList?.length > 0">
                <a href="#">{{'IBO Details' | i18n}}</a>
                <ul>
                    <li routerLinkActive="active" *ngFor="let navigatedIBO of navigationList">
                        <a href="#/ibos/IboDetails/{{navigatedIBO['id']}}">{{navigatedIBO['name']}}</a>
                    </li>
                </ul>
            </li>
        </ul>
    `
})
export class IBOsNavigationElement implements OnInit, DoCheck {
    private $menuElement: any;
    private navigationList = <any>[];
    private menuRendered: boolean = false;

    constructor(private navigationService: NavigationElementsService, private menuDirective: MenuDirective, private menuElement: ElementRef) {
        this.$menuElement = $(this.menuElement.nativeElement);

        this.navigationService.updateIBOsNavigation$.subscribe((navigationData) => {
                this.navigationList.push(navigationData);
                log.d('I received this Navigation Data:', JSON.stringify(this.navigationList));
            }
        );
    }

    ngOnInit() {
    }

    ngDoCheck() {
        if (this.navigationList.length > 0 && !this.menuRendered) {
            log.er('calling renderSubMenus()');
            this.menuDirective.renderSubMenus(this.$menuElement.find('ul.renderMe'));
            this.menuRendered = true;
        }
    }
}

好的,那我在这里做了什么不同?好几件事......

  1. 我导入了指令MenuDirective,因此我可以调用其renderSubMenus()方法。
  2. 我使用ElementReffind()来选择我要发送给this.menuDirective.renderSubMenus()的代码块。我通过class找到它,请参阅:this.$menuElement.find('ul.renderMe')
  3. 我实现了Angular的DoCheck挂钩来检测我想要的更改并将逻辑应用于该更改事件。请参阅ngDoCheck()方法,我在其中检查列表navigationList是否已填充,以及我是否已经渲染了此代码块(我遇到了问题,因为渲染次数太多而且我喜欢6 {{ 1}}按钮:灾难)
  4. <强>要点:

    '重新加载'模板:

    1. 我使用方法创建了一个指令,该方法应用了通常在init 上发生的逻辑。
    2. 我在我要重新加载的组件中实例化该指令。
    3. 使用+我得到了我想要'重新加载'的模板部分。
    4. 我选择何时应用'reload'方法,在我的情况下,我使用ElementRef。您可以随时调用该方法。
    5. 我调用指令的'reload '方法作为参数传递我想要重新加载的模板中的代码部分(如果需要,我可以传递整个模板)
    6. 该方法将应用于我发送相同逻辑的模板部分,如果我使用ngDoCheck() 隐藏元素对组件进行实例化。
    7. 所以,从技术上讲,我没有重新加载组件我在组件的模板中应用了与我重新加载时相同的逻辑。

答案 2 :(得分:0)

尝试使用ChangeDetectorRef.detectChanges() - 它的工作原理与$ scope相同。来自Angular 1的$ digest()。

注意:必须将ChangeDetectorRef注入组件。