Angular2:渲染/重新加载组件的模板 [英] Angular2: rendering / reloading a component's template

查看:333
本文介绍了Angular2:渲染/重新加载组件的模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

理想情况下,我需要重新加载/重新渲染组件的模板,但是如果有更好的方法可以实现,我将很乐意实现.

Ideally I would need to reload / rerender my component's template but if there is a better way to do this I will gladly implement it.

期望的行为:

所以,我有一个菜单元素组件.当(在另一个组件中)我单击一个IBO (某种客户",可以说)被单击时,我需要添加(我'我正在使用*ngIf)菜单中的一个新选项,该选项为 IBO Details 和一个子列表.

So, I have a component for a menu element. When (in another component) I click an IBO (some sort of 'client', per say) is clicked I need to add (I'm using *ngIf) a new option in the menu that would be IBO Details and a child list.

IBOsNavigationElement组件(菜单组件):

IBOsNavigationElement component (menu 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>
            <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,将填充列表,并且,因此,我需要新选项显示在菜单中.

Initially, navigationList will be empty [], because the user didn't click any IBO (client), but as soon as an IBO is clicked the list will be populated and, therefore, I need the new option to appear in the menu.

使用此代码,当我单击一个IBO时,会创建<li>元素及其子元素,但是:我只看到<li>元素.

With this code, when I click an IBO, the <li> element and it's children are created but: I only see the <li> element.

问题:

菜单选项已生成,但布局样式未处理.需要使用所有元素对其进行初始化,以便了解如何显示菜单选项.

The menu option is generated but not proccessed by the layout styles. It needs to be initialized with all the elements in order to know how to display the menu options.

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

注意:

如果我使用不带*ngIf的模板,效果很好,但是从一开始我就没有任何意义的 IBO Details 选项,因为初始化时没有单击任何IBO./p>

If I use the template without the *ngIf, works well but I would have from the first moment an IBO Details option that has no sense, because no IBO has been clicked when initialized.

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>
    `

所需的输出:

在单击任何东西之前(在init上):

Before clicking anything (on init):

点击IBO (客户端)后:

更新1:

阐明我的意思:

已生成菜单选项,但布局样式未对其进行处理

The menu option is generated but not proccessed by the layout styles

如果,我的菜单组件在没有*ngIf的情况下被初始化:

If, my menu component is initialized without the *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>

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

Then the layout styles can generate the menu output (see images) according to this structure:

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

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

And therefore add the + symbol and the submenu behavior, etc.

但是,如果初始化时不包含所有元素(当*ngIffalse时,则不渲染<li>并且它的子元素,因此布局在绘制/创建菜单时不会考虑它们)在渲染后添加,那么它们将存在于源代码中,但由于以下原因,我们将无法在菜单中看到它们:

But, if it's initialized without all the elements (when *ngIf is false the <li> and it's children are not rendered so the layout does not take them into account to draw/create the menu) and these elements are added after the rendering, then they will exist in source code, but we won't be able to see them in the menu because:

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

推荐答案

我终于开始工作了!

所以,我的问题是:

当生成新的HTML元素(带有*ngIf)时,它们不会显示,因为它们没有得到与它们相同的处理处理其他菜单元素也可以.

When the new HTML elements are generated (with *ngIf) they don't get displayed because they don't get processed the same way as the other menu elements do.

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

So I asked how to reload or re-render the template with all the 'new' elements... But I did not find where to reload a component or a component's template. As instead, I applied the logic that process the menu to my updated template.

(如果您需要短篇小说版本,请在底部阅读摘要)

因此,我深入研究了模板的最深层逻辑,并创建了一个指令来呈现菜单:

So, I dived into my template's deepest logic and created a directive to render the menu:

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().

So here I create the menu directive that renders the layout of the menu according to the existing html elements. And, as you can see, I isolated the behavior that processes the menu elements adding the + icon, creating the submenu feature, etc...: renderSubMenus().

renderSubMenus()的行为方式:

How does renderSubMenus() behave:

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

It loops through the DOM elements of the nativeElement passed as parameter and applies the logic to display the menu in the correct way.

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>

这就是我构建菜单的方式.

And that would be how I build the menu.

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

Now let's see the IBOsNavigationElement component, that is included in the menu with the attribute [ibos-navigation-element].

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 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;
        }
    }
}

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

Ok, so what have I done different here? Several things...

  1. 我导入了指令MenuDirective,因此可以调用其renderSubMenus()方法.
  2. 我使用ElementReffind()选择要发送给this.menuDirective.renderSubMenus()的代码块.我通过它的class找到它,请参见:this.$menuElement.find('ul.renderMe').
  3. 我实现了Angular的 DoCheck 挂钩检测我想要的更改并将逻辑应用于该更改事件.请参阅ngDoCheck()方法,在其中检查列表navigationList是否已填充以及是否已经渲染了这段代码(我遇到了问题,因为渲染了太多次,并且我有6个+按钮:灾难).
  1. I import the directive MenuDirective so I can call its renderSubMenus() method.
  2. I use ElementRef and find() to select the block of code that I want to send to this.menuDirective.renderSubMenus(). I find it through its class, see: this.$menuElement.find('ul.renderMe').
  3. I implement Angular's DoCheck hook to detect the changes that I want and apply logic to that change event. See ngDoCheck() method where I check if the list navigationList is populated and if I have already rendered this block of code (I had issues because was rendering too many times and I had like 6 + buttons: disaster).

摘要:

重新加载" 模板:

  1. 我使用方法创建了一条指令,该指令应用了通常发生在init上的逻辑.
  2. 我在要重新加载的组件中实例了该指令.
  3. 使用ElementRef,我可以获得要'reload' 的模板部分.
  4. 我选择何时要应用'reload'方法,在我的情况下,我是使用ngDoCheck()做到的.您可以随时调用该方法.
  5. 我调用指令的'reload '方法,将我要重新加载的模板中代码的一部分作为参数传递给(如果愿意,我可以传递整个模板).
  6. 该方法将应用于模板中与我发送的逻辑相同的部分,如果我使用*ngIf 用隐藏的元素对组件进行实例化.
  1. I created a directive with a method that applies the logic that usually occurs on init.
  2. I instance that directive in the component that I want to reload.
  3. With ElementRef I get the portion of template that I want to 'reload'.
  4. I choose when I want to apply the 'reload' method, in my case I did it with ngDoCheck(). You can call that method whenever you want.
  5. I call the directive's 'reload' method passing as parameter the portion of code within my template that I want to reload (I could have passed the entire template if I wanted).
  6. The method will apply to the portion of template that I sent the same logic that would have applied if I instanced the component with the hidden elements by *ngIf.

因此,从技术上讲,我没有重新加载组件. 我向组件模板应用了与重新加载组件相同的逻辑.

So, technically, I did not reload the component. I applied to the component's template the same logic that would have been applied if I reloaded it.

这篇关于Angular2:渲染/重新加载组件的模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆