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

查看:51
本文介绍了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 详细信息 和一个子列表.

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 (client),但是一旦单击 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 时,会创建

  • 元素及其子元素,但是:我只看到
  • 元素.

    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 详细信息 没有意义的选项,因为没有 IBO初始化时被点击.

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

    期望输出:

    在点击任何东西之前(在初始化时):

    Before clicking anything (on init):

    在我点击 IBO 之后 (客户):

    After I clicked an IBO (client):

    更新 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 时,

  • 和它的子元素不会被渲染所以布局在绘制/创建菜单时不考虑它们)并且这些元素是在渲染后添加的,然后它们将存在于源代码中,但我们将无法看到它们在菜单中,因为:

    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 元素被生成(with *ngIf)时,它们不会被显示,因为它们没有被处理em> 与其他菜单元素相同.

    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,我得到了我想要重新加载"的模板部分.
    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.

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

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

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