Sticky Header mit ANGULARJS Teil I/II

16 Januar 2017
Ingo Baasch
HTML-Fragmente mit ng-repeat und AngularJS innerhalb einer View duplizieren

Wo kann es nützlich sein, einen Teil der HTML-Struktur in einer View zu kopieren?

Die Notwendigkeit, HTML-Elemente zu duplizieren, ergab sich bei uns mit der Entwicklung einer responsiven Webanwendung. Bei einigen Ansichten sollte ein Sticky Header oder auch Sticky Navigation genannt zum Einsatz kommen. In unserem Fall war der Sticky Header ein Element, das ab einer bestimmten Scroll-Position im Browserfenster fest an seiner Position stehen bleibt. Im Gegensatz zum restlichen Inhalt, der weiter gescrollt werden kann. Da sowohl der Sticky Header als auch seine „Umgebung“ komplex aufgebaut sind, war es auch aus Gründen der Performance notwendig, eine Kopie dieses Elementes im HTML-DOM anzulegen. Daraus hat sich dann die Idee entwickelt, für das Duplizieren die Angular-Direktive ng-repeat zu verwenden.


Die Direktive ng-repeat

Die Direktive ng-repeat wird eigentlich dazu verwendet um im HTML-Template in einer Schleife über Datenstrukturen zu iterieren. Also beispielsweise über die Elemente eines Arrays oder über die Properties eines Objekts. Mit Hilfe von ng-repeat können diese Daten in der View zum Aufbau einer Liste oder zum Füllen einer Tabelle verwendet werden.

Dabei kopiert Angular bei jedem Schleifendurchlauf von ng-repeat das gesamte HTML-Fragment, welches sich innerhalb „seines“ Elementes befindet:

<div ng-repeat="value in array">
    <HTML-Fragment>
        ∙∙∙
    </HTML-Fragment>
</div>

So entsteht z. B. nach der Ausführung von ng-repeat bei zwei Durchläufen:

<div>
    <HTML-Fragment>
        ∙∙∙
    </HTML-Fragment>
    <HTML-Fragment>
        ∙∙∙
    </HTML-Fragment>
</div>

Dieser Mechanismus kann neben seinem eigentlichen Sinn (der Erzeugung von Listen und Tabellen) nun auch dazu verwendet werden HTML-Fragmente zu duplizieren. Damit können wir recht elegant eine Kopie von einem HTML-Fragment und allen darin enthaltenen Kind-Elementen erstellen:

<div ng-repeat="isCopy in [true, false]">
    <HTML-Fragment>
        ∙∙∙
    </HTML-Fragment>
</div>

Ein schöner Nebeneffekt ist dabei, dass die erzeugte Variable isCopy dazu verwendet werden kann, die beiden erzeugten Duplikate zu unterscheiden und zu identifizieren. Beim ersten Schleifendurchlauf nimmt isCopy den Wert true an, danach den Wert false.

Mögliche Verwendungen der Schleifenvariable:

ng-class="{'sticky-header-container': isCopy === true}"
ng-show="isCopy === false"
id="{{isCopy === false ? 'name1' : 'name2'}}"

Ist im HTML-Fragment ein Event-Handler angebunden, wird dieser von ng-repeat automatisch auch im Duplikat angelegt.

Die beiden erzeugten Duplikate können sich gemeinsam eine Instanz eines Controllers teilen (wenn der Controller außerhalb der ng-repeat-Schleife deklariert wird). Sie können aber auch jeweils eine eigene Instanz dieses Controllers verwenden (wenn der Controller innerhalb der ng-repeat-Schleife deklariert wird).


Welche Vorteile ergeben sich im Vergleich zum manuellen Kopieren des HTML-Fragments?

Identische HTML-Fragmente müssen nur einmal angelegt werden. Ergeben sich Veränderungen müssen diese ebenfalls nur einmal gepflegt werden und die Gefahr, dass dabei ein Duplikat vergessen wird besteht nicht mehr.


Welche Nachteile ergeben sich im Vergleich zum manuellen Kopieren des HTML-Fragments?

Die Komplexität des HTML-Fragments wird erhöht, da Unterschiede wie Styles, Sichtbarkeit und eindeutige Namen (id) mithilfe der Schleifenvariablen ausgedrückt werden müssen (siehe oben: Mögliche Verwendungen der Schleifenvariable).


Welche Vorteile ergeben sich im Vergleich zum programmatischen Kopieren des HTML-Fragments per JavaScript/ jQuery im Controller?

Der HTML-DOM muss nicht per JavaScript/jQuery manipuliert werden, sondern kann unter Einhaltung des „Angular-Way“ mithilfe von ng-repeat verändert werden. Die Logik für den Aufbau der Struktur des Templates bleibt dabei für den Entwickler innerhalb der View direkt ablesbar.

Weiterhin ist die Performance und die Reaktionsgeschwindigkeit für das Ein- und Ausblenden des duplizierten HTML-Fragmentes höher wenn es in den digest cycle von Angular eingebunden ist.

<div>

    <HTML-Fragment>

        ∙∙∙

    </HTML-Fragment>

    <HTML-Fragment>

        ∙∙∙

    </HTML-Fragment>

</div>