abp小试牛刀之前端开发
前端页面开发相关
- 添加newsitem模块
ng generate module newsitem --route newsitem --module app.module
- 修改路由并添加主题:app-routing.module.ts
import { ApplicationLayoutComponent } from '@abp/ng.theme.basic';
{
path: 'newsitem',
component: ApplicationLayoutComponent,
loadChildren: () => import('./news/newsitem/newsitem.module').then(m => m.NewsitemModule),
data: {
routes: {
name: '::News:NewsItem'
} as ABP.Route
}
}
- 路由占位符:newsitem.component.html
<router-outlet></router-outlet>
- 添加newsitem的列表组件
ng generate component news/newsitem/newsitem-list
- 在newsitem.module.ts中引入SharedModule使用共享的组件和服务
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NewsitemRoutingModule } from './newsitem-routing.module';
import { NewsitemComponent } from './newsitem.component';
import { NewsitemListComponent } from './newsitem-list/newsitem-list.component';
import { SharedModule } from 'src/app/shared/shared.module';
@NgModule({
declarations: [NewsitemComponent, NewsitemListComponent],
imports: [
CommonModule,
NewsitemRoutingModule,
SharedModule
]
})
export class NewsitemModule { }
- 添加模块子组件并修改路由:newsitem-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { NewsitemComponent } from './newsitem.component';
import { NewsitemListComponent } from './newsitem-list/newsitem-list.component';
const routes: Routes = [
{
path: '',
component: NewsitemComponent,
children: [{ path: '', component: NewsitemListComponent }]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class NewsitemRoutingModule {}
- 添加state
ng generate ngxs-schematic:state newsitems
说明:对state必须要类上添加Injectable依赖注入
import { Injectable } from '@angular/core';
@Injectable()// 依赖注入
- 定义数据模型
import { ABP } from '@abp/ng.core';
// 第一步
// tslint:disable-next-line: no-namespace
export namespace NewsitemManagement {
export interface State {
result: Response;
selectedItem: NewsItem;
}
export type Response = ABP.PagedResponse<NewsItem>;
// 列表
export interface NewsItem {
title: string;
short: string;
full: string;
published: boolean;
startDateUtc: string;
endDateUtc: string;
allowComments: boolean;
orderBy: number;
id: string;
}
// 创建
export interface CreateNewsItemInput {
title: string;
short: string;
full: string;
published: boolean;
startDateUtc: string;
endDateUtc: string;
allowComments: boolean;
orderBy: number;
}
// 修改
export interface UpdateNewsItemInput extends CreateNewsItemInput {
id: string;
}
}
- 添加服务
ng generate service newsitem/newsitem
修改newsitems.actions.ts文件如下
// 第二步
// 描述要采取的操作及其关联元数据的类
import { ABP } from '@abp/ng.core';
import { NewsitemManagement } from 'src/app/store/models/newsitems';
// 列表
export class GetNewsItems {
static readonly type = '[NewsitemManagement] Get';
constructor(public payload?: ABP.PageQueryParams) {}
}
// 获取
export class GetById {
static readonly type = '[NewsitemManagement] Get NewsItem By Id';
constructor(public payload: string) {}
}
// 创建
export class CreateNewsItem {
static readonly type = '[NewsitemManagement] Create NewsItem';
constructor(public payload: NewsitemManagement.CreateNewsItemInput) {}
}
// 删除
export class DeleteNewsItem {
static readonly type = '[NewsitemManagement] Delete NewsItem';
constructor(public payload: string) {}
}
// 编辑
export class UpdateNewsItem {
static readonly type = '[NewsitemManagement] Update NewsItem';
constructor(public payload: NewsitemManagement.UpdateNewsItemInput & { id: string }) {}
// constructor(public payload: NewsitemManagement.UpdateNewsItemInput, public id?: string) { }
}
修改newsitem.service.ts文件如下
// 第三步
import { Injectable } from '@angular/core';
import { RestService, Rest, ABP } from '@abp/ng.core';
import { NewsitemManagement } from 'src/app/store/models';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class NewsitemService {
constructor(private rest: RestService) {}
// 列表
get(params = {} as ABP.PageQueryParams): Observable<NewsitemManagement.Response> {
const request: Rest.Request<null> = {
method: 'GET',
url: '/api/app/newsItem',
params
};
return this.rest.request<null, NewsitemManagement.Response>(request);
}
// 获取
getById(id: string): Observable<NewsitemManagement.NewsItem> {
const request: Rest.Request<null> = {
method: 'GET',
url: `/api/app/newsItem/${id}`
};
return this.rest.request<null, NewsitemManagement.NewsItem>(request);
}
// 创建
create(body: NewsitemManagement.CreateNewsItemInput): Observable<NewsitemManagement.NewsItem> {
const request: Rest.Request<NewsitemManagement.CreateNewsItemInput> = {
method: 'POST',
url: '/api/app/newsItem',
body,
};
return this.rest.request<NewsitemManagement.CreateNewsItemInput, NewsitemManagement.NewsItem>(request);
}
// 删除
delete(id: string): Observable<NewsitemManagement.NewsItem> {
const request: Rest.Request<null> = {
method: 'DELETE',
url: `/api/app/newsItem/${id}`,
};
return this.rest.request<null, NewsitemManagement.NewsItem>(request);
}
// 编辑
update(body: NewsitemManagement.UpdateNewsItemInput): Observable<NewsitemManagement.NewsItem> {
const url = `/api/app/newsItem/${body.id}`;
delete body.id;
const request: Rest.Request<NewsitemManagement.UpdateNewsItemInput> = {
method: 'PUT',
url,
body
};
return this.rest.request<NewsitemManagement.CreateNewsItemInput, NewsitemManagement.NewsItem>(request);
}
// update(updateBookInput: NewsitemManagement.UpdateNewsItemInput): Observable<NewsitemManagement.NewsItem> {
// return this.rest.request<NewsitemManagement.UpdateNewsItemInput, NewsitemManagement.NewsItem>({
// method: 'PUT',
// url: `/api/app/newsItem/${updateBookInput.id}`,
// body: updateBookInput
// });
// }
}
修改newsitems.state.ts文件如下
// 第四步
import { State, Action, StateContext, Selector } from '@ngxs/store';
import {
GetNewsItems,
CreateNewsItem,
DeleteNewsItem,
UpdateNewsItem,
GetById
} from '../actions/newsitems.actions';
import { NewsitemManagement } from '../models/newsitems';
import { NewsitemService } from 'src/app/news/newsitem/shared/newsitem.service';
import { tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { ABP } from '@abp/ng.core';
// 状态定义
@State<NewsitemManagement.State>({
name: 'NewsitemsState',
defaults: { result: {}, selectedItem: {} } as NewsitemManagement.State
})
@Injectable() // 依赖注入
export class NewsitemsState {
@Selector()
static get({ result }: NewsitemManagement.State): NewsitemManagement.NewsItem[] {
return result.items || [];
}
@Selector()
static getNewsItemTotalCount({ result }: NewsitemManagement.State): number {
return result.totalCount || 0;
}
constructor(private newsitemService: NewsitemService) {}
@Action(GetNewsItems)
get({ patchState }: StateContext<NewsitemManagement.State>, { payload }: GetNewsItems) {
return this.newsitemService.get(payload).pipe(
tap(result =>
patchState({
result
})
)
);
}
@Action(GetById)
getById({ patchState }: StateContext<NewsitemManagement.State>, { payload }: GetById) {
return this.newsitemService.getById(payload).pipe(
tap(selectedItem =>
patchState({
selectedItem,
}),
),
);
}
@Action(CreateNewsItem)
addNewsItem(ctx: StateContext<NewsitemManagement.State>, action: CreateNewsItem) {
return this.newsitemService.create(action.payload);
}
@Action(DeleteNewsItem)
deleteNewsItem(ctx: StateContext<NewsitemManagement.State>, action: DeleteNewsItem) {
return this.newsitemService.delete(action.payload);
}
@Action(UpdateNewsItem)
updateNewsItem(ctx: StateContext<NewsitemManagement.State>, action: UpdateNewsItem) {
return this.newsitemService.update(action.payload);
}
}
- 组件代码编写
修改newsitem-list.component.ts文件如下
import { Component, OnInit } from '@angular/core';
import { Store, Select } from '@ngxs/store';
import { NewsitemsState } from 'src/app/store/states';
import { Observable } from 'rxjs';
import { NewsitemManagement } from 'src/app/store/models';
import {
GetNewsItems,
CreateNewsItem,
UpdateNewsItem,
DeleteNewsItem,
GetById
} from 'src/app/store/actions';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { finalize, pluck, switchMap, take } from 'rxjs/operators';
import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap';
import { ABP } from '@abp/ng.core';
import { ConfirmationService, Toaster } from '@abp/ng.theme.shared';
@Component({
selector: 'app-newsitem-list',
templateUrl: './newsitem-list.component.html',
styleUrls: ['./newsitem-list.component.scss'],
providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }]
})
export class NewsitemListComponent implements OnInit {
@Select(NewsitemsState.get)
data$: Observable<NewsitemManagement.NewsItem[]>;
@Select(NewsitemsState.getNewsItemTotalCount)
totalCount$: Observable<number>;
loading = false;
isModalVisible = false;
modalBusy = false;
form: FormGroup;
pageQuery: ABP.PageQueryParams = { maxResultCount: 10 };
sortOrder = '';
sortKey = '';
selected: NewsitemManagement.NewsItem;
constructor(
private store: Store,
private fb: FormBuilder,
private confirmationService: ConfirmationService
) {}
ngOnInit(): void {
this.get();
}
get() {
this.loading = true;
this.store.dispatch(new GetNewsItems(this.pageQuery)).subscribe(() => {
this.loading = false;
});
}
onPageChange(page: number) {
this.pageQuery.skipCount = (page - 1) * this.pageQuery.maxResultCount;
this.get();
}
create() {
this.selected = {} as NewsitemManagement.NewsItem;
this.openModal();
}
edit(id: string) {
this.store
.dispatch(new GetById(id))
.pipe(pluck('NewsitemsState', 'selectedItem'))// selectedItem必须与state中的tap返回一致
.subscribe(selectedItem => {
this.selected = selectedItem;
this.openModal();
});
}
delete(id: string, title: string) {
this.confirmationService
.warn('::NewsItem:NewsItemDeletionConfirmationMessage', '::NewsItem:AreYouSure', {
messageLocalizationParams: [title]
})
.subscribe((status: Toaster.Status) => {
if (status === Toaster.Status.confirm) {
this.store.dispatch(new DeleteNewsItem(id)).subscribe(() => this.get());
}
});
}
openModal() {
this.buildForm();
this.isModalVisible = true;
}
buildForm() {
this.form = this.fb.group({
title: [this.selected.title || '', [Validators.required, Validators.maxLength(256)]],
short: [this.selected.short || '', [Validators.required, Validators.maxLength(256)]],
full: [this.selected.full || '', [Validators.required, Validators.maxLength(256)]],
startDateUtc: [this.selected.startDateUtc || '', [Validators.required]],
endDateUtc: [this.selected.endDateUtc || '', [Validators.required]],
published: [this.selected.published || false],
allowComments: [this.selected.allowComments || false],
orderBy: [this.selected.orderBy || 0]
});
}
save() {
if (!this.form.valid || this.modalBusy) {
return;
}
this.modalBusy = true;
this.store
.dispatch(
this.selected.id
? new UpdateNewsItem({
...this.selected,
...this.form.value,
id: this.selected.id
})
: new CreateNewsItem(this.form.value)
)
.pipe(finalize(() => (this.modalBusy = false)))
.subscribe(() => {
this.isModalVisible = false;
this.get();
});
}
}
修改newsitem-list.component.html文件如下
<div class="card">
<div class="card-header">
<div class="row">
<div class="col col-md-6">
<h5 class="card-title">
{{ '::NewsItem:List' | abpLocalization }}
</h5>
</div>
<div class="text-right col col-md-6">
<div class="text-lg-right pt-2">
<button id="create" class="btn btn-primary" type="button" (click)="create()">
<i class="fa fa-plus mr-1"></i>
<span>{{ '::NewsItem:Add' | abpLocalization }}</span>
</button>
</div>
</div>
</div>
</div>
<div class="card-body">
<abp-table *ngIf="[150, 0] as columnWidths" [abpLoading]="loading" [abpLoadingDelay]="500" [abpTableSort]="{ key: sortKey, order: sortOrder }" [colgroupTemplate]="tableColGroup"
[headerTemplate]="tableHeader" [bodyTemplate]="tableBody" [value]="data$ | async" [rows]="pageQuery.maxResultCount" [totalRecords]="totalCount$ | async" [scrollable]="true"
(pageChange)="onPageChange($event)">
<ng-template #tableColGroup>
<colgroup>
<col *ngFor="let width of columnWidths" [ngStyle]="{ 'width.px': width || undefined }" />
</colgroup>
</ng-template>
<ng-template #tableHeader>
<tr>
<th>{{ '::NewsItem:Actions' | abpLocalization }}</th>
<th (click)="sortOrderIcon.sort('title')">
{{ '::NewsItem:Title' | abpLocalization }}
<abp-sort-order-icon #sortOrderIcon sortKey="title" [(selectedSortKey)]="sortKey" [(order)]="sortOrder">
</abp-sort-order-icon>
</th>
<th (click)="sortOrderIcon.sort('short')">
{{ '::NewsItem:Short' | abpLocalization }}
<abp-sort-order-icon #sortOrderIcon sortKey="short" [(selectedSortKey)]="sortKey" [(order)]="sortOrder">
</abp-sort-order-icon>
</th>
<th (click)="sortOrderIcon.sort('orderBy')">
{{ '::NewsItem:OrderBy' | abpLocalization }}
<abp-sort-order-icon #sortOrderIcon sortKey="orderBy" [(selectedSortKey)]="sortKey" [(order)]="sortOrder">
</abp-sort-order-icon>
</th>
</tr>
</ng-template>
<ng-template #tableBody let-data>
<tr>
<td class="text-center">
<div ngbDropdown container="body" class="d-inline-block">
<button class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" ngbDropdownToggle>
<i class="fa fa-cog mr-1"></i>{{ '::NewsItem:Actions' | abpLocalization }}
</button>
<div ngbDropdownMenu>
<button ngbDropdownItem (click)="edit(data.id)">
{{ '::NewsItem:Edit' | abpLocalization }}
</button>
<button ngbDropdownItem (click)="delete(data.id, data.title)">
{{ '::NewsItem:Delete' | abpLocalization }}
</button>
</div>
</div>
</td>
<td>
{{ data.title }}
</td>
<td>
{{ data.short }}
</td>
<td>
{{ data.orderBy }}
</td>
</tr>
</ng-template>
</abp-table>
</div>
</div>
<abp-modal [(visible)]="isModalVisible" [busy]="modalBusy">
<ng-template #abpHeader>
<h3>{{ (selected?.id ? '::NewsItem:EditNewsItem' : '::NewsItem:NewNewsItem') | abpLocalization }}</h3>
</ng-template>
<ng-template #abpBody>
<form [formGroup]="form" (ngSubmit)="save()">
<div class="form-group">
<label for="news-title">{{ '::NewsItem:Title' | abpLocalization }}</label><span>*</span>
<input type="text" id="news-title" class="form-control" formControlName="title" autofocus />
</div>
<div class="form-group">
<label for="news-short">{{ '::NewsItem:Short' | abpLocalization }}</label><span>*</span>
<input type="text" id="news-short" class="form-control" formControlName="short" autofocus />
</div>
<div class="form-group">
<label for="news-full">{{ '::NewsItem:Full' | abpLocalization }}</label><span>*</span>
<input type="text" id="news-full" class="form-control" formControlName="full" autofocus />
</div>
<div class="form-group">
<label for="news-startDateUtc">{{'::NewsItem:StartDateUtc' | abpLocalization }}</label><span>*</span>
<!-- <input #datepicker="ngbDatepicker" class="form-control" name="startDateUtc" formControlName="startDateUtc" ngbDatepicker (click)="datepicker.toggle()" /> -->
<input type="text" id="news-endDateUtc" class="form-control" formControlName="startDateUtc" autofocus />
</div>
<div class="form-group">
<label for="news-endDateUtc">{{'::NewsItem:EndDateUtc' | abpLocalization }}</label><span>*</span>
<input type="text" id="news-endDateUtc" class="form-control" formControlName="endDateUtc" autofocus />
</div>
<div class="form-group">
<label for="news-orderBy">{{ '::NewsItem:OrderBy' | abpLocalization }}</label><span>*</span>
<input type="text" id="news-orderBy" class="form-control" formControlName="orderBy" />
</div>
<div class="form-group">
<label
for="news-allowComments">{{'::NewsItem:AllowComments' | abpLocalization }}</label><span>*</span>
<div class="custom-checkbox custom-control mb-2">
<input type="checkbox" id="allowComments-true" class="custom-control-input" formControlName="allowComments" />
<label class="custom-control-label" for="allowComments-true">{{'::NewsItem:AllowComments' | abpLocalization }}</label>
</div>
</div>
<div class="form-group">
<label for="news-published">{{ '::NewsItem:Published' | abpLocalization }}</label><span>*</span>
<div class="custom-checkbox custom-control mb-2">
<input type="checkbox" id="allowComments-published" class="custom-control-input" formControlName="published" />
<label class="custom-control-label" for="allowComments-published">{{'::NewsItem:Published' | abpLocalization}}</label>
</div>
</div>
</form>
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>{{ '::NewsItem:Cancel' | abpLocalization }}</button>
<abp-button iconClass="fa fa-check" (click)="save()" [disabled]="form?.invalid">{{'::NewsItem:Save' | abpLocalization }}</abp-button>
</ng-template>
</abp-modal>
效果如下





浙公网安备 33010602011771号