* This article will assume some basic knowledge of Angular so it can focus on the specific task of requesting and showing data from Cosmic JS
TL; DR
Take a look at the repository and install the app
What is this app about?
A typical company website is just a set of pages structured in a more or less simple navigation. We want to create a website that allows the user to create any number of pages and decide the structure for the navigation, without the need to wait for a code release. For that, we are going to rely on Cosmic JS and create the structure to hold this project. On this example, our bucket will have the following object types:
- Pages. This is a simple object, with no additional metafields.
- Navigation. It will hold a collection of Pages.
- Presets. This is a configuration object with a list of attributes that will help us start our app. It will hold the value for the homepage and the main navigation that we will use for the menu of our site.
Using the API
Now that we defined the structure of our data, we need to create our Angular app (I recommend using the Angular CLI) and create a service to make our calls to the Cosmic JS API. See how simple is to request a single page object from Cosmic JS:
getPage(slug: string): Observable<Page> {
if (!this.page$.get(slug)) {
const url = `api.cosmicjs.com/v1/company-presentation/object/${slug}`;
const response = this.http.get<Page>(url).pipe(
tap(_ => console.log(`fetched page: ${slug}`)),
map(_ => {
return new Page(_['object']);
}),
shareReplay(1),
catchError(this.handleError<Page>(`getPage: ${slug}`))
);
this.page$.set(slug, response);
}
return this.page$.get(slug);
}
get a single page and save it to the stream to keep it cached for the session
Note that, for nested objects, Cosmic JS will not return the full tree, so sometimes you will need to concatenate calls to make the most of our service, e.g.: When fetching the main presets, you would probably want to include the navigation pages.
getMainPresets(includeNavigation?: boolean): Observable<Preset> {
if (includeNavigation) {
return this.getPreset(environment.presets).pipe(
switchMap(response => {
return forkJoin([of(response), this.getNavigation(response.mainNavigation._id)]);
}),
map(res => {
res[0].mainNavigation = res[1];
return res[0];
})
);
} else {
return this.getPreset(environment.presets);
}
}
this call will combine the result of getNavigation into the result of getPreset
Our api calls need to be authenticated with a read key, we can include that on each method, but the easiest may be to create an interceptor. This will intercept all http requests, so we check if it's a Cosmic JS request and append the read key where necessary.
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.url.match(/api.cosmicjs/)) {
let params = new HttpParams({ fromString: req.params.toString() });
if (req.method === 'GET') {
params = params.append('read_key', environment.read_key);
} else {
params = params.append('write_key', environment.write_key);
}
req = req.clone({
params: params
});
}
return next.handle(req);
}
intercept and append the read or write parameters
Routing to the pages
At this point, our website does nothing, the first thing it needs to do is to know how to load its first page. We need to create a routed module for the Pages, and set it to load on the root. Once inside the module, on its own routing, we will set a route for the parameter :slug to load the Page Component. And what happens when there is no route specified? This will be our most common scenario, for that, we set an empty route with a Guard.
const routes: Routes = [
{
path: '',
canActivate: [HomepageGuard]
},
{
path: ':slug',
component: PageComponent
}
];
A guard will be executing when matching a route and before loading the component, so we will use it to fetch the main presets from Cosmic JS, and redirect the app to the homepage we stablished.
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return this.cosmicService.getMainPresets().pipe(
map(presets => {
return this.router.parseUrl(`/${presets.homepage.slug}`);
})
);
}
from Angular 7.1, canActivate can return either a boolean or an UrlTree
Components
Now we have a route configuration that will load the Page component, and this is the way to load a page using the slug from the parameter list:
ngOnInit() {
this.route.paramMap
.pipe(
map(paramMap => paramMap.get('slug')),
switchMap(slug => (slug ? this.cosmicService.getPage(slug) : EMPTY))
)
.subscribe(page => (this.page = page));
}
now we can use the page object on our template
With this, we have a way to navigate via url, but we should also load a menu so the user can see the pages available. For that, we will add a menu component on the core module, as it will be shared throughout the app. Our menu will be composed of a navigation list, a logo and the company name; all of this is defined on the presets, so the menu will have a call like this:
ngOnInit() {
this.cosmicService.getMainPresets(true).subscribe(presets => {
this.logo = presets.companyLogoUrl;
this.navigation = presets.mainNavigation;
this.title = presets.companyName;
});
}
note that we specifically ask to include the navigation by adding "true" as a parameter to the method
All of this would allow you to quickly create a website using Angular and Cosmic JS, all that is left to do is to create the templates and extend the configuration to meet your needs.
Top comments (0)