Angular已迅速普及并得到广泛使用。 Angular由Google工程师开发和维护,已经在动态Web应用程序中找到了自己的位置,并且是一个越来越受欢迎的平台。

Angular具有庞大而热情的社区和出色的MVC的优点,它们不需要开发人员花费宝贵的时间编写代码来将多个MVC组件重新组合在一起。 简而言之,Angular是用于前端开发的健壮且全面的Web应用程序框架,它已经进行了单元测试,使其成为许多开发人员的首选工具。

如果您使用的是Angular,则可能会遇到对内容管理功能的需求-博客就是一个例子。 在Angular应用中添加CMS似乎令人望而生畏,特别是如果您试图将其集成到WordPress之类的传统CMS中时,但是基于API的CMS新品种极大地简化了事情。 ButterCMS是基于SaaS的无头CMS的一个示例,它提供了托管的CMS仪表板和内容API,您可以从Angular应用程序中查询它们。 这意味着您无需启动任何新的基础架构即可将CMS添加到Angular应用中。

本教程将演示如何构建由CMS驱动的Angular应用程序,该应用程序具有通过API提供支持的营销页面(客户案例研究),博客和常见问题解答。 无需服务器!

安装

首先,您将开始安装Angular CLI。

npm install -g @angular/cli</td>

使用Angular CLI设置一个新的Angular项目。 默认情况下,Angular CLI使用CSS样式,因此添加--style=scss标志可--style=scss Angular CLI改为使用SCSS:

ng new hello-buttercms-project --style=scss
cd hello-buttercms-project

安装Angular Material和Angular Material相关的软件包:

npm install --save @angular/material @angular/cdk
npm install --save @angular/animations

安装ButterCMS。 在命令行中运行以下命令:

npm install buttercms --save

也可以使用CDN加载黄油:

<script src="https://cdnjs.buttercms.com/buttercms-1.1.1.min.js"></script>

快速入门

在您选择的代码编辑器中打开项目。 在src/app创建一个名为_services的目录。

我们创建一个名为butterCMS.service.js的文件。 这使我们可以将您的API令牌放在一个位置,而不会意外更改它。

import * as Butter from 'buttercms';

export const butterService = Butter('b60a008584313ed21803780bc9208557b3b49fbb');

您可以将此文件导入到我们要使用ButterCMS的任何组件中。

对于快速入门,请转到src/app/hello-you/hello-you.component.ts并导入butterService

import {butterService} from '../_services';

HelloYouComponent内部创建方法:

fetchPosts() {
  butter.post.list({
    page: 1,
    page_size: 10
  })
  .then((res) => {
    console.log('Content from ButterCMS')
    console.log(res)
  })
}

现在,通过将组件添加到OnInit生命周期挂钩中来在加载组件时调用此方法:

ngOnInit() {
  this.fetchPosts();
}

该API请求会提取您的博客文章。 您的帐户附带一个示例帖子,您将在回复中看到该帖子。

接下来,创建另一种方法来检索“首页标题内容”字段:

fetchHeadline() {
  butter.content.retrieve(['homepage_headline'])
    .then((res) => {
      console.log('Headline from ButterCMS')
      console.log(res)
    })
}

将此方法添加到OnInit生命周期挂钩。

ngOnInit() {
  this.fetchPosts();
  this.fetchHeadline();
}

此API请求获取首页标题内容。 您可以设置自己的自定义内容字段来管理所需的任何种类的内容。

添加营销页面

设置CMS支持的页面是一个简单的三步过程:

  1. 定义页面类型
  2. 创建一个页面
  3. 集成到您的应用程序

定义页面

首先,创建一个页面类型来表示您的客户案例研究页面。 接下来,定义客户案例研究所需的字段。 定义页面类型后,您现在可以创建第一个案例研究页面。 指定页面的名称和URL,然后填充页面的内容。

定义页面后,ButterCMS API将以JSON格式返回它,如下所示:

{
    "data": {
        "slug": "acme-co",
        "fields": {
            "facebook_open_graph_title": "Acme Co loves ButterCMS",
            "seo_title": "Acme Co Customer Case Study",
            "headline": "Acme Co saved 200% on Anvil costs with ButterCMS",
            "testimonial": "<p>We've been able to make anvils faster than ever before! - <em>Chief Anvil Maker</em></p>\r\n<p><img src=\"https://cdn.buttercms.com/NiA3IIP3Ssurz5eNJ15a\" alt=\"\" caption=\"false\" width=\"249\" height=\"249\" /></p>",
            "customer_logo": "https://cdn.buttercms.com/c8oSTGcwQDC5I58km5WV",
        }
    }
}

本指南使用Angular框架和Angular CLI生成我们所有的组件并打包我们的应用程序。

让我们看一下代码。

创建一个新项目

ng new buttercms-project --style=scss
cd buttercms-project
npm install --save @angular/material @angular/cdk
npm install --save @angular/animations
npm install -S buttercms
ng serve

您的localhost:4200应该已经准备好服务您的Angular页面了。

创建TypeScript以导出ButterCMS服务

src/app ,创建一个名为_services的目录。 创建一个名为butterCMS.service.js的文件。

import * as Butter from 'buttercms';
export const butterService = Butter('your_api_token');

更新组件路由

这些组件由Angular CLI使用以下命令生成:

ng g component <my-new-component>

src/app ,创建一个名为app-routing.module.ts的文件:

import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {CustomerComponent} from './customer/listing/customer.listing.component';
import {FaqComponent} from './faq/faq.component';
import {BlogPostComponent} from './blog-post/listing/blog-post.component';
import {HomeComponent} from './home/home.component';
import {CustomerDetailsComponent} from './customer/details/customer.details.component';
import {BlogPostDetailsComponent} from './blog-post/details/blog-post.details.component';
import {FeedComponent} from './feed/feed.component';
import {HelloYouComponent} from './hello-you/hello-you.component';

const appRoutes: Routes = [
    {path: 'customer', component: CustomerComponent},
    {path: 'customer/:slug', component: CustomerDetailsComponent},
    {path: 'faq', component: FaqComponent},
    {path: 'blog', component: BlogPostComponent},
    {path: 'blog/:slug', component: BlogPostDetailsComponent},
    {path: 'rss', component: FeedComponent},
    {path: 'hello-you', component: HelloYouComponent},
    {path: 'home', component: HomeComponent},
    {path: '**', redirectTo: 'home'}
];

@NgModule({
    imports: [RouterModule.forRoot(appRoutes)],
    exports: [RouterModule]
})
export class AppRoutingModule {
}

设置客户列表页面

apps/customer类型下:

ng g component listing

在文件apps/customer/listing/customer.listing.component.ts

  1. 进口butterService
  2. OnInit挂钩中,使用butterService获取客户列表
  3. 将结果存储在pages变量中,标记(HTML)将使用数据进行更新。
import {Component, OnInit} from '@angular/core';
import {butterService} from '../../_services';

@Component({
    selector: 'app-customer',
    templateUrl: './customer.listing.component.html',
    styleUrls: ['./customer.listing.component.scss']
})

export class CustomerComponent implements OnInit {
  public pages: any[];
  constructor() { }

  ngOnInit() {
    butterService.page.list('customer_case_study')
      .then((res) => {
        this.pages = res.data.data;
      });
  }
}

customer.listing.component.html显示结果:

<mat-card>
  <mat-card-title class="page-title">Customers</mat-card-title>
  <mat-divider></mat-divider>
  <mat-card-content class="page-body">
      <mat-card *ngFor="let page of pages">
          <mat-card-title>
              <div class="container">
                  <a [routerLink]="[page.slug]">
                      <div fxLayout="row" fxLayout.xs="column"
                           fxFlex class="content">
                          <div class="blocks">
                              <img src="{{page.fields.customer_logo}}" alt="{{page.fields.seotitle}}" height="64"
                                   width="64"/>
                          </div>
                          <div class="blocks">
                              {{page.fields.headline}}
                          </div>
                      </div>
                  </a>
              </div>
          </mat-card-title>
      </mat-card>
  </mat-card-content>
  <mat-divider></mat-divider>
  <mat-card-footer>
      <div class="page-footer">
          <mat-icon>whatshot</mat-icon>
      </div>
  </mat-card-footer>
</mat-card>

设置客户详细信息页面

apps/customer ,输入ng g component details

apps/customer/details/customer.details.component.ts

创建客户页面

  1. 进口butterService
  2. OnInit钩子中,使用butterService来获取URL路径中给出了错误信息的客户页面
  3. 将结果存储在页面变量中,标记(HTML)将使用客户数据进行更新。
import {Component, OnInit} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {ActivatedRoute} from '@angular/router';
import {butterService} from '../../_services';
import {map, take} from 'rxjs/operators';

@Component({
  selector: 'app-customer-details',
  templateUrl: './customer.details.component.html',
  styleUrls: ['./customer.details.component.scss']
})

export class CustomerDetailsComponent implements OnInit {
  constructor(protected route: ActivatedRoute) { }

  protected slug$: Observable<string>;
  public page: any;

  ngOnInit() {
    this.slug$ = this.route.paramMap
      .pipe(
        map(params => (params.get('slug')))
      );

    this.slug$.pipe(
      take(1))
      .subscribe(slug => {
        butterService.page.retrieve('customer_case_study', slug)
          .then((res) => {
            this.page = res.data.data;
          }).catch((res) => {
          console.log(res);
        });
      });
  }
}

将结果显示在customer.details.component.html

<mat-card>
  <div class="container">
    <div fxLayout="column" class="details">
      <div class="blocks">
        <img src="{{page.fields.customer_logo}}" alt="" height="124" width="124"/>
      </div>

      <h1 class="blocks">
        {{page.fields.headline}}
      </h1>
      <h3 class="is-size-3">Testimonials</h3>
      <div [innerHTML]="page.fields.testimonial"></div>
      <div [innerHTML]="page.fields.body"></div>
    </div>
  </div>
</mat-card>

现在,您可以通过所有“客户页面”列表或直接通过URL导航到“客户页面”。

添加知识库

设置内容字段

假设您想将CMS添加到带有标题和带有答案的问题列表的静态FAQ页面。

使用Butter使您的内容动态化是一个两步过程:

  1. 在Butter中设置自定义内容字段
  2. 将字段集成到您的应用程序中。

要设置自定义内容字段,请首先登录Butter仪表板。

创建一个新的工作区或单击一个现有的工作区。 通过工作区,您可以以友好的方式为内容编辑者组织内容字段,并且对开发或API没有影响。 例如,一个房地产网站可能有一个名为Properties的工作区,另一个名为About Page

一旦进入工作区,请单击按钮以创建新的内容字段。 选择对象类型,然后将字段命名为FAQ Headline。

保存后,添加另一个字段,但是这次选择“ 收集”类型,并将字段命名为FAQ Items

在下一个屏幕上,为集合中的项目设置两个属性。

现在返回您的工作区并更新标题和常见问题解答。

整合您的应用

创建常见问题解答组件

apps ,键入ng g component faq

apps/faq/faq.component.ts

设置onInit挂钩以加载常见问题

import {Component, OnInit} from '@angular/core';
import {butterService} from '../_services';

@Component({
  selector: 'app-faq',
  templateUrl: './faq.component.html',
  styleUrls: ['./faq.component.scss']
})

export class FaqComponent implements OnInit {
  constructor() {}

  public faq: any = {
      items: [],
      title: 'FAQ'
  };

  ngOnInit() {
    butterService.content.retrieve(['faq_headline', 'faq_items'])
      .then((res) => {
        console.log(res.data.data);
        this.faq.title = res.data.data.faq_headline;
        this.faq.items = res.data.data.faq_items;
      });
  }
}

显示结果

<mat-card>
  <mat-card-title class="page-title"></mat-card-title>
  <mat-divider></mat-divider>
  <mat-card-content class="page-body">
    <mat-card *ngFor="let item of faq.items">
      <mat-card-content>
        <h3>
          {{item.question}}
        </h3>
        <div>
          {{item.answer}}
        </div>
      </mat-card-content>
    </mat-card>
  </mat-card-content>
  <mat-divider></mat-divider>
  <mat-card-footer>
    <div class="page-footer">
      <mat-icon>whatshot</mat-icon>
    </div>
  </mat-card-footer>
</mat-card>

在黄油仪表盘中输入的值将立即更新我们应用程序中的内容。

写博客

为了显示帖子,我们在您的应用中创建一个简单的/blog路由,并从Butter API中获取博客帖子,以及一个/blog/:slug路由来处理各个帖子。

请参阅我们的API参考以获取其他选项,例如按类别或作者过滤。 响应中还包含一些我们将用于分页的元数据。

设置博客主页

apps/blog-post ,输入ng g component listing

apps/blog-post/listing/blog-post.listing.component.ts

更新组件以获取所有帖子:

  1. 进口butterService
  2. 在Init上获取所有帖子
import {Component, OnInit} from '@angular/core';
import {butterService} from '../../_services';

@Component({
  selector: 'app-blog-post',
  templateUrl: './blog-post.component.html',
  styleUrls: ['./blog-post.component.scss']
})
export class BlogPostComponent implements OnInit {
  public posts: any[];

  constructor() { }

  ngOnInit() {
    butterService.post.list({
      page: 1,
      page_size: 10
    }).then((res) => {
      console.log(res.data)
      this.posts = res.data.data;
    });
  }
}

显示结果:

<mat-card>
  <mat-card-title class="page-title">Blog Posts</mat-card-title>
  <mat-divider></mat-divider>
  <mat-card-content class="page-body">
    <mat-card *ngFor="let post of posts">
      <mat-card-title>

        <a [routerLink]="[post.slug]">
          <div class="container">
            <div fxLayout="row" fxLayout.xs="column"
               fxFlex class="content">
              <div class="blocks">
                <img *ngIf="post.featured_image" src="{{post.featured_image}}" height="64" width="64"/>
              </div>
              <div class="blocks">
                {{post.title}}
              </div>
            </div>
          </div>
          <div class="container">
            <div fxLayout="column" class="summary">
              <div [innerHTML]="post.summary"></div>
            </div>
          </div>
        </a>
      </mat-card-title>
    </mat-card>
  </mat-card-content>
  <mat-divider></mat-divider>

  <mat-card-footer>
    <div class="page-footer">
      <mat-icon>whatshot</mat-icon>
    </div>
  </mat-card-footer>
</mat-card>

设置博客文章页面

apps/blog-post ,输入ng g component details

apps/blog-post/details/blog-post.details.component.ts

显示单个帖子:

  1. 进口butterService
  2. OnInit挂钩中,使用butterService获取URL路径中给出了段名的博客文章。
  3. 将结果存储在post变量中,标记(HTML)将使用客户数据进行更新。
import {Component, OnInit, ViewEncapsulation} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {ActivatedRoute} from '@angular/router';
import {butterService} from '../../_services';
import {map, take} from 'rxjs/operators';


@Component({
    selector: 'app-blog-post-details',
    templateUrl: './blog-post.details.component.html',
    styleUrls: ['./blog-post.details.component.scss'],
    encapsulation: ViewEncapsulation.None
})
export class BlogPostDetailsComponent implements OnInit {

    constructor(protected route: ActivatedRoute) {
    }

    protected slug$: Observable<string>;
    public post = {
        meta: null,
        data: null
    };

    ngOnInit() {
        this.slug$ = this.route.paramMap
            .pipe(
                map(params => (params.get('slug')))
            );

        this.slug$.pipe(
            take(1))
            .subscribe(slug => {
                butterService.post.retrieve(slug)
                    .then((res) => {
                        this.post = res.data;
                    }).catch((res) => {
                    console.log(res);
                });
            });
    }
}

显示结果:

<mat-card>
  <div class="container">
    <div fxLayout="column" class="blog-details">
      <div class="container">
        <div fxLayout="row">
          <h1 class="blocks">
            {{post.data.title}}
          </h1>
          <div *ngIf="post.meta.previous_post"><a [routerLink]="post.meta.previous_post"><</a></div>
          <div *ngIf="post.meta.next_post"><a [routerLink]="post.meta.next_post">></a></div>
        </div>
        <h4>
          {{post.data.author.first_name}} {{post.data.author.last_name}}
        </h4>
        <div class="post-body" [innerHTML]="post.data.body"></div>
      </div>
    </div>
  </div>
</mat-card>

现在,您的应用程序具有一个可以正常运行的博客,可以在ButterCMS仪表板中轻松对其进行更新。

类别,标签和作者

使用Butter的API来分类,标记和作者,以显示和过滤博客中的内容。

列出所有类别并按类别获取帖子

onInit()生命周期挂钩上调用以下方法:

methods: {
  ...
  getCategories() {
    butter.category.list()
      .then((res) => {
        console.log('List of Categories:')
        console.log(res.data.data)
      })
  },
  getPostsByCategory() {
    butter.category.retrieve('example-category', {
        include: 'recent_posts'
      })
      .then((res) => {
        console.log('Posts with specific category:')
        console.log(res)
      })
  }
},
created() {
  ...
  this.getCategories()
  this.getPostsByCategory()
}

包起来

恭喜! 您已成功使用内容API将静态Angular应用程序转换为基于CMS的应用程序,从而维护了无服务器架构。 您的开发团队可以利用Angular节省时间的方面,并且通过使用无服务器CMS节省了更多时间。

From: https://www.sitepoint.com/how-to-build-a-serverless-cms-powered-angular-application/

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐