In the last blog, we built the Story module and developed the writing story function in it. In this blog, we will implement the story list shown on the first page and start developing the comment function.
Development of Twelve Stories List
1 Front Part
We need to create a home component as the place where we present the list of stories. We open the cmd and type the following command to create a home component:
ng g c home
We then introduced tag components from NG-ZORRO in app.module.ts to categorize stories later:
//app.module.ts //... import { NzTabsModule } from 'ng-zorro-antd/tabs'; //... @NgModule({ declarations: [ //... ], imports: [ //... NzTabsModule, //... ], providers: [CookieService], bootstrap: [AppComponent] }) export class AppModule { }
Next, we open home.component.html and enter the following code:
<!--home.component.html--> <nz-tabset> <nz-tab nzTitle="whole"> <app-storylist></app-storylist> </nz-tab> <nz-tab nzTitle="Tab 2"> Content of Tab Pane 2 </nz-tab> <nz-tab nzTitle="Tab 3"> Content of Tab Pane 3 </nz-tab> </nz-tabset>
The app-storylist here is a component of the story list we'll build later, so if you want to see the effect, you can temporarily replace it with something else.
Then we add the home component to the routing list:
//app-routing.module.ts //.. const routes: Routes = [ //.. {path:'',component:HomeComponent}, //.. ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
In this way, a blank page with a tab will appear on our home page:
Next, let's continue to implement our storylist component in the story module. Create the storylist component by typing the following command in cmd:
ng g c storylist -m story
In this component we show two parts of the data: the first part is the basic information of the story, and the second part is all the comments related to the story, so we need to define two interfaces in this component, storyDetail and commentData:
//story/storylist/commentData.ts export interface commentData { author:string; content: string; commentBody:string; }
- Author: story author.
- Content: Story content.
- commentBody: A comment body, which means which story or comment this comment belongs to.
//story/storylist/storyDetail.ts export interface storyDetail { id:number; author:string; title: string; content: string; publishdate:string; commentCount:string; externalId:string; }
- Id: Story Id.
- Author: story author
- Title: story title
- Content: Story content
- publishdate: publish date
- commentCount: Number of comments
- externalId: An external Id in the format'doc_'plus the current time if it is a story and'cmt_' plus the current time if it is a comment. This Id will be used in future comment tree development.
Then we need to introduce the following modules in story.module.ts:
//story.moddule.ts //.. import { NzDividerModule } from 'ng-zorro-antd/divider'; import { NzSpaceModule } from 'ng-zorro-antd/space'; import { NzCardModule } from 'ng-zorro-antd/card'; import { NzIconModule } from 'ng-zorro-antd/icon'; import { IconDefinition } from '@ant-design/icons-angular'; import { NzCollapseModule } from 'ng-zorro-antd/collapse'; import { NzCommentModule } from 'ng-zorro-antd/comment'; import { NzAvatarModule } from 'ng-zorro-antd/avatar'; import { StarOutline, CommentOutline, LikeOutline } from '@ant-design/icons-angular/icons'; import { CommentsComponent } from './comments/comments.component'; import { CommentformComponent } from './commentform/commentform.component'; import { NzListModule } from 'ng-zorro-antd/list'; const icons: IconDefinition[] = [ StarOutline, CommentOutline, LikeOutline ]; @NgModule({ declarations: [ //.. ], imports: [ //... NzDividerModule, NzSpaceModule, NzCardModule, NzIconModule.forRoot(icons), NzCollapseModule, NzCommentModule, NzAvatarModule, NzListModule ], exports:[StorylistComponent] }) export class StoryModule { }
- NzDividerModule: A splitter line module for splitting text.
- NzSpaceModule: A spacing module that sets the spacing between components to prevent them from sticking together.
- NzCardModule: A card container that can hold basic content such as text and pictures.
- Icon Library provided by NzIconModule:NG-ZORRO.
- NzCollapseModule: Collapse Panel Component
- The comment box component provided by NzCommentModule:NG-ZORRO is the base component of our comment tree. It provides the basic functions of common comments, such as compliment, click, reply, and even a reuse user's avatar. Its components work as follows:
- NzAvatarModule: The avatar component is the head on the left of the image above.
- NzListModule: A list component that displays content in a list format.
Now that the above modules have been introduced, we can start writing the front-end part of the storylist. Open the storylist.component.html file and enter the following code:
<!--storylist.component.html--> <div *ngFor="let story of storyList" > <nz-card nzTitle="{{story.title}}" > <div innerHtml='{{story.content}}'></div> </nz-card> <nz-card> <nz-space> <span *nzSpaceItem><i nz-icon nzType="star" nzTheme="outline"></i> Collection</span> <span *nzSpaceItem ><i nz-icon nzType="comment" nzTheme="outline"></i><a> comment</a> </span> <span *nzSpaceItem><i nz-icon nzType="like" nzTheme="outline"></i> like</span> </nz-space> </nz-card> <br> </div>
This list of stories is structured simply to list the data returned by all the back-end servers in a card fashion. It is worth noting that because we use a rich text editor to store stories, we use the innerHtml attribute when displaying the story content instead of placing the content directly inside the tag, otherwise the rich text format will not be displayed.
Then we open the storylist.component.ts file and start writing the logical part of the front end:
//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'; @Component({ selector: 'app-storylist', templateUrl: './storylist.component.html', styleUrls: ['./storylist.component.scss'] }) export class StorylistComponent implements OnInit { storyList:storyDetail[]; constructor(private storyService:StoryService) { this.storyList = []; this.showAllStory(); } ngOnInit(): void { } showAllStory(){ this.storyService.showAllStory().subscribe((data:any) => { if (data) { data['storylist'].forEach((element:storyDetail) => { this.storyList.push({ 'id':element['id'], 'title':element['title'], 'content':element['content'], 'author':element['author'], 'publishdate':element['publishdate'], 'commentCount':element['commentCount'], 'externalId':element['externalId'] }) }); } }) } }
Now the front-end logic contains a storyService object and a list of storyDetails, which we show on the front-end by taking all the story data from the storyService object in the showAllStory function and storing it in the storyDetail list.
Then, we open the story.service.ts file to implement the showAllStory function in the service:
//service/story.service.ts //... @Injectable({ providedIn: 'root' }) export class StoryService { constructor(private http:HttpClient) { } //... showAllStory(){ return this.http.get('http://localhost:8000/getallstory') } }
In this way, we have completed the front part of the story list component. Next, let's continue to implement the split distribution of the back part of the story list.
2 rear part
We open story_comment_app.py to implement the GetAllStory class:
# server/apps/story_comment_app/story_comment_app.py class GetAllStory(BaseHandler): def get(self): allstory = session.query(Storys).all() allstorylist = [] for story in allstory: story_dir = { 'id':story.id, 'title':story.title, 'content':story.content, 'author':story.author, 'publishdate':story.publishDate.__str__(), 'commentCount':story.commentCount, 'externalId':story.externalId } allstorylist.append(story_dir) storylist = {'storylist':allstorylist} self.write(json.dumps(storylist))
There's nothing to say about this function. The current logic is to list all the data in the Stories table, store it in a dictionary in a list, and return the list to the front end as a json.
Finally, we add GetAllStory to the back-end routing:
# server/main.py # ... routelist = [ # ... (r"/getallstory",GetAllStory), # ... ] # ...
That way, we've finished developing our story list, and now we should be able to see our published stories on the first page:
Next, let's get into the big game, the development of the review system.
Development of Thirteen Comments System
1 Comment on system structure
Let's first look at the structure of this review system:
As you can see from the image above, each story can have multiple comments in the expanded comment tree, and for each comment, new comments can be made individually and indented below it. This requires us to implement a nested component that contains both published comments and a form for new comments.The structure of the review system is shown in the following figure:
In the diagram, each Comment has two parts: the sub-comments below it and the form for commenting. Therefore, our comment system consists of two components: a list showing the sub-comments and a form for posting comments. List showing the sub-comments receives all the sub-comments under that comment, including the grand-child comments, recursively from the back end, and publishes the comments.The form function is relatively simple, just a normal form. This way, we can combine the two components to achieve our comment tree function.
In this blog, we have implemented the story list function and introduced the structure of the comment system. In the next blog, we will continue to lead you to develop the comment system and introduce the related knowledge of templates in angular. We hope you will continue to pay attention to ~