abp小试牛刀之前端开发

前端页面开发相关

  1. 添加newsitem模块
ng generate module newsitem --route newsitem --module app.module
  1. 修改路由并添加主题: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
    }
  }
  1. 路由占位符:newsitem.component.html
<router-outlet></router-outlet>
  1. 添加newsitem的列表组件
ng generate component news/newsitem/newsitem-list
  1. 在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 { }
  1. 添加模块子组件并修改路由: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 {}

  1. 添加state
ng generate ngxs-schematic:state newsitems

说明:对state必须要类上添加Injectable依赖注入

import { Injectable } from '@angular/core';
@Injectable()// 依赖注入
  1. 定义数据模型
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;
  }
}

  1. 添加服务
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);
  }
}

  1. 组件代码编写

修改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>

效果如下



posted @ 2020-04-09 16:04  struggle_new  阅读(502)  评论(0)    收藏  举报