Dynamische Placeholder im Inline-Editor
Neos, Editor UX, Code
In Neos gibt es leider noch keine Möglichkeit, out-of-the-box dynamische Platzhalter zu erstellen, welche auf Daten vom Content Repository basieren.
Hier ein kleiner Trick, wie dies trotzdem möglich ist.
In der yaml
Konfiguration von einer Property, welches Inline mit dem CKEditor bearbeitet werden kann, kann ein Placeholder definiert werden. Dies sollte auch immer gesetzt werden, weil sonst der Editor nicht weiss, wo er genau klicken soll, um den Text zu bearbeiten. Was ist aber wenn man zum Beispiel bei einer Seite den Titel der Seite ausgeben will, diesen aber überschreibbar gestalten will? Klar, man kann es im Inspektor lösen, aber wäre es nicht besser, den Titel der Seite als Placeholder auszugeben? Leider ist das mit den Bordmittel mit Neos nicht möglich, aber mit ein bisschen Fusion und Javascript ist dies möglich.
Da der Editor zu einem unbestimmten Zeitpunkt die Attribute setzt, muss erst wenn er dies erledigt hat das Attribute data-neos-placeholder
oder data-placeholder
(ab Neos UI Version 8.3.1) ersetzt werden. Eine Variante wäre dies mit einem setTimeout
zu erledigen, da ist jedoch nicht wirklich sicher, ob der Browser schon fertig ist. Oder aber der Benutzer sieht zu lange den im yaml
definierten Placeholder. Aber wie kann das sauber gelöst werden? Eigentlich ganz einfach: Mit einem Observer, welcher die Attribute beobachtet und darauf reagiert. Neben dem muss der Prototype Neos.Neos:Editable
um die Möglichkeit den Placeholder sowie einen Fallback zu setzten erweitert werden.
Lass uns zuerst mit der Erweiterung von dem Editable beginnen:
prototype(Neos.Neos:Editable) {
fallback = null
placeholder = ${this.fallback}
renderer {
editable.renderer.attributes.data-neos-placeholder-override = ${String.stripTags(String.replace(props.placeholder, '<br>', ' \n '))}
@process.fallback = ${value || props.fallback}
}
}
Neos.Neos:Editable
wird hierbei um zwei neue Properties erweitert: fallback
und placeholder
. Dabei wird per default placeholder
auf den gleichen Wert wie fallback
gesetzt. Damit kann einfach der Fallback gesetzt werden, welches 99% der benötigten Requirements abdeckt. Beim Placeholder werden <br>
zu Zeilenumbrüchen konvertiert und anschliessend werden alle HTML-Tags entfernt. Der Fallback wird mit einem einfach @process
erledigt.
Anschliessend wird folgendes Javascript im Backend eingebunden. Dieses prüft, ob ein Placeholder, ein dynamischer Placeholder und ob diese beiden Werte nicht den gleichen Wert haben und setzt gegebenenfalls den dynamischen Placeholder als Placeholder.
Ab Neos UI in der Version 8.3.1 sieht der Code folgendermassen aus:
function observerCallback(mutationList) {
for (const mutation of mutationList) {
if (mutation.type !== "attributes") {
return;
}
const target = mutation.target;
const placeholderTarget = target.querySelector("[data-placeholder]");
if (!placeholderTarget) {
return;
}
const override = target.getAttribute("data-neos-placeholder-override");
const original = placeholderTarget.getAttribute("data-placeholder");
if (!original || !override || original === override) {
return;
}
console.log(`Override placeholder '${original}' with '${override}'`);
placeholderTarget.setAttribute("data-placeholder", override);
}
}
const observer = new MutationObserver(observerCallback);
const config = {
childList: true,
subtree: true,
attributes: true,
attributeOldValue: true,
attributeFilter: ["data-neos-placeholder-override"],
};
observer.observe(document.body, config);
Und darunter folgendermassen:
function observerCallback(mutationList) {
for (const mutation of mutationList) {
if (mutation.type !== "attributes") {
return;
}
const target = mutation.target;
const original = target.getAttribute("data-neos-placeholder");
const override = target.getAttribute("data-neos-placeholder-override");
if (!original || !override || original === override) {
return;
}
target.setAttribute("data-neos-placeholder", override);
}
}
const observer = new MutationObserver(observerCallback);
const config = {
childList: true,
subtree: true,
attributes: true,
attributeOldValue: true,
attributeFilter: ["data-neos-placeholder-override"],
};
observer.observe(document.body, config);
Nun kann zum Beispiel bei einem Dokument der Titel der Seite als Fallback Wert gesetzt werden. Falls der Editor einen anderen Text setzt, wird dieser natürlich im Frontend ausgegeben, andernfalls wird der Fallback verwendet.
headline = Neos.Neos:Editable {
property = 'headline'
block = false
fallback = ${q(node).property('title')}
}
Ich hoffe, ich konnte dir mit diesem kleinen Trick helfen. Ich würde mich natürlich über ein kleines Feedback via Twitter freuen. Have fun!