ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

向全栈迈进——Angular+Tornado开发树洞博客(十一)

2021-11-21 17:06:43  阅读:127  来源: 互联网

标签:Tornado component 全栈 评论 showComment storyId 组件 import Angular


在上一篇博客中,我们开发了评论系统的前端部分,介绍了angular中模板的概念。在这篇博客中,我们将继续开发评论系统的前端部分,并介绍组件间通信的相关内容。
打开comments.component.ts文件,输入以下内容:

//comments.component.ts
import { Component, Input, OnInit } from '@angular/core';
import { CommentService } from 'src/app/service/comment.service';

@Component({
  selector: 'app-comments',
  templateUrl: './comments.component.html',
  styleUrls: ['./comments.component.scss']
})
export class CommentsComponent implements OnInit {
  showComment:boolean[]
  @Input() commentTree:any;
  @Input() storyId:number;

  constructor() {
    this.showComment = [false];
    this.storyId = 0;
   }

  ngOnInit(): void {
  }

  showCommentForm(storyId:number){
    this.showComment[storyId] = !this.showComment[storyId];
  }
}

这个组件中有两个属性:commentTree和storyId,前者用于存放评论树内容,而后者表示当前评论树属于哪个故事。
由于我们这个控件要作为storyList的子控件使用,即需要由storyList指定comments组件要放在哪个故事下面,因此我们使用@Input()修饰器来修饰commentTree和storyId参数,表明storyList组件可以指定comments组件中这两个属性的值:
使用@Input修饰器可以让父组件传递数据给子组件
底下的showCommentForm函数用来反转showComment数组中对应元素的值,控制评论树中评论表单的隐现。
这样,我们这个comments的前端部分就开发完了,下面让我们实现另一个组件commentForm,
这个组件用于让用户发表评论。
打开commentform/commentform.component.html,输入以下代码:

<!--commentform.component.html-->
<form nz-form  [formGroup]="commentForm" (ngSubmit)="publishComment()">
    <nz-form-item>
      <nz-form-control>
          <input formControlName="content" nz-input placeholder="说点什么" />
      </nz-form-control>
    </nz-form-item>
    <nz-form-item>
      <nz-form-control>
        <button nz-button nzType="primary">发表</button>
      </nz-form-control>
    </nz-form-item>
</form>

这个组件没什么说的,就是一个简单的表单。
下面打开commentform.component.ts文件,输入以下代码:

//commentform.component.ts
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { NzMessageService } from 'ng-zorro-antd/message';
import { CookieService } from 'ngx-cookie-service';
import { CommentService } from 'src/app/service/comment.service';
import { commentData } from '../storylist/commentData';
import { refreshCommentInfo } from './refreshcommentinfo';

@Component({
  selector: 'app-commentform',
  templateUrl: './commentform.component.html',
  styleUrls: ['./commentform.component.scss']
})
export class CommentformComponent implements OnInit {
  newComment:commentData;
  author:string;
  @Input() commentBody:string;
  @Input() storyId:number;
  @Output() refreshCommentEvent: EventEmitter<refreshCommentInfo>;


  commentForm = new FormGroup({
    content:new FormControl('')
  });

  constructor(private cookie:CookieService,private commentService:CommentService,private message:NzMessageService,private router:Router) {
    this.newComment = {
      author:'',
      content:'',
      commentBody:''
    }
    this.author = this.cookie.get('currentuser');
    this.commentBody = ''
    this.storyId = 0
    this.refreshCommentEvent = new EventEmitter();
   }

  ngOnInit(): void {
    console.log('comment form ' + this.commentBody)
    console.log('storyid:' + this.storyId)
  }

  publishComment(){
    this.newComment = this.commentForm.value;
    this.newComment.author = this.author;
    this.newComment.commentBody = this.commentBody;
    this.commentService.writeComment(this.newComment).subscribe((data:any)=>{
      if (data){
        if (data['result'] == 'Success'){
          console.log('Success comment');
          console.log('storyid:' + this.storyId)
          console.log('external storyId:' + this.newComment.commentBody)
          this.message.create('success','评论成功')
          this.commentForm.setValue({'content':''});
          setTimeout(() => {
            this.refreshCommentEvent.emit({'storyId':this.storyId,'externalStoryId':this.newComment.commentBody});
          }, 800);
        }
      }
    })
  }

}

我们来看一下这个组件中用到了哪些变量:

  • newComment:类型为之前定义的commentData,用于接收用户输入的新评论;
  • author:评论作者;
  • commentBody:表示这条评论的主体是谁,可以是故事也可以是别的评论;
  • storyId:评论所属的故事主体;
  • refreshCommentEvent:当评论完成时触发的事件,这里暂时不讲;
  • commentForm:FormGroup,用于处理表单。
    因为我们的这个表单控件也是作为storyList和comments组件的子组件使用的,我们同样使用@Input()来修饰commentBody和storyId变量;然而,对于refreshCommentEvent我们使用了@Output()修饰器来装饰它,表示它是一个向父组件传递信息的变量:
    @Output()与@Input()相反,是让子组件传递数据给父组件的
    这里用@Output()的目的是,当用户在评论树中发表了评论后,通知storyList重新抓取当前故事中的评论,实现立即显示新评论的功能。
    这个commentForm只有一个函数:publishComment,用于让用户发布新评论,并在成功发布评论后向父组件发送消息,让父组件重新获取当前故事的评论树。
    接下来,让我们修改storylist.component.html,调用我们刚刚写好的commentform和comments组件:
<!--storylist.component.html-->
<div *ngFor="let story of storyList" >
    <nz-card nzTitle="{{story.title}}" >
	<!--...-->
    <span *nzSpaceItem ><i nz-icon nzType="comment" nzTheme="outline"></i><a (click)="showAllComment(story.id,story.externalId)"> 评论</a> </span>
	<!--...-->
    </nz-space>
    </nz-card>
    <div *ngIf="showComment[story.id]">
    <nz-card nzTitle="评论" >
        <app-commentform [commentBody]="story.externalId" [storyId]="story.id" (refreshCommentEvent)="refreshComment($event)"></app-commentform>
        <app-comments [commentTree] = "commentTree" [storyId]="story.id"></app-comments>
    </nz-card>
    </div>
    <br>
</div>

我们为之前的评论标签添加一个a标签,让它调用我们稍后要实现的showAllComment函数来控制评论栏的隐现。这里我们用一个存有storyId的布尔型数组showComment来控制评论栏的隐现,如果对应storyId的值是true,则显示该故事的评论栏以及底下的评论树。
在commentform控件中,我们用**[commentBody][storyId]的语法向commentForm中的同名属性传值,表明评论框的评论是给这个故事的。类似的,我们在comments控件中也会传入[commentTree][storyId]**的属性,以便显示故事的评论树。
我们打开storylist.component.ts文件,向其中添加以下代码:

//storylist.component.ts
import { Component, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { StoryService } from 'src/app/service/story.service';
import { storyDetail } from './storeDetail';
import { FormControl,FormGroup } from '@angular/forms';
import { CommentService } from 'src/app/service/comment.service';
import { refreshCommentInfo } from '../commentform/refreshcommentinfo';

@Component({
  selector: 'app-storylist',
  templateUrl: './storylist.component.html',
  styleUrls: ['./storylist.component.scss']
})
export class StorylistComponent implements OnInit {
  storyList:storyDetail[];
  showComment:boolean[];
  public commentTree:any;
  commentForm = new FormGroup({
    content:new FormControl('')
  });

  constructor(private storyService:StoryService,private commentService:CommentService) {
  	//...
    this.showComment = [false];
   }
  ngOnInit(): void {

  }
  //...
  showAllComment(storyId:number,externalStoryId:string){
    this.showComment[storyId] = !this.showComment[storyId];
    this.getAllComment(storyId,externalStoryId);
  }
  getAllComment(storyId:number,externalStoryId:string){
    const commentowner = {'storyId':externalStoryId}
    this.commentService.getComment(commentowner).subscribe((data:any) =>{
      console.log(data['result']);
      //this.data = data;
      this.commentTree[storyId] = data['result'];
    })
  }
  refreshComment(info:refreshCommentInfo){
    this.showComment[info['storyId']] = true;
    this.getAllComment(info['storyId'],info['externalStoryId']);
  }
}

这里我们定义了两个新变量:showComment和commentTree,前者用于控制评论栏的隐现,而后者用于显示故事的评论树。在构造函数中,我们将showComment设为false,即在默认情况下不显示评论。
然后,我们实现一个showAllComment函数,这个函数的功能之一是反转showComment数组的指定元素的值,从而实现点击评论按钮来隐藏显示评论栏;功能之二是稍后要实现的获取该故事的评论树getAllComment函数;而refreshComment函数用于用户发表评论后重新获取当前故事的评论。
下面再让我们实现评论的service部分,打开cmd窗口,输入以下命令:

ng g s service/comment

然后打开comment.service.ts文件,输入以下代码:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { commentOwner } from '../story/comments/commentowner';
import { commentData } from '../story/storylist/commentData';

@Injectable({
  providedIn: 'root'
})
export class CommentService {

  constructor(private http:HttpClient) { }

  writeComment(comment:commentData):Observable<commentData>{
    return this.http.post<commentData>('http://localhost:8000/writecomment',comment)
  }

  getComment(storyId:commentOwner):Observable<commentOwner>{
    return this.http.post<commentOwner>('http://localhost:8000/getcomment',storyId)
  }

}

这里我们实现两个service:writeComment和getComment。顾名思义,前者用于发表评论,后者用于获取评论。
这样,我们就完成了评论系统所有的前端开发部分。我们来总结一下在这两篇博客中用到的知识点:

  • <ng-template>:angular模板,可以通过#语法来为模板指定模板名,以便之后的复用。
  • ngTemplateOutlet:用在<ng-template>标签内部,表示当前的模板要复用哪个模板,值为上一条中通过#定义的模板名
  • ngTemplateOutletContext:决定复用的模板中的数据,其值为一个key-value对象。
  • ngFor let-xxx [ngForOf]:模板中使用的*ngFor语法形式。
  • @Input()修饰器:表明这个属性可以被父组件访问、赋值,在html代码中通过[属性名]为该属性赋值。
  • @Output()修饰器:表明这个属性可以传递数据给父组件,与@Input()作用相反。
    以上就是我们开发评论系统的前端部分目前用到的所有知识点,可以说模板的相关概念理解起来还是有点困难的,思想可能会有一点绕。
    在下一篇博客中,我们将继续实现评论系统的后端部分,希望大家继续关注~

标签:Tornado,component,全栈,评论,showComment,storyId,组件,import,Angular
来源: https://blog.csdn.net/BetrayArmy/article/details/121454091

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有