Component Fusion Objects
Components are the main building blocks of Fusion which contain markup and templates. They provide encapsulation and reusability.
In case your Fusion component should render a Content Node, make sure it extends from Neos.Neos:ContentComponent.
In case your Fusion component is a plain "Atom" which has no connection and knowledge of a Node, extend from Neos.Fusion:Component
.
#Neos.Fusion:Component
Neos.Fusion:Component
is the base component prototype. It takes its properties and exposes them on the renderer
as props
. This leads to well-encapsulated components with a defined API, looking roughly like this:
prototype(My.Package:MyComponent) < prototype(Neos.Fusion:Component) {
# public API
title = 'Bold text'
description = 'This is a description'
# implementation
renderer = afx`
<p class="my-component">
<strong>{props.title}</strong><br/>
<span>{props.description}</span>
</p>
`
}
Output example:
My Title
This is a description
Neos.Fusion:Component allows arbitrary markup
With Neos.Fusion:Component, you can create arbitrary markup which is not modified in any way.
#Neos.Neos:ContentComponent
Additionally, there is Neos.Neos:ContentComponent
, which provides integration into the Neos UI.
It inherits from Neos.Fusion:Component and adds the Content Element Wrapping. The content element wrapping is responsible for ensuring a Node (represented by a Fusion object) is properly usable inside the Neos Backend. In detail, it does the following:
- Ensure that a single wrapping tag exists inside the component. If not, a wrapping
<div>
is added. - The wrapping
<div>
gets additional CSS classes and data attributes if rendered in the backend, needed for the Neos User Interface - containing f.e. the currently rendered node's identifier, the currently rendered Fusion path, and the Node's properties as JSON. - For this to work, the current
node
is read from the Fusion context byNeos.Neos:ContentElementWrapping
.
#What features does the Content Element Wrapping enable?
Content element wrapping is the basis for the following features:
- the possibility to select a Node in the Neos backend, directly on a page.
- the blue border around a selected node, and the floating toolbar attached to it.
- If a new child node is added, refresh only the single node, and not the whole page.
- If the node is removed, remove the node without refreshing the whole page.
- If a property is changed in the Inspector (and it is marked with
reloadIfChanged: true
inNodeTypes.yaml
), reload only the single Node and not the whole page.
#How should Neos.Neos:ContentComponent be used?
A common pattern is to define the markup inside a plain Neos.Fusion:Component
, and then reuse that inside a Neos.Neos:ContentComponent
, like in the following example:
prototype(My.Package:Content.Section) < prototype(Neos.Neos:ContentComponent) {
# Edited in inspector
title = ${q(node).property('title')}
# Inline Edited
description = Neos.Neos:Editable {
property = "description"
}
# delegate the implementation to the plain component
renderer = My.Package:Atoms.Section {
title = ${props.title}
description = ${props.description}
}
}
prototype(My.Package:Atoms.Section) < prototype(Neos.Fusion:Component) {
# public API
title = ''
description = ''
# implementation
renderer = afx`
<section class="my-component">
<h2>{props.title}</h2>
<div>{props.description}</div>
</section>
`
}
#Internal Implementation
Sometimes it is useful to check out the internal implementation of a Fusion prototype, as this can help to understand how the various Fusion objects work together.
So, let's have a look into the internal implementation of Neos.Neos:ContentComponent
:
# internal implementation, slightly shortened
prototype(Neos.Neos:ContentComponent) < prototype(Neos.Fusion:Component) {
@process.contentElementWrapping {
expression = Neos.Neos:ContentElementWrapping
@position = 'end 999999999'
}
}
So, you see there is not much magic going on here:
- We extend from
Neos.Fusion:Component
- Via
@process
, we take the full output and apply the content element wrapping (which does what is explained above).
#Neos.Neos:Editable
For making a property inline-editable using the Rich Text Editor, Neos.Neos:Editable
is used - usually inside a Neos.Neos:ContentComponent
. This looks usually like this:
prototype(My.Package:Content.Section) < prototype(Neos.Neos:ContentComponent) {
description = Neos.Neos:Editable {
property = "description"
}
renderer = afx`
<section class="my-component">
<div>{props.description}</div>
</section>
`
}
It is recommended to delegate the markup rendering to a Neos.Fusion:Component
, like in the example above ("How should Neos.Neos:ContentComponent be used?"). In this case, we recommend to keep the Neos.Neos:Editable
inside the Neos.Neos:ContentComponent
.
It is bad practice to use a Neos.Neos:Editable
inside a plain Neos.Fusion:Component
, as it breaks the logical layering and might lead to inconsistencies in the Neos UI.
#Legacy Prototypes
#Neos.Fusion:Template
Neos.Fusion:Template
is a Fluid-backed component. For new projects, you should not use Fluid anymore to define the frontend rendering of your output. Instead, use Neos.Fusion:Component
and AFX.
#Neos.Neos:Content
Neos.Fusion:Content
is a Fluid-backed content component. For new projects, you should not use Fluid anymore to define the frontend rendering of your output. Instead, use Neos.Neos:ContentComponent
and AFX.