Separation of concerns

8. Content und Component

In diesem Kapitel geht es um die Aufteilung von Objekten in "Content" und "Component" und welchen Vorteil und Nutzen daraus gewonnen wird.

Zuerst ein neuer NodeType

Fusion erlaubt es uns bekanntlich wiederverwendbare Komponenten zu erstellen. Um zu zeigen wie das in der Praxis aussieht, fügen wir unserer Neos-Seite ein Button-Element hinzu. Bauen wir doch dafür einen NodeType, damit auf der Seite beliebig Buttons eingefügt werden können.

Angefangen mit der NodeType-Definition.

"NeosTutorial.Site:Content.Button":
  superTypes:
    "Neos.Neos:Content": TRUE
  ui:
    label: "Button"
    group: "general"
    icon: "icon-hand-pointer"
  properties:
    text:
      type: "string"
      defaultValue: "Button Text"
      ui:
        inlineEditable: TRUE
    link:
      type: "string"
      ui:
        label: "Link"
        reloadIfChanged: true
        inspector:
          group: "config"
          position: 10
          editor: "Neos.Neos/Inspector/Editors/LinkEditor"

Der Button-NodeType wurde so definiert, dass der Button-Text inline editiert werden kann. Im Inspektor kann ein Link für den Button eingestellt werden.

Weiter geht es mit der Fusion-Implementierung.

prototype(NeosTutorial.Site:Content.Button) < prototype(Neos.Neos:ContentComponent) {
  link = ${q(node).property("link")}
  renderer = afx`  
    <Neos.Fusion:Tag tagName={node.context.inBackend ? "span" : "a"} attributes.href={props.link}>
      <button class="button is-primary">
        <Neos.Neos:Editable property="text" />
      </button>
    </Neos.Fusion:Tag>
  `
}

Im Fusion-Teil lassen wir den Button rendern. ${q(node).property("link")} ist eine sogenannte FlowQuery. Diese Queries werden verwendet um Inhalte der Content-Repository abzurufen. In diesem Fall wird die link-Property vom Button-NodeType, welcher über den Inspektor gesetzt wird, abgerufen. Je nachdem ob man sich im Neos-Backend befindet, wird ein span-Tag anstelle eines a-Tags verwendet um beim Klick auf den Button nicht auf eine andere Seite zu springen.

Ein Banner mit Button

Jetzt haben wir jeweils einen NodeType für einen Banner und einen Button erstellt. Was ist aber wenn wir in unserem Banner einen Button haben wollen, der nicht einmal inline editierbar sein muss? Um das zu ermöglichen gibt es natürlich einige Vorgehensweisen. Dabei sind manche besser und andere schlechter. Als Gedankenexperiment könnte man sich ein paar Lösungen erörtern:

  • Man kopiert den Code des Buttons in den Banner. Das Problem ist somit schnell gelöst, nur ähnelt der Code dadurch schnell einem italienischen Nudelgericht.
  • Man verwendet im Banner den Button-Prototype. Dann muss aber der Button-Text über die NodeType-Definition des Banners eingestellt werden und eigentlich soll der Button doch gar nicht inline editierbar sein?
  • Man trennt die Daten- und Präsentations-"Schicht" des Buttons auf. Oder anders gesagt man trennt den Button in Content- und Component-Objekte auf. Der Component-Teil befasst sich ausschließlich mit dem Präsentieren (rendern) des Buttons, während im Content-Teil die Daten abgerufen, verwaltet und zur Component übergeben werden. Überraschung, genau das machen wir jetzt auch.

Refactoring

Da wir bereits einen Content-Ordner haben, erstellen wir daneben auch einen Component-Ordner. Hier drin wird auch gleich eine Fusion-Datei für das Button-Component-Objekt erzeugt.

Die Component.Button.fusion sieht jetzt so aus:

// Resources/Private/Fusion/Component/Component.Button.Fusion
prototype(NeosTutorial.Site:Component.Button) < prototype(Neos.Fusion:Component) {
  link = ""
  text = ""
  renderer = afx`  
    <Neos.Fusion:Tag tagName={node.context.inBackend ? "span" : "a"} attributes.href={props.link}>
      <button class="button is-primary">
        {props.text}
      </button>
    </Neos.Fusion:Tag>
  `
}

Das Component-Objekt befasst sich jetzt nur noch mit dem Rendern und erhält seine Daten über Properties. Es werden keine FlowQueries ausgeführt. Wo zuvor <Neos.Neos:Editable property="text" /> stand, ist nun einfach {props.text}. Components erben in diesem Fall auch wieder von Neos.Fusion:Component. Sehen wir uns an wie sich das Content-Objekt geändert hat.

prototype(NeosTutorial.Site:Content.Button) < prototype(Neos.Neos:ContentComponent) {
  renderer = NeosTutorial.Site:Component.Button {
    link = ${q(node).property("link")}
    text = Neos.Neos:Editable {
      property = "text"
    }
  }
}

Da sich das Component-Objekt mit dem rendern befasst wird dies auch in den renderer gegeben. Dabei werden auch direkt die Properties mit Daten versehen. Ob man für Content-Objekte auch AFX verwendet, soll einem selbst überlassen sein.

Auch der Banner sollte in Component und Content aufgeteilt werden. Ist das erledigt, kann im Banner-Component-Objekt das Button-Component-Objekt eingefügt werden. 

prototype(NeosTutorial.Site:Component.Banner) < prototype(Neos.Fusion:Component) {
    text = ""
    renderer = afx`
      <div class="card">
        <div class="card-content level">
          <div class="title mb-0 level-left">
            {props.text}
          </div>
          <div class="level-right">
            <NeosTutorial.Site:Component.Button link="/kontakt" @children="text">
              Jetzt kontaktieren!
            </NeosTutorial.Site:Component.Button>
          </div>
        </div>
      </div>
    `
}

Aus Components kann nun auch das Layout der Seite gebaut werden. Als nächstes ließen zum Beispiel Header und Footer Components bauen, die in der Document.Page.fusion verwendet werden. Bis hierhin sollten die Basics genügen um den Großteil der Neos-Seite zu implementieren.

In den nächsten Kapiteln bauen wir einen Blog für die Tutorial-Seite in welchem Tutorial-Beiträge verfasst werden können.

Lesen Sie weitere Kapitel

Impulsgespräch

Kostenloses Impulsgespräch

Mirko Kaufmann

Ihr Ansprechpartner:
Mirko Kaufmann

info@kaufmann.digital
T: +49-5771-8438930

Nach oben