- 在ArticlesService中添加
destroy、favorite和unfavorite方法 - 创建ArticleComponent
- 创建MarkdownPipe
- 创建ArticleResolver
- 创建ArticleModule,并将其导入AppModule中
- 创建ArticleMeta组件
- 导出到我们的应用程序
- 创建FavoriteButton
- 在SharedModule中声明并导出FavoriteButtonComponent和ArticleMetaComponent
- 将FavoriteButtonComponent和ArticleMetaComponent添加到ArticleComponent中
- 更新EditorComponent以导航到文章页面
现在我们可以创建新的文章了,让我们创建一个文章页面,这样我们就可以把它们实际展示给大家看。文章页面需要有删除和收藏/取消收藏文章的功能。让我们修改文章服务来实现这个功能。
在ArticlesService中添加destroy、favorite和unfavorite方法
src/app/shared/services/articles.service.ts
+ destroy(slug) {+ return this.apiService.delete('/articles/' + slug);+ }+ favorite(slug): Observable<Article> {+ return this.apiService.post('/articles/' + slug + '/favorite');+ }+ unfavorite(slug): Observable<Article> {+ return this.apiService.delete('/articles/' + slug + '/favorite');+ }
很好!现在让我们创建文章页面。现在让我们创建文章页面。
创建ArticleComponent
src/app/article/article.component.html
<div class="article-page"><div class="banner"><div class="container"><h1>{{ article.title }}</h1></div></div><div class="container page"><div class="row article-content"><div class="col-md-12"><div [innerHTML]="article.body | markdown"></div><ul class="tag-list"><li *ngFor="let tag of article.tagList"class="tag-default tag-pill tag-outline">{{ tag }}</li></ul></div></div><hr /><div class="article-actions"></div><div class="row"><div class="col-xs-12 col-md-8 offset-md-2"><div *showAuthed="false"><a [routerLink]="['/login']">Sign in</a> or <a [routerLink]="['/register']">sign up</a> to add comments on this article.</div></div></div></div></div>
src/app/article/article.component.ts
import { Component, OnInit } from '@angular/core';import { FormControl } from '@angular/forms';import { ActivatedRoute, Router } from '@angular/router';import {Article,ArticlesService,User,UserService} from '../shared';@Component({selector: 'article-page',templateUrl: './article.component.html'})export class ArticleComponent implements OnInit {article: Article;currentUser: User;canModify: boolean;isSubmitting = false;isDeleting = false;constructor(private route: ActivatedRoute,private articlesService: ArticlesService,private router: Router,private userService: UserService,) { }ngOnInit() {// Retreive the prefetched articlethis.route.data.subscribe((data: { article: Article }) => {this.article = data.article;});// Load the current user's datathis.userService.currentUser.subscribe((userData: User) => {this.currentUser = userData;this.canModify = (this.currentUser.username === this.article.author.username);});}onToggleFavorite(favorited: boolean) {this.article.favorited = favorited;if (favorited) {this.article.favoritesCount++;} else {this.article.favoritesCount--;}}onToggleFollowing(following: boolean) {this.article.author.following = following;}deleteArticle() {this.isDeleting = true;this.articlesService.destroy(this.article.slug).subscribe(success => {this.router.navigateByUrl('/');});}}
我们需要创建一个用于渲染标记的管道。
如果你使用Angular的CLI创建了你的应用程序,你需要通过运行npm install marked --save并重新启动Webpack来安装marked的包。
创建MarkdownPipe
src/app/article/markdown.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';import * as marked from 'marked';@Pipe({name: 'markdown'})export class MarkdownPipe implements PipeTransform {transform(content: string): string {return marked(content, { sanitize: true });}}
我们还需要在组件初始化之前自动解析文章的数据。
创建ArticleResolver
src/app/article/article-resolver.service.ts
import { Injectable, } from '@angular/core';import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';import { Observable } from 'rxjs/Rx';import { Article, ArticlesService, UserService } from '../shared';@Injectable()export class ArticleResolver implements Resolve<Article> {constructor(private articlesService: ArticlesService,private router: Router,private userService: UserService) {}resolve(route: ActivatedRouteSnapshot,state: RouterStateSnapshot): Observable<any> {return this.articlesService.get(route.params['slug']).catch((err) => this.router.navigateByUrl('/'));}}
最后,让我们创建文章模块,并将其导入到App模块中。
创建ArticleModule,并将其导入AppModule中
src/app/article/article.module.ts
import { ModuleWithProviders, NgModule } from '@angular/core';import { RouterModule } from '@angular/router';import { ArticleComponent } from './article.component';import { ArticleResolver } from './article-resolver.service';import { MarkdownPipe } from './markdown.pipe';import { SharedModule } from '../shared';const articleRouting: ModuleWithProviders = RouterModule.forChild([{path: 'article/:slug',component: ArticleComponent,resolve: {article: ArticleResolver}}]);@NgModule({imports: [articleRouting,SharedModule],declarations: [ArticleComponent,MarkdownPipe],providers: [ArticleResolver]})export class ArticleModule {}
src/app/app.module.ts
[...]import { RouterModule } from '@angular/router';import { AppComponent } from './app.component';+import { ArticleModule } from './article/article.module';import { AuthModule } from './auth/auth.module';import { EditorModule } from './editor/editor.module';import { HomeModule } from './home/home.module';[...]],imports: [BrowserModule,+ ArticleModule,AuthModule,EditorModule,HomeModule,[...]
我们现在可以查看文章了!但是,我们目前还不能看到文章的作者是谁、何时发布的、收藏/不收藏按钮、关注按钮、编辑/删除按钮等。然而,目前我们还不能看到文章的作者是谁,何时发布的,喜欢/不喜欢按钮,关注按钮,编辑/删除按钮等。
让我们创建一个组件,它可以在顶部和底部显示元数据,也可以显示其他元素,如关注/收藏按钮、删除/编辑、日期等。
创建ArticleMeta组件
src/app/shared/article-helpers/article-meta.component.html
<div class="article-meta"><a [routerLink]="['/profile', article.author.username]"><img [src]="article.author.image" /></a><div class="info"><a class="author"[routerLink]="['/profile', article.author.username]">{{ article.author.username }}</a><span class="date">{{ article.createdAt | date: 'longDate' }}</span></div><ng-content></ng-content></div>
src/app/shared/article-helpers/article-meta.component.ts
import { Component, Input } from '@angular/core';import { Article } from '../models';@Component({selector: 'article-meta',templateUrl: './article-meta.component.html'})export class ArticleMetaComponent {@Input() article: Article;}
导出到我们的应用程序
src/app/shared/article-helpers/index.ts
export * from './article-meta.component';
src/app/shared/index.ts
+export * from './article-helpers';export * from './buttons';export * from './layout';export * from './list-errors.component';[...]
现在让我们创建收藏夹按钮,它将与我们创建关注按钮的方法超级相似。
创建FavoriteButton
src/app/shared/buttons/favorite-button.component.html
<button class="btn btn-sm"[ngClass]="{ 'disabled' : isSubmitting,'btn-outline-primary': !article.favorited,'btn-primary': article.favorited }"(click)="toggleFavorite()"><i class="ion-heart"></i> <ng-content></ng-content></button>
src/app/shared/buttons/favorite-button.component.ts
import { Component, EventEmitter, Input, Output } from '@angular/core';import { Router } from '@angular/router';import { Article } from '../models';import { ArticlesService, UserService } from '../services';@Component({selector: 'favorite-button',templateUrl: './favorite-button.component.html'})export class FavoriteButtonComponent {constructor(private articlesService: ArticlesService,private router: Router,private userService: UserService) {}@Input() article: Article;@Output() onToggle = new EventEmitter<boolean>();isSubmitting = false;toggleFavorite() {this.isSubmitting = true;this.userService.isAuthenticated.subscribe((authenticated) => {// Not authenticated? Push to login screenif (!authenticated) {this.router.navigateByUrl('/login');return;}// Favorite the article if it isn't favorited yetif (!this.article.favorited) {this.articlesService.favorite(this.article.slug).subscribe(data => {this.isSubmitting = false;this.onToggle.emit(true);},err => this.isSubmitting = false);// Otherwise, unfavorite the article} else {this.articlesService.unfavorite(this.article.slug).subscribe(data => {this.isSubmitting = false;this.onToggle.emit(false);},err => this.isSubmitting = false);}})}}
src/app/shared/buttons/index.ts
+export * from './favorite-button.component';export * from './follow-button.component';
在SharedModule中声明并导出FavoriteButtonComponent和ArticleMetaComponent
src/app/shared/shared.module.ts
[...]import { HttpModule } from '@angular/http';import { RouterModule } from '@angular/router';+import { ArticleMetaComponent } from './article-helpers';+import { FavoriteButtonComponent, FollowButtonComponent } from './buttons';import { ListErrorsComponent } from './list-errors.component';import { ShowAuthedDirective } from './show-authed.directive';[...]RouterModule],declarations: [+ ArticleMetaComponent,+ FavoriteButtonComponent,FollowButtonComponent,ListErrorsComponent,ShowAuthedDirective],exports: [+ ArticleMetaComponent,CommonModule,+ FavoriteButtonComponent,FollowButtonComponent,FormsModule,ReactiveFormsModule,[...]
现在让我们把它们添加到页面上
将FavoriteButtonComponent和ArticleMetaComponent添加到ArticleComponent中
src/app/article/article.component.html
<div class="article-page"><div class="banner"><div class="container"><h1>{{ article.title }}</h1>++ <article-meta [article]="article">++ <span [hidden]="!canModify">+ <a class="btn btn-sm btn-outline-secondary"+ [routerLink]="['/editor', article.slug]">+ <i class="ion-edit"></i> Edit Article+ </a>++ <button class="btn btn-sm btn-outline-danger"+ [ngClass]="{disabled: isDeleting}"+ (click)="deleteArticle()">+ <i class="ion-trash-a"></i> Delete Article+ </button>+ </span>++ <span [hidden]="canModify">+ <follow-button+ [profile]="article.author"+ (onToggle)="onToggleFollowing($event)">+ </follow-button>++ <favorite-button+ [article]="article"+ (onToggle)="onToggleFavorite($event)">+ {{ article.favorited ? 'Unfavorite' : 'Favorite' }} Article <span class="counter">({{ article.favoritesCount }})</span>+ </favorite-button>+ </span>++ </article-meta></div></div><div class="container page"><div class="row article-content"><div class="col-md-12"><div [innerHTML]="article.body | markdown"></div><ul class="tag-list"><li *ngFor="let tag of article.tagList"class="tag-default tag-pill tag-outline">{{ tag }}</li></ul></div></div><hr /><div class="article-actions">+ <article-meta [article]="article">++ <span [hidden]="!canModify">+ <a class="btn btn-sm btn-outline-secondary"+ [routerLink]="['/editor', article.slug]">+ <i class="ion-edit"></i> Edit Article+ </a>++ <button class="btn btn-sm btn-outline-danger"+ [ngClass]="{disabled: isDeleting}"+ (click)="deleteArticle()">+ <i class="ion-trash-a"></i> Delete Article+ </button>+ </span>++ <span [hidden]="canModify">+ <follow-button+ [profile]="article.author"+ (onToggle)="onToggleFollowing($event)">+ </follow-button>++ <favorite-button+ [article]="article"+ (onToggle)="onToggleFavorite($event)">+ {{ article.favorited ? 'Unfavorite' : 'Favorite' }} Article <span class="counter">({{ article.favoritesCount }})</span>+ </favorite-button>+ </span>++ </article-meta></div><div class="row"><div class="col-xs-12 col-md-8 offset-md-2"><div *showAuthed="false"><a [routerLink]="['/login']">Sign in</a> or <a [routerLink]="['/register']">sign up</a> to add comments on this article.</div></div></div></div></div>
最后,让我们更新编辑器,成功后导航到文章页面。
更新EditorComponent以导航到文章页面
src/app/editor/editor.component.ts
[...]this.articlesService.save(this.article).subscribe(+ article => this.router.navigateByUrl('/article/' + article.slug),err => {this.errors = err;this.isSubmitting = false;[...]
你可以将你的代码与Github上的有效代码进行比较,或者在本地检查分支:
git checkout m-1
