Basic form-based headless CMS systems are good for constrained and predefined content types. However, to give marketers the ability to create articles, page structure or create their own custom landing pages or microsites, the Magnolia SPA Editor will accelerate your Marketer and free up your development capacity.
The Visual SPA Editor is bridging the gap between enterprise-level content management systems and SPA frontend development.
What are we going to build?
We are going to build a Travel Website SPA promoting travel packages.
In the Hello Magnolia - Headless tutorial, we have built a basic Headless CMS Website. In the present tutorial, we create an application, but enable the Marketer to visually change the SPA.
Marketers can add new navigation items.
They can create new pages and arrange the content freely with a pre-defined list of Components.
Prerequisites
Magnolia CLI and required development tools. The instructions are here.
Install the demo project
To jumpstart our project, we will install our demo project.
The easiest way to do this is to clone our git repository.
? What Magnolia would you like to install?
1) magnolia-empty-webapp
2) magnolia-community-webapp
3) magnolia-community-demo-webapp
4) magnolia-dx-core-webapp
5) magnolia-dx-core-demo-webapp
Answer: 2Copy
Once the installation is finished, you will see an output similar to this on your console:
info Magnolia has been successfully setup for light development!
info You can now open a new terminal tab or window and start it up with the CLI command'mgnl start'
info Magnolia will be ready after a few seconds at localhost:8080/magnoliaAuthor. Username and password is superuserCopy
Now, pick up some handy configuration and sample images.
We include a React, Angular and Vue example, just choose which one you want to use.
In your terminal, go to the actual frontend app directory (for React, Angular or Vue).
The -g flag installs the Angular CLI globally on your computer.
cd ../spa/angular-minimalCopy
npm installCopy
ng serveCopy
cd ../spa/vue-minimalCopy
npm installCopy
npm run serveCopy
See It In Action
Let’s have a look at the running demo containing the starter content before we inspect the Starter Code. In the previous chapter, we have started Magnolia and built and started our SPA project.
Click Import action (You might need to scroll down in the action bar.)
For React, select the file ./magnolia/_dev/content-to-import/website.react-minimal.yaml.
For Angular, select the file ./magnolia/_dev/content-to-import/website.angular-minimal.yaml.
For Vue, select the file ./magnolia/_dev/content-to-import/website.vue-minimal.yaml.
Page Builder
In the Pages app, open the SPA Project by double-clicking on react-minimal, angular-minimal or vue-minimal depending on which project you have installed.
Now you can see the React, Angular or Vue app rendered in the Magnolia Page Builder. Go-ahead and try to change the title or add an image.
To see the images displayed in the SPA app on localhost:3000, you must add the web access GET right with path /dam/* to the anonymous user in the Security app in your Magnolia Author instance.
Hooray
You are editing a SPA!
Inspect the Code
Let’s see how our demo project is structured:
The blue and green components are fixed components, the developer controls them and their position. The magenta components inside the Area Component are controlled and freely arranged (added, moved, deleted, etc.) by the marketer. The marketer uses the Page Editor to edit the SPA with components built and defined by the developer.
You can look at it as the developer putting handles and knobs on frontend components, so that marketers and content authors can move them around and tune them with custom content.
The green Page Component and Area Component are provided by Magnolia.
The Page Component is responsible for rendering the top frontend component, and preparing the Magnolia Page Editor.
The Area Component is mapping the content created by the marketer, to the components defined by the developer, in order to render them. You can define multiple area components on the same Page Component, and a component can have one or more areas of its own.
Light Modules
You can share code and components in multiple light modules.
Shared Light Module: spa-lm
The Magnolia templates and dialogs are the same no matter the front-end framework, so the demo project puts all the shared content in the spa-lm module.
cd magnolia/light-modules/spa-lmCopy
Templates
Template definitions register the component with the Magnolia system. They also point to the Dialog definition.
cd templates/componentsCopy
headline.yaml
image.yaml
paragraph.yaml
Dialogs
Dialog definitions provision the form that the Marketers will use to tune and enter content. They define the fields and the names of the properties to be persisted.
cd ../../dialogs/componentsCopy
headline.yaml
image.yaml
paragraph.yaml
Application Light Module
The actual SPA will be deployed to a front-end framework-specific module.
We have defined two page templates, which the marketer will be able to include in our SPA in the page builder. In the page template, you can define areas and which components will be available in the areas.
React
Angular
Vue
cd ../../../react-minimal-lm;
cd templates/pages;Copy
cd ../../../angular-minimal-lm;
cd templates/pages;Copy
cd ../../../vue-minimal-lm;
cd templates/pages;Copy
We have two categories of templates: Page and Components.
In our source directory, we have two subdirectories: Page (containing Layout Components) and Components.
Pages
Basic
Contact
Personalization (React)
Components
Expander
Headline
Image
(List-)Item
List
Navigation
Paragraph
magnolia.config.js
The componentMappings in this file are what the Magnolia JS libraries use to determine which SPA component to instantiate, based on the templateID (for example: spa-lm:components/paragraph) which is received in the JSON from the Magnolia endpoint.
The Root Component (App.js in React, root.component.ts / root.component.html in Angular, App.vue in Vue) is the main entry point of our app. It defines the page structure and routing.
The imports of the Magnolia JS lib: @magnolia/react-editor or @magnolia/angular-editor.
The Page Component is defining the layout and the placement of the area components. Let’s have a look at the Basic Page. The EditableArea defines which content should be displayed.
In our case we have defined main and extras in our page template.
We learned earlier that the Magnolia SPA editor framework is helping to map the content structure created in Magnolia to frontend components. Let’s have a look at the Headline Component as an example.
You can see how the field defined in the dialog is passed directly through to the component!
Build a Component
In the Headless CMS tutorial, we have created a headless app to show tour packages. Using the Visual SPA Editor and the basic form-centric approach is not mutually exclusive. On the contrary, both approaches complement each other.
What are we going to build?
We will build a Tour List Component. Our Marketers will be able to choose and add the component anywhere they want.
Create a new component using light modules.
Create a dialog for Marketers to add a headline.
Create Tour List Component
Let’s create a new Component in Magnolia CMS first.
Next, we want to define where the component can be used. We will add our tourList Component to the available components of our Basic Page
React
Angular
Vue
cd ../../../react-minimal-lm/templates/pagesCopy
Edit the file basic.yaml and add following text to the availableComponents from the main Area:
TourList:id:spa-lm:components/tourListCopy
The full file will look like this:
title:"React: Basic"baseUrl:http://localhost:3000routeTemplate:'/{language}{{@path}}'# templateScript: /react-minimal-lm/webresources/build/index.html# The templateScript property is deprecated since Magnolia 6.2.30 for use with the SPA renderer.dialog:spa-lm:pages/basic$type:spaareas:main:title:MainAreaavailableComponents:Headline:id:spa-lm:components/headlineImage:id:spa-lm:components/imageParagraph:id:spa-lm:components/paragraphExpander:id:spa-lm:components/expanderList:id:spa-lm:components/listTourList:id:spa-lm:components/tourListextras:title:ExtrasAreaavailableComponents:Headline:id:spa-lm:components/headlineParagraph:id:spa-lm:components/paragraphList:id:spa-lm:components/listCopy
cd ../../../angular-minimal-lm/templates/pagesCopy
Edit the file basic.yaml and add following text to the availableComponents from the main Area:
TourList:id:spa-lm:components/tourListCopy
The full file will look like this:
title:"Angular: Basic"baseUrl:http://localhost:4200routeTemplate:'/{language}{{@path}}'# templateScript: /angular-minimal-lm/webresources/build/index.html# The templateScript property is deprecated since Magnolia 6.2.30 for use with the SPA renderer.dialog:spa-lm:pages/basicrenderType:spaclass:info.magnolia.rendering.spa.renderer.SpaRenderableDefinitionareas:main:title:MainAreaavailableComponents:Headline:id:spa-lm:components/headlineImage:id:spa-lm:components/imageParagraph:id:spa-lm:components/paragraphExpander:id:spa-lm:components/expanderList:id:spa-lm:components/listTourList:id:spa-lm:components/tourListextras:title:ExtrasAreaavailableComponents:Headline:id:spa-lm:components/headlineParagraph:id:spa-lm:components/paragraphList:id:spa-lm:components/listCopy
cd ../../../vue-minimal-lm/templates/pagesCopy
Edit the file basic.yaml and add following text to the availableComponents from the main Area:
TourList:id:spa-lm:components/tourListCopy
The full file will look like this:
title:'Vue: Basic'baseUrl:http://localhost:3000routeTemplate:'/{language}{{@path}}'# templateScript: /vue-minimal-lm/webresources/dist/index.html# The templateScript property is deprecated since Magnolia 6.2.30 for use with the SPA renderer.dialog:spa-lm:pages/basicrenderType:spaclass:info.magnolia.rendering.spa.renderer.SpaRenderableDefinitionareas:main:title:MainAreaavailableComponents:Headline:id:spa-lm:components/headlineImage:id:spa-lm:components/imageParagraph:id:spa-lm:components/paragraphExpander:id:spa-lm:components/expanderList:id:spa-lm:components/listTourList:id:spa-lm:components/tourListextras:title:ExtrasAreaavailableComponents:Headline:id:spa-lm:components/headlineParagraph:id:spa-lm:components/paragraphList:id:spa-lm:components/listCopy
Now we can already select the component in the Page Editor.
Of course, before we can see anything displayed, we have to implement the frontend component.
The Frontend Component
Our new component is now provisioned in Magnolia, and an instance of it is saved to the content repository. First, we want the component to display the Header entered by the Marketer. Once this is running and we have seen how to add and edit the component in the SPA Editor, we will extend the component to read and display our Tour List data.
Now, let’s build out our component in React, Angular or Vue.
Add the new created TourListComponent into your app.module.ts file. Add it as an import and to the declarations and entryComponents. (It might have been added by the ng command, but double-check entryComponents.)
Please ensure that the styleUrls path correctly points to the style file, it might have a .css or it might have a .scss extension. (It depends on your angular CLI setup. Tricky!)
Ensure that your SPA development server is running. If not, in the /spa/vue-minimal directory run:
npm run startCopy
Try your Component
Now that you implemented your component, open up the Page Editor again and view your page.
Your component is displayed.
Nice work!
You’ve just gone through the complete cycle of building a component (with handles and knobs on it) that a marketer can place in a visual SPA Editor.
Next, keep going with your new component. Bring in the tour listing logic you implemented in the Hello Magnolia - Headless section into your new component.
Displaying List of Tours
Our Component is up and running. Next we want to:
Upload some Demo Content
Define the REST API endpoint
Extend our Frontend Component to display the list of tours
Add Tours Content Type
In the folder spa-lm switch to contentTypes directory:
cd ./contentTypesCopy
Create a file named tours.yaml and copy following content into it:
datasource:workspace:toursautoCreate:truemodel:properties:name:label:Namerequired:truei18n:truedescription:label:Descriptioni18n:trueisFeatured:type:Boolean#Types 'Decimal', 'Long' and 'Double' are also available.label:Featurethisitemimage:type:assetlabel:ImagetourTypes:label:TourTypesmultiple:truelocation:label:StartCityi18n:truedate:label:Datetype:Dateduration:type:Longlabel:TourDurationoptions:"2":value:2label:2days"7":value:7label:7days"14":value:14label:14days"21":value:21label:21daystourOperator:label:TourOperatori18n:truebody:type:richTextlabel:Bodyi18n:trueCopy
Create a Magnolia app
Change to the spa-lm/apps directory.
cd ../appsCopy
Create a file named tours.yaml with following content:
!content-type:toursname:tourslabel:Tours# Optionally override any of the app configuration supplied by the content type.subApps:detail:label:DetailTourform:properties:isFeatured:buttonLabel:"Featured"tourTypes:$type:jcrMultiValueFieldfield:$type:linkFieldchooser:workbenchChooser:appName:categoriesCopy
To see your new Tours app, log out and log back in, and press the grid icon.
Install content
We have prepared Demo Content to start with. To install the Demo Content follow these steps:
Open the Tours app.
Click the Import action. You might need to scroll down in the action bar.
Select the ./magnolia/_dev/content-to-import/tours.magnolia-travels.xml file.
Add Tours REST API Endpoint
In the folder spa-lm switch to restEndpoints/delivery directory:
cd ./restEndpoints/deliveryCopy
Create a file named tours.yaml and copy following content into it:
Finally, we can extend our TourList Component to fetch and display the imported data.
To see the tour images displayed in the SPA app on localhost:3000, you must add the web access GET right with path /.rest/* to the anonymous user in the Security app in your Magnolia Author instance.
React
Angular
Vue
Add some more styling
In /spa/react-minimal/src/components create a file TourList.css in the same directory as TourList.js and copy in this stylesheet:
Open the tour-list.component.css file in your previously created component directory /spa/angular-minimal/src/app/components/tour-list and copy in the following stylesheet:
It might be tour-list.component.scss depending on the configuration of your Angular CLI.
You might have to change the styleUrls to tour-list.component.scss depending on the name of your actual file.
Edit the TourList component template tour-list.component.html and replace it with this content:
<divclass="tour-list"><h1>{{ headline }}</h1><divclass="tour-list-cards"><divclass="card" *ngFor="let tour of tours"><img
[src]="tour.image"class="card-img-top"alt="..."
/><divclass="card-body"><h5class="card-title">{{tour.name}}</h5><pclass="card-text">{{tour.description}}</p></div></div></div></div>Copy
Add some more styling
In /spa/vue-minimal/src/components open your TourList.vue file and add at the end of it the stylesheet: