Angular2: rendering / reloading a component's template

前端 未结 3 2053
无人共我
无人共我 2021-01-03 02:13

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.

Desired behavior:

3条回答
  •  日久生厌
    2021-01-03 02:39

    I finally got it working!

    So, my problem was:

    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.

    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.

    (If you want the short story version, go at the bottom and read the Summary)

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

    MenuDirective (directive)

    @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 = $('');
                    $a.on('click', (e) => {
                        this.toggle($menuItem);
                        e.stopPropagation();
                        return false;
                    }).append(sign);
                });
        }
    }
    

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

    How does renderSubMenus() behave:

    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

    
    

    And that would be how I build the menu.

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

    IBOsNavigationElement (component)

    @Component({
        selector: '[ibos-navigation-element]',
        template: `
             {{'IBOs' | i18n}}
            
            
        `
    })
    export class IBOsNavigationElement implements OnInit, DoCheck {
        private $menuElement: any;
        private navigationList = [];
        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. 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).

    Summary:

    To 'reload' the template:

    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.

提交回复
热议问题