Angular – Building an ActionBar – Move elements to a different container when there is no space in the current container

I want to create a custom ActionBar library. The user that wants to use the ActionBar can insert actions, that will get rendered by ng-content.
When the ActionBar is too small to display all the actions I want to display a dropdown with the remaining actions that don’t fit into the bar.
I got a ResizeObserver that handels the hiding/showing.

Calling the ActionBar

 <action-bar>
        <action-bar-left>
            // user action to display in the action bar
            <button class="btn accept" (click)="onActionBarBtnClick('accept')"></button>
        </action-bar-left>
 </action-bar>

ActionBarLeft.component.html

<ng-content></ng-content>
<button #dropDownButton class="action-bar-not-visible-elements" (click)="showOverlay()" [ngClass]="elementsHidden ? '' : 'action-bar-dropdown-hidden'">
    <div class="action-bar-overlay-container">
        <div [ngClass]="overlayShown ? 'action-bar-overlay-shown' : ''" #overlay>
        <!--actions that don't fit will get rendered here-->
        </div>
    </div>
</button>

Currently I am using the Renderer2 to move the item from the ActionBar to the Dropdown and vice-versa. That works quiet well except that it dosn’t get the order quiet right. When I resize the ActionBar many times the actions are not in the same order as initialy.

ActionBarLeft.component.ts

constructor(private _host: ElementRef, private _renderer: Renderer2, private _ngZone: NgZone) { }

public ngAfterViewInit(): void {
    this._ngZone.runOutsideAngular(() => {
        this._resizeObserver = new ResizeObserver((_entries, _observer) => {
            this.displayActions();
        });
        this._resizeObserver.observe(this._host.nativeElement);
    });
}

private displayActions(): void {
    this.elementsHidden = false;
    this.resetOverlay();
    const hostElem = this._host.nativeElement;
    let availableWidth = this._host.nativeElement.clientWidth - this._reservedDropDownSpace;
    const notVisible = [];
    for (const el of hostElem.children) {
        availableWidth -= el.clientWidth;
        if (availableWidth < 0) {
            if (el.className.startsWith("action-bar-not-visible-elements")) {
                continue;
            }
            this.elementsHidden = true;
            notVisible.push(el);
        }
    }
    for (const el of notVisible) {
        this._renderer.appendChild(this._overlay.nativeElement, el);
    }
}

private resetOverlay(): void {
    for (const child of this._overlay.nativeElement.children) {
        this._renderer.insertBefore(this._host.nativeElement, child, this._dropDownButton.nativeElement);
    }
}

The Renderer2 is quiet comfortable because I can get the size of the ActionBar elements so that I can precisely determine which element fits into the bar.

I do not want to use the Render2 because of the DOM manipulation and because it dosn’t keep the order of the actions.
If there is no better solution and someone knows how to keep the order, I would stickt to the Renderer2.

I tried StructuralDirectives:

viewContainerRef.createEmbeddedView(templateRef);

The problem is, that I can’t get the width of the rendered Element that way.

What I want to do is:

  • Let the user add ActionBarActions inline
  • Determine which actions fit into the ActionBar
  • display the other actions that don’t fit in a dropdown
  • when resizing the window, it should be recalculated if the actions fit into the ActionBar

    There is an ActionBarRight, but it is not interesting in this context.

Source: New feed
Source Url Angular – Building an ActionBar – Move elements to a different container when there is no space in the current container