Web Studio is here! An enhanced experience to make it easier to create, preview, and collaborate on your website contentLearn More
We believe that Editors (you know, the folks actually creating content) should have full control over their website pages and not rely on a developer.
The Starter Template makes it easy to source content, but it also assists with generating your Pages for you based on your sitemap in Agility CMS.
This means that editors in the CMS control what pages are available, what their URLs are, and exactly what UI components (we call these Page Modules) make up each page.
The following example is based on the Blog Starter instance. Don't have one? Sign up for one - https://agilitycms.com/free
The above figure represents a sitemap of Pages as well as the UI Components (Page Modules) that are on each page - all are managed in the CMS.
This means that when a build occurs in your Angular site, the following pages will be auto-generated for you:
/
/blog
/blog-posts/* --your dynamic page route for all of your blog posts (i.e. /blog/my-first-post)
/about
It can generate and serve those pages in response to requests from browsers. It can also pre-generate Angular Templates as HTML files that you serve later. The Blog Starter Template uses the agility-page.component.ts
file to source the Static Pages and Dynamic Page Items from your Sitemap in Agility, and the agility-page.component.html
is what renders your Page Modules.
agility-page.components.ts:
import { Component, OnInit } from '@angular/core';
import { Location } from '@angular/common';
import { Routes, RouterModule, Router, ActivatedRoute } from '@angular/router';
import { AgilityService } from '../../agility/agility.service';
import { Title } from '@angular/platform-browser';
import { isDevMode } from '@angular/core';
@Component({
selector: 'app-agility-page',
templateUrl: './agility-page.component.html',
styleUrls: ['./agility-page.component.css'],
})
export class AgilityPageComponent implements OnInit {
public pageInSitemap: any = null;
public page: any = null;
public pageStatus: number = 0;
public dynamicPageItem: any = null;
public isPreview: boolean;
constructor(
private location: Location,
private router: Router,
private route: ActivatedRoute,
private titleService: Title,
private agilityService: AgilityService
) {
this.pageStatus = 0;
this.isPreview = isDevMode();
}
async ngOnInit(): Promise {
try {
const sitemapFlat = await this.agilityService.getSitemapFlat();
let currentPath = location.pathname;
if (currentPath.indexOf('?') !== -1)
currentPath = currentPath.substring(0, currentPath.indexOf('?'));
if (currentPath === '/') [currentPath] = Object.keys(sitemapFlat);
this.pageInSitemap = sitemapFlat[currentPath];
if (!this.pageInSitemap) {
this.pageStatus = 404;
console.error(`404 - Page ${currentPath} not found in sitemap.`);
return;
}
//get the page object
this.page = await this.agilityService.getPage(this.pageInSitemap.pageID);
if (!this.page) {
console.error(
`500 - Page ${currentPath} with id ${this.pageInSitemap.pageID} could not be loaded.`
);
this.pageStatus = 500;
return;
}
//get the dynamic page item
if (this.pageInSitemap.contentID > 0) {
this.dynamicPageItem = await this.agilityService.getContentItem(
this.pageInSitemap.contentID
);
}
//set the document title...
this.titleService.setTitle(this.pageInSitemap.title);
this.pageStatus = 200;
} catch (error) {
console.error('An error occurred: ', error);
this.pageStatus = 500;
}
}
}
agility-page.component.html:
Page Modules in Agility CMS are the functional components that make up a page. Editors use these to compose what type of content is on each page and in what order they appear.
Developers define what page modules are available in the CMS and what fields they have. Each module defined in Agility CMS should correspond to a .component.ts
and .component.html
file in your Angular site.
Generally speaking, if you don't have any modules defined or editors don't add modules to pages, then there won't be anything to output for your pages.
In the Blog Starter Template, the name of the Page Module is used to find a corresponding .component.ts
file that matches the same name. If a match is found, that component is dynamically imported and rendered.
For example, if a module has a reference name of RichTextArea in the CMS, then while the page is being rendered, it will look for module-richtextarea.component.ts
in the module-richtextarea
directory.
Internally, the agility-module.component.ts
file will dynamically import the Page Module and pass all the field values for that module as props
.
module-richtextarea.component.ts:
import { Component, OnInit, Input } from '@angular/core';
import { IAgilityModuleComponent } from '../../agility/agility.module.icomponent';
@Component({
selector: 'app-module-richtextarea',
template: `
<div class="relative px-8">
<div class="max-w-2xl mx-auto my-12 md:mt-18 lg:mt-20">
<div
class="prose max-w-full mx-auto"
v-html="item.fields.textblob"
[innerHTML]="textblob"
></div>
</div>
</div>
`,
styles: [],
})
export class ModuleRichTextAreaComponent implements IAgilityModuleComponent {
@Input() data: any;
public textblob: string;
constructor() {}
ngOnInit(): void {
this.textblob = this.data.item.fields.textblob;
}
}
If there is no .component.ts
or .component.html
file for your Page Module, then nothing can be rendered for it in your Angular site.
You can alter your fields at any time and the field values passed to your component will update automatically.
When using Page Modules, the fields of the module are passed down to the .component.ts
file as props
. However, you may run into a scenario where you want to fetch additional data into your Page Modules, such as information from a Content List or Item in Agility.
An example can be seen here on the Post Listing Page Module in the Blog Starter Template.
Import AgilityService
into your Page Modules .component.ts
file
import { AgilityService } from '../../agility/agility.service';
Use the methods provided by AgilityService to fetch data from Content Lists, Content Items, Sitemaps, and Pages. In this example, we use the getContentList
method to fetch our Content List of Posts:
import { Component, Input, OnInit } from '@angular/core';
import { IAgilityModuleComponent } from 'src/agility/agility.module.icomponent';
import { AgilityService } from '../../agility/agility.service';
import { htmlDecode } from 'js-htmlencode';
@Component({
selector: 'app-module-posts-listing',
templateUrl: './module-posts-listing.component.html',
})
export class ModulePostsListingComponent implements IAgilityModuleComponent {
@Input() data: any;
public posts: any[] = null;
public moduleData: any = null;
constructor(private agilityService: AgilityService) {}
async ngOnInit(): Promise<void> {
const postsRes = await this.agilityService.getContentList('posts');
this.moduleData = this.data.item.fields;
this.posts = postsRes.items.map((p) => {
return {
title: p.fields.title,
slug: p.fields.slug,
date: new Date(p.fields.date).toLocaleDateString(),
image: p.fields.image,
content: p.fields.content,
category: p.fields.category.fields.title || 'Uncategorized',
};
});
}
}
To use the Data in your Page Module:
<ng-template [ngIf]="posts !== null && moduleData !== null">
<div class="relative px-8 mb-12">
<div class="max-w-screen-xl mx-auto">
<div class="sm:grid sm:gap-8 sm:grid-cols-2 lg:grid-cols-3">
<div *ngFor="let post of posts">
<a href="blog/{{ post.slug }}">
<div class="flex-col group mb-8 md:mb-0">
<div class="relative h-64">
<img
src="{{ post.image.url }}"
alt="{{ post.image.label }}"
class="object-cover object-center rounded-t-lg"
style="width: 100%; height: 100%"
/>
</div>
<div class="bg-gray-100 p-8 border-2 border-t-0 rounded-b-lg">
<div
class="
uppercase
text-primary-500 text-xs
font-bold
tracking-widest
leading-loose
"
>
{{ post.category }}
</div>
<div class="border-b-2 border-primary-500 w-8"></div>
<div
class="
mt-4
uppercase
text-gray-600
italic
font-semibold
text-xs
"
>
{{ post.date }}
</div>
<h2
class="
text-secondary-500
mt-1
font-black
text-2xl
group-hover:text-primary-500
transition
duration-300
"
>
{{ post.title }}
</h2>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
</ng-template>