Angular实现类似博客评论的递归显示及获取回复评论的数据


Posted in Javascript onNovember 06, 2017

前言

我们在一些技术博客中会经常看到很多递归评论,也即是我们可以回复博友的评论,且界面很美观,有梯度的显示格式,日前在空余时间写类似的 demo,所以记录下来,可以给需要的人一些借鉴的作用。
好了,废话少说,直奔主题。。。

思路

我们在写后台程序的时候,经常会遇到生成类似树的这种数据结构,我们直觉就是使用递归的方法来实现,起初我也是这么想的,就是写一个 Angular4 的递归方法,组成一个字符串,然后在界面显示,类似下面代码

@Component({
 selector: "comment",
 template: '{{ comments }}'
})
export class CommentComponent {

 public comments: string = "";

 generateComment(Comment comment) {
 this.comments = this.comments + "<div>" + comment.content + "</div>";
 if (comment.pComment != null) {
  generateComment(comment.pComment);
 }
 }
}

很天真的以为可以了,结果一试,标签不会被解析,才想起已经过了解析标签的过程了。。。

后来想着,现在的前端框架都是以组件化自称, Angular4 也不例外,那么一个 Component 可以嵌入任何 Component,那肯定可以嵌入自己,也就有了类似递归的概念了,想到这立马试试。。。

具体实现

思路是这样子,我定义了数据的格式,是每个评论下面有一个子评论数组,而不是每个评论有一个父评论,数据格式如下:

"comments": 
  [
  {
   "id": 1,
   "username": "James1",
   "time": "2017-07-09 21:02:21",
   "content": "哈哈哈1<h1>哈哈哈</h1>",
   "status": 1,
   "email": "1xxxx@xx.com",
   "cComments": [
    {
    "id": 2,
    "username": "James2",
    "time": "2017-07-09 21:02:22",
    "content": "哈哈哈2",
    "status": 1,
    "email": "2xxxx@xx.com",
    "cComments": null
   }
   ]
  }
  ]

CommentComponent 组件实现了评论模块,但是递归评论并不在这个组件实现,而是在子组件 CommentViewComponent 实现,因为 CommentComponent 还包括一个一个输入评论的文本框。

评论总模块 ComponentComponent 代码:

comment.component.ts

@Component({
 selector: 'comment',
 templateUrl: './comment.component.html',
 styleUrls: ['./comment.component.css']
})
export class CommentComponent implements OnInit {

 @Input()
 public comments: Comment[];

 ngOnInit(): void {
 }
}

comment.component.html

<div class="container font-small">
 <div class="row">
 <div class="col-lg-8 offset-lg-2 col-md-10 offset-md-1">

  <comment-view [comments]="comments"></comment-view>

  <div class="well" id="comment">
  <h4>{{ 'comment.leaveComment' | translate }}</h4>
  <form role="form">
   <div class="form-group">
   <input type="hidden" [(ngModel)]="id" name="id">
   <textarea [(ngModel)]="content" name="content" class="form-control" rows="5"></textarea>
   </div>
   <button type="submit" class="btn btn-primary">Submit</button>
  </form>
  </div>
 </div>
 </div>
</div>

comment.component.css

.media {
 font-size: 14px;
}

.media-object {
 padding-left: 10px;
}

子模块 ComponentViewComponent 代码:

component-view.component.ts

@Component({
 selector: 'comment-view',
 templateUrl: './comment-view.component.html',
 styleUrls: ['./comment-view.component.css']
})
export class CommentViewComponent implements OnInit {
 @Input()
 public comments: Comment[];

 constructor(private router: Router,
    private activateRoute: ActivatedRoute ) {
 }

 ngOnInit(): void {
 }
}

component-view.component.html

<div *ngFor="let comment of comments">
 <div class="media">
 <div class="pull-left">
  <span class="media-object"></span>
 </div>
 <div class="media-body">
  <h4 class="media-heading">{{ comment.username }}
  <small class="pull-right">{{ comment.time }} | <a href="#" rel="external nofollow" >{{ 'comment.reply' | translate }}</a></small>
  </h4>
  {{ comment.content }}
  <hr>
  <comment-view *ngIf="comment.cComments != null" [comments]="comment.cComments"></comment-view>
 </div>
 </div>
</div>

comonent-view.component.css

.media {
 font-size: 14px;
}

.media-object {
 padding-left: 10px;
}

结果

这时的展示结果如下图所示:

Angular实现类似博客评论的递归显示及获取回复评论的数据

上面只是说明了如何实现评论梯形显示,在博客评论中我们经常看到可以回复某一条评论,本文讲述如何实现点击某一条评论的回复按钮后,获取该条评论的内容并显示在输入框中。类似 CSDN 博客评论一样,点击回复后输入框自动添加了 [reply]u011642663[/reply]

思路

依据上一篇文章中的评论梯形显示,我们还需要实现点击回复后,屏幕自动到达输入框位置,并且获取了点击回复的评论的信息。首先分解一下这个功能点,在项目中我们也会经常分解功能点,这个功能点有 2 个小点:一是在每条评论中加上 [回复] 按钮,点击回复后跳到输入框位置;二是点击回复后,获取到点击回复的那条评论的信息。下面我们一一解决。

跳转到输入框

我们接触前段第一个语言便是 HTML,我们知道 HTML 中有一个 # 定位,下面代码简单解释一下。

假设这个 HTML 代码文件是 index.html

<html>
 <head>
 </head>
 <body>
 <a href="index.html#pointer" rel="external nofollow" >Click me to pointer</a>
 <div id="pointer">
  <h1>哈哈哈哈</h1>
 </div>
 </body>
</html>

上面代码只要点击 Click me to pointer 这个链接,页面就会跳到 id=”pointer” 这个 div 的位置。所以我们在实现这个点击回复跳转到输入框中就可以使用这个方法。

我们在 comment-component.html 中将评论输入框加入 id=”comment”,接下来就是路径拼接的问题了,我们可以通过 Angular 的 Router 的 url 来获取本页面的路径,然后在这个路径后面加入 #comment 就可以实现跳转了,下面是实现这个跳转功能的代码

添加 id=”comment”

comment-component.html

<!-- Comment -->
<div class="container font-small">
 <div class="row">
 <div class="col-lg-8 offset-lg-2 col-md-10 offset-md-1">

  <comment-view [comments]="comments" (contentEvent)="getReplyComment($event)" ></comment-view>

  <div class="well" id="comment">
  <h4>{{ 'comment.leaveComment' | translate }}</h4>
  <form role="form">
   <div class="form-group">
   <input type="hidden" [(ngModel)]="id" name="id">
   <textarea [(ngModel)]="content" name="content" class="form-control" rows="5"></textarea>
   </div>
   <button type="submit" class="btn btn-primary">Submit</button>
  </form>
  </div>
 </div>
 </div>
</div>

添加通过路由获取当前页面 URL

comment-view.component.ts

@Component({
 selector: 'comment-view',
 templateUrl: './comment-view.component.html',
 styleUrls: ['./comment-view.component.css']
})
export class CommentViewComponent implements OnInit {
 @Input()
 public comments: Comment[];

 // 用于跳转到回复输入框的url拼接
 public url: string = "";

 constructor(private router: Router,
    private activateRoute: ActivatedRoute ) {
 }

 ngOnInit(): void {
 this.url = this.router.url;
 this.url = this.url.split("#")[0];
 this.url = this.url + "#comment";
 }
}

添加链接 href=”“

comment-view.component.html

<div *ngFor="let comment of comments">
 <div class="media">
 <div class="pull-left">
  <span class="media-object"></span>
 </div>
 <div class="media-body">
  <h4 class="media-heading">{{ comment.username }}
  <small class="pull-right">{{ comment.time }} | <a href="{{url}}" rel="external nofollow" rel="external nofollow" (click)="reply(comment)" >{{ 'comment.reply' | translate }}</a></small>
  </h4>
  {{ comment.content }}
  <hr>
  <comment-view *ngIf="comment.cComments != null" [comments]="comment.cComments" (contentEvent)="transferToParent($event)"></comment-view>
 </div>
 </div>
</div>

这就实现了页面跳转的功能点,接下来实现获取回复的评论的信息。

获取回复的评论信息

有人会说获取回复的评论信息,这不简单么?加个 click 事件不就行了。还记得上一篇文章咱们是如何实现梯形展示评论的么?咱们是通过递归来实现的,怎么添加 click 事件让一个不知道嵌了多少层的组件能够把评论信息传给父组件?首先不具体想怎么实现,我们这个思路是不是对的:把子组件的信息传给父组件?答案是肯定的,我们就是要把不管嵌了多少层的子组件的信息传给 comment.component.ts 这个评论模块的主组件。
Angular 提供了 @Output 来实现子组件向父组件传递信息,我们在 comment-view.component.ts 模块中添加 @Output 向每个调用它的父组件传信息,我们是嵌套的,这样一层一层传出来,直到传给 comment-component.ts 组件。我们看代码怎么实现。

实现代码

comment-view.component.ts

@Component({
 selector: 'comment-view',
 templateUrl: './comment-view.component.html',
 styleUrls: ['./comment-view.component.css']
})
export class CommentViewComponent implements OnInit {
 @Input()
 public comments: Comment[];
 // 点击回复时返回数据
 @Output()
 public contentEvent: EventEmitter<Comment> = new EventEmitter<Comment>();
 // 用于跳转到回复输入框的url拼接
 public url: string = "";

 constructor(private router: Router,
    private activateRoute: ActivatedRoute ) {
 }

 ngOnInit(): void {
 this.url = this.router.url;
 this.url = this.url.split("#")[0];
 this.url = this.url + "#comment";
 }

 reply(comment: Comment) {
 this.contentEvent.emit(comment);
 }

 transferToParent(event) {
 this.contentEvent.emit(event);
 }
}

comment-view.component.html

<div *ngFor="let comment of comments">
 <div class="media">
 <div class="pull-left">
  <span class="media-object"></span>
 </div>
 <div class="media-body">
  <h4 class="media-heading">{{ comment.username }}
  <small class="pull-right">{{ comment.time }} | <a href="{{url}}" rel="external nofollow" rel="external nofollow" (click)="reply(comment)" >{{ 'comment.reply' | translate }}</a></small>
  </h4>
  {{ comment.content }}
  <hr>
  <comment-view *ngIf="comment.cComments != null" [comments]="comment.cComments" (contentEvent)="transferToParent($event)"></comment-view>
 </div>
 </div>
</div>

comment.component.ts

@Component({
 selector: 'comment',
 templateUrl: './comment.component.html',
 styleUrls: ['./comment.component.css']
})
export class CommentComponent implements OnInit {

 @Input()
 public comments: Comment[];

 // 要回复的评论
 public replyComment: Comment = new Comment();

 public id: number = 0;
 public content: string = "";

 ngOnInit(): void {
 }

 getReplyComment(event) {
 this.replyComment = event;
 this.id = this.replyComment.id;
 this.content = "[reply]" + this.replyComment.username + "[reply]\n";
 }
}

comment.component.html

<!-- Comment -->
<div class="container font-small">
 <div class="row">
 <div class="col-lg-8 offset-lg-2 col-md-10 offset-md-1">

  <comment-view [comments]="comments" (contentEvent)="getReplyComment($event)" ></comment-view>

  <div class="well" id="comment">
  <h4>{{ 'comment.leaveComment' | translate }}</h4>
  <form role="form">
   <div class="form-group">
   <input type="hidden" [(ngModel)]="id" name="id">
   <textarea [(ngModel)]="content" name="content" class="form-control" rows="5"></textarea>
   </div>
   <button type="submit" class="btn btn-primary">Submit</button>
  </form>
  </div>
 </div>
 </div>
</div>

解释一下代码逻辑:

我们在 comment-view.component.ts 添加以下几点:

  • 定义了@Output() contentEvent
  • 添加了reply(comment: Comment) 事件在点击回复的时候触发的,触发的时候 contentEvent 将 comment 传到父模块
  • 添加 transferToParent(event) 是接受子组件传来的 event, 并且继续将 event 传到父组件

在 comment.component.ts 中定义了 getReplyComment(event) 方法,该方法接收子组件传递来的评论信息,并将信息显示在页面上。大功告成。。。

效果图

Angular实现类似博客评论的递归显示及获取回复评论的数据

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
jQuery中:image选择器用法实例
Jan 03 Javascript
JS中获取函数调用链所有参数的方法
May 07 Javascript
分享十五款 jQuery 社交网络分享插件
May 16 Javascript
JavaScript中的prototype原型学习指南
May 09 Javascript
AngularJS 视图详解及示例代码
Aug 17 Javascript
基于Bootstrap和jQuery构建前端分页工具实例代码
Nov 23 Javascript
a标签置灰不可点击的实现方法
Feb 06 Javascript
详谈jQuery中使用attr(), prop(), val()获取value的异同
Apr 25 jQuery
ReactJs实现树形结构的数据显示的组件的示例
Aug 18 Javascript
AngularJS实现的获取焦点及失去焦点时的表单验证功能示例
Oct 25 Javascript
Node.js Koa2使用JWT进行鉴权的方法示例
Aug 17 Javascript
Node.js API详解之 net模块实例分析
May 18 Javascript
vue自定义过滤器创建和使用方法详解
Nov 06 #Javascript
Bootstrap 树控件使用经验分享(图文解说)
Nov 06 #Javascript
vue复合组件实现注册表单功能
Nov 06 #Javascript
基于vue组件实现猜数字游戏
May 28 #Javascript
vue路由嵌套的SPA实现步骤
Nov 06 #Javascript
vue综合组件间的通信详解
Nov 06 #Javascript
Angular 4中如何显示内容的CSS样式示例代码
Nov 06 #Javascript
You might like
收音机另类DIY - 纸巾盒做外壳
2021/03/02 无线电
基于mysql的论坛(7)
2006/10/09 PHP
PHP OPCode缓存 APC详细介绍
2010/10/12 PHP
自己写的php curl库实现整站克隆功能
2015/02/12 PHP
php读取文件内容到数组的方法
2015/03/16 PHP
全面解析PHP面向对象的三大特征
2017/06/10 PHP
PHP实现广度优先搜索算法(BFS,Broad First Search)详解
2017/09/16 PHP
PHP示例演示发送邮件给某个邮箱
2019/04/03 PHP
使用jQuery的将桌面应用程序引入浏览器
2010/11/19 Javascript
js实现屏幕自适应局部代码分享
2015/01/30 Javascript
jquery.map()方法的使用详解
2015/07/09 Javascript
js中unicode转码方法详解
2015/10/09 Javascript
JavaScript过滤字符串中的中文与空格方法汇总
2016/03/07 Javascript
Node.js的npm包管理器基础使用教程
2016/05/26 Javascript
浅谈jquery高级方法描述与应用
2016/10/04 Javascript
自定义require函数让浏览器按需加载Js文件
2016/11/24 Javascript
浅谈Angular路由守卫
2017/08/26 Javascript
Vue项目组件化工程开发实践方案
2018/01/09 Javascript
vue-cli webpack2项目打包优化分享
2018/02/07 Javascript
webpack里使用jquery.mCustomScrollbar插件的方法
2018/05/30 jQuery
JS删除String里某个字符的方法
2021/01/06 Javascript
vue中注册自定义的全局js方法
2019/11/15 Javascript
[01:01:41]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Magma BO3 第二场 1月31日
2021/03/11 DOTA
Python实现向QQ群成员自动发邮件的方法
2014/11/19 Python
python打包成so文件过程解析
2019/09/28 Python
python 实现提取log文件中的关键句子,并进行统计分析
2019/12/24 Python
Python基础教程之输入输出和运算符
2020/07/26 Python
python如何快速拼接字符串
2020/10/28 Python
利用CSS3的定位页面元素
2009/08/29 HTML / CSS
html5的画布canvas——画出弧线、旋转的图形实例代码+效果图
2013/06/09 HTML / CSS
超市总经理岗位职责
2014/02/02 职场文书
寒暑假实习证明书模板
2014/11/29 职场文书
党员检讨书范文
2014/12/27 职场文书
环卫处个人工作总结
2015/03/04 职场文书
2019财务管理制度最新范本!
2019/07/09 职场文书
详解Python常用的魔法方法
2021/06/03 Python