iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 29
1
Blockchain

區塊練起來-智能合約與DApp開發系列 第 29

[區塊練起來-智能合約與DApp開發] DAY 29 - 實戰DApp!區塊鏈履歷應用(3)

  • 分享至 

  • twitterImage
  •  

貼心小語

上一篇做完了 Government 與 Host 的功能頁面,還將共同功能抽離出來做成 ComponentBase ,使我們開發更順暢,所以今天來完成教育單位與企業單位的功能吧/images/emoticon/emoticon18.gif


教育單位專區

將新增學歷、新增修課證明與新增證照個別設計成元件(Component),一樣會使用 Tabset 的形式來呈現,有些基本的配置這邊就不會再說明,如:在 Module 裡引入 FormsModule 等操作。

新增學歷之元件

透過 AngularCLI 產生元件:

ng g c modules/main/school/components/education/school-education-add --flat

在新增學歷的時候會需要設置目前的就學狀態,所以我們先設計列舉在 types 資料夾的 form.ts 中:

export enum EducationStatus {
    undergraduate,
    learning,
    graduate
}

好了之後我們就來設計 school-education-add.component.ts

import { Component, Injector } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { take } from 'rxjs/operators';
import { ComponentBase } from 'src/app/base/component.base';
import { EducationStatus } from 'src/app/types';

@Component({
    selector: 'app-school-education-add',
    templateUrl: './school-education-add.component.html',
    styleUrls: ['./school-education-add.component.scss']
})
export class SchoolEducationAddComponent extends ComponentBase {
    public educationForm: FormGroup;

    constructor(
        private injector: Injector,
        private formBuilder: FormBuilder
    ) {
        super(injector);
        this.educationForm = this.formBuilder.group({
            contract: ['', [Validators.required, this.addressValidator]],
            major: ['', [Validators.required]],
            status: [EducationStatus.undergraduate, [Validators.required]]
        });
    }

    public addEducation(data: any): void {
        this.isPending = true;
        this.setFormDisabled(this.educationForm);
        const resume = this.providerSvc.getResume(data.contract);
        this.providerSvc.executeMethod(
            resume.methods.setEducation(data.status, data.major)
            .send({ from: this.providerSvc.defaultAccount })
        ).pipe(
            take(1)
        ).subscribe(
            receipt => {
                this.transactionConfirmed();
                this.educationForm.reset();
                this.setFormDisabled(this.educationForm, false);
            },
            err => {
                this.transactionError(err.message);
                this.educationForm.reset();
                this.setFormDisabled(this.educationForm, false);
            }
        );
    }
    
}

設計頁面:

<div class="my-3" *ngIf="!isConfirmed && !isError">
    <form
        [formGroup]="educationForm"
        (ngSubmit)="addEducation(educationForm.value)"
    >
        <div class="form-group">
            <label for="contract">履歷位址</label>
            <input
                type="text"
                class="form-control"
                id="contract"
                placeholder="輸入履歷位址"
                [formControlName]="'contract'"
            >
        </div>
        <div class="form-group">
            <label for="major">主修科系</label>
            <input
                type="text"
                class="form-control"
                id="major"
                placeholder="輸入主修科系"
                [formControlName]="'major'"
            >
        </div>
        <div class="form-group">
            <label for="status">就學狀態</label>
            <select class="form-control" id="status" [formControlName]="'status'">
                <option [ngValue]="0">畢業</option>
                <option [ngValue]="1">在學中</option>
                <option [ngValue]="2">肄業</option>
            </select>
        </div>
        <div class="text-right">
            <button type="submit" class="btn btn-primary" [disabled]="educationForm.invalid || isPending">新增</button>
        </div>
    </form>
</div>
<div class="my-3" *ngIf="isConfirmed">
    <div class="alert alert-success alert-dismissible fade show" role="alert">
        <strong>更新完成</strong>
        <button type="button" class="close" (click)="resetConfirmState()">
          <span aria-hidden="true">×</span>
        </button>
    </div>
</div>
<div class="my-3" *ngIf="isError">
    <div class="alert alert-danger alert-dismissible fade show" role="alert">
        <strong>發生錯誤</strong> {{ errorMessage }}
        <button type="button" class="close" (click)="resetErrorState()">
          <span aria-hidden="true">×</span>
        </button>
    </div>
</div>

新增修課證明之元件

透過 AngularCLI 產生元件:

ng g c modules/main/school/components/course/school-course-add --flat

設計 school-course-add.component.ts

import { Component, Injector } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { ComponentBase } from 'src/app/base/component.base';
import { take } from 'rxjs/operators';

@Component({
    selector: 'app-school-course-add',
    templateUrl: './school-course-add.component.html',
    styleUrls: ['./school-course-add.component.scss']
})
export class SchoolCourseAddComponent extends ComponentBase {
    public courseForm: FormGroup;

    constructor(
        private injector: Injector,
        private formBuilder: FormBuilder
    ) {
        super(injector);
        this.courseForm = this.formBuilder.group({
            contract: ['', [Validators.required, this.addressValidator]],
            name: ['', [Validators.required]],
            content: ['', [Validators.required]],
            comment: ['', [Validators.required]],
            grade: ['', [Validators.required, Validators.pattern(/^(?:[1-9]?\d|100)$/)]]
        });
    }

    public addCourse(data: any): void {
        this.isPending = true;
        this.setFormDisabled(this.courseForm);
        const resume = this.providerSvc.getResume(data.contract);
        this.providerSvc.executeMethod(
            resume.methods.setCourse(data.name, data.content, data.comment, data.grade)
            .send({ from: this.providerSvc.defaultAccount })
        ).pipe(
            take(1)
        ).subscribe(
            receipt => {
                this.transactionConfirmed();
                this.courseForm.reset();
                this.setFormDisabled(this.courseForm, false);
            },
            err => {
                this.transactionError();
                this.courseForm.reset();
                this.setFormDisabled(this.courseForm, false);
            }
        );
    }

}

設計頁面:

<div class="my-3" *ngIf="!isConfirmed && !isError">
    <form
        [formGroup]="courseForm"
        (ngSubmit)="addCourse(courseForm.value)"
    >
        <div class="form-group">
            <label for="contract">履歷位址</label>
            <input
                type="text"
                class="form-control"
                id="contract"
                placeholder="輸入履歷位址"
                [formControlName]="'contract'"
            >
        </div>
        <div class="form-group">
            <label for="name">課程名稱</label>
            <input
                type="text"
                class="form-control"
                id="name"
                placeholder="輸入課程名稱"
                [formControlName]="'name'"
            >
        </div>
        <div class="form-group">
            <label for="content">課程內容</label>
            <textarea
                class="form-control"
                id="content"
                rows="5"
                [formControlName]="'content'"
            ></textarea>
        </div>
        <div class="form-group">
            <label for="grade">成績</label>
            <input
                type="text"
                class="form-control"
                id="grade"
                placeholder="輸入成績0~100"
                [formControlName]="'grade'"
            >
        </div>
        <div class="form-group">
            <label for="comment">評論</label>
            <textarea
                class="form-control"
                id="comment"
                rows="5"
                [formControlName]="'comment'"
            ></textarea>
        </div>
        <div class="text-right">
            <button type="submit" class="btn btn-primary" [disabled]="courseForm.invalid || isPending">新增</button>
        </div>
    </form>
</div>
<div class="my-3" *ngIf="isConfirmed">
    <div class="alert alert-success alert-dismissible fade show" role="alert">
        <strong>更新完成</strong>
        <button type="button" class="close" (click)="resetConfirmState()">
          <span aria-hidden="true">×</span>
        </button>
    </div>
</div>
<div class="my-3" *ngIf="isError">
    <div class="alert alert-danger alert-dismissible fade show" role="alert">
        <strong>發生錯誤</strong> {{ errorMessage }}
        <button type="button" class="close" (click)="resetErrorState()">
          <span aria-hidden="true">×</span>
        </button>
    </div>
</div>

新增證照之元件

透過 AngularCLI 產生元件:

ng g c modules/main/school/components/license/school-license-add --flat

設計 school-license-add.component.ts

import { Component, Injector } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { take } from 'rxjs/operators';
import { ComponentBase } from 'src/app/base/component.base';

@Component({
    selector: 'app-school-license-add',
    templateUrl: './school-license-add.component.html',
    styleUrls: ['./school-license-add.component.scss']
})
export class SchoolLicenseAddComponent extends ComponentBase {
    public licenseForm: FormGroup;

    constructor(
        private injector: Injector,
        private formBuilder: FormBuilder
    ) {
        super(injector);
        this.licenseForm = this.formBuilder.group({
            contract: ['', [Validators.required, this.addressValidator]],
            name: ['', [Validators.required]],
            content: ['', [Validators.required]]
        });
    }

    public addLicense(data: any): void {
        const resume = this.providerSvc.getResume(data.contract);
        this.providerSvc.executeMethod(
            resume.methods.setLicense(data.name, data.content)
            .send({ from: this.providerSvc.defaultAccount })
        ).pipe(
            take(1)
        ).subscribe(
            receipt => {
                this.transactionConfirmed();
                this.licenseForm.reset();
                this.setFormDisabled(this.licenseForm, false);
            },
            err => {
                this.transactionError();
                this.licenseForm.reset();
                this.setFormDisabled(this.licenseForm, false);
            }
        );
    }

}

設計頁面:

<div class="my-3" *ngIf="!isConfirmed && !isError">
    <form
        [formGroup]="licenseForm"
        (ngSubmit)="addLicense(licenseForm.value)"
    >
        <div class="form-group">
            <label for="contract">履歷位址</label>
            <input
                type="text"
                class="form-control"
                id="contract"
                placeholder="輸入履歷位址"
                [formControlName]="'contract'"
            >
        </div>
        <div class="form-group">
            <label for="name">證照名稱</label>
            <input
                type="text"
                class="form-control"
                id="name"
                placeholder="輸入證照名稱"
                [formControlName]="'name'"
            >
        </div>
        <div class="form-group">
            <label for="content">證照內容</label>
            <textarea
                class="form-control"
                id="content"
                rows="5"
                [formControlName]="'content'"
            ></textarea>
        </div>
        <div class="text-right">
            <button type="submit" class="btn btn-primary" [disabled]="licenseForm.invalid || isPending">新增</button>
        </div>
    </form>
</div>
<div class="my-3" *ngIf="isConfirmed">
    <div class="alert alert-success alert-dismissible fade show" role="alert">
        <strong>更新完成</strong>
        <button type="button" class="close" (click)="resetConfirmState()">
          <span aria-hidden="true">×</span>
        </button>
    </div>
</div>
<div class="my-3" *ngIf="isError">
    <div class="alert alert-danger alert-dismissible fade show" role="alert">
        <strong>發生錯誤</strong> {{ errorMessage }}
        <button type="button" class="close" (click)="resetErrorState()">
          <span aria-hidden="true">×</span>
        </button>
    </div>
</div>

添加到 School 元件

修改 school.component.html

<div class="container my-3">
  <div class="jumbotron jumbotron-fluid">
      <div class="container">
          <h1 class="display-4">教育單位專區</h1>
          <p class="lead">提供教育單位新增學歷、修課證明以及證照的頁面</p>
      </div>
  </div>
</div>
<div class="container">
  <ngb-tabset>
      <ngb-tab title="新增學歷">
          <ng-template ngbTabContent>
              <app-school-education-add></app-school-education-add>
          </ng-template>
      </ngb-tab>
      <ngb-tab title="新增修課證明">
          <ng-template ngbTabContent>
              <app-school-course-add></app-school-course-add>
          </ng-template>
      </ngb-tab>
      <ngb-tab title="新增證照">
          <ng-template ngbTabContent>
              <app-school-license-add></app-school-license-add>
          </ng-template>
    </ngb-tab>
  </ngb-tabset>
</div>

成果展示

新增學歷頁面:
https://ithelp.ithome.com.tw/upload/images/20190925/20119338naIN8uqKCB.png
新增修課證明頁面:
https://ithelp.ithome.com.tw/upload/images/20190925/20119338mnxRuFdERC.png
新增證照頁面:
https://ithelp.ithome.com.tw/upload/images/20190925/20119338ixuYiN9b7E.png

企業單位專區

主要會有設置工作經歷與設置離職日的功能,所以一樣拆成兩個元件,並用 Tabset 來顯示成果。

更新 BootStrap 模組

因為我們要使用 BootStrap 的日期選擇器,所以我們在這邊引入 NgbDatepickerModule

import { NgModule } from '@angular/core';
import { NgbTabsetModule, NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap';

@NgModule({
    imports: [
        NgbTabsetModule,
        NgbDatepickerModule
    ],
    exports: [
        NgbTabsetModule,
        NgbDatepickerModule
    ]
})
export class BootstrapModule { }

設置工作經歷之元件

透過 AngularCLI 產生元件:

ng g c modules/main/company/components/experience/company-experience-add --flat

設計 company-experience-add.component.ts

mport { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { take } from 'rxjs/operators';
import { ComponentBase } from 'src/app/base/component.base';

@Component({
  selector: 'app-company-experience-add',
  templateUrl: './company-experience-add.component.html',
  styleUrls: ['./company-experience-add.component.scss']
})
export class CompanyExperienceAddComponent extends ComponentBase {
    public experienceForm: FormGroup;

    constructor(
        private injector: Injector,
        private formBuilder: FormBuilder
    ) {
        super(injector);
        this.experienceForm = this.formBuilder.group({
            contract: ['', [Validators.required, this.addressValidator]],
            position: ['', [Validators.required]],
            startDate: ['', [Validators.required]]
        });
    }

    public addExperience(data: any): void {
        data.startDate = new Date(data.startDate.year, data.startDate.month - 1, data.startDate.day).valueOf();
        this.isPending = true;
        this.setFormDisabled(this.experienceForm);
        const resume = this.providerSvc.getResume(data.contract);
        this.providerSvc.executeMethod(
            resume.methods.setExperience(data.position, data.startDate)
            .send({ from: this.providerSvc.defaultAccount })
        ).pipe(
            take(1)
        ).subscribe(
            receipt => {
                this.transactionConfirmed();
                this.experienceForm.reset();
                this.setFormDisabled(this.experienceForm, false);
            },
            err => {
                this.transactionError();
                this.experienceForm.reset();
                this.setFormDisabled(this.experienceForm, false);
            }
        );
    }

}

設計頁面:

<div class="my-3" *ngIf="!isConfirmed && !isError">
    <form
        [formGroup]="experienceForm"
        (ngSubmit)="addExperience(experienceForm.value)"
    >
        <div class="form-group">
            <label for="contract">履歷位址</label>
            <input
                type="text"
                class="form-control"
                id="contract"
                placeholder="輸入履歷位址"
                [formControlName]="'contract'"
            >
        </div>
        <div class="form-group">
            <label for="position">職稱</label>
            <input
                type="text"
                class="form-control"
                id="position"
                placeholder="輸入職稱"
                [formControlName]="'position'"
            >
        </div>
        <div class="form-group">
            <label>開工日</label>
            <div class="input-group">
                <input class="form-control" placeholder="yyyy-mm-dd"
                    ngbDatepicker #d="ngbDatepicker" [formControlName]="'startDate'">
                <div class="input-group-append">
                  <button class="btn btn-outline-secondary" (click)="d.toggle()" type="button">+</button>
                </div>
            </div>
        </div>
        <div class="text-right">
            <button type="submit" class="btn btn-primary" [disabled]="experienceForm.invalid || isPending">新增</button>
        </div>
    </form>
</div>
<div class="my-3" *ngIf="isConfirmed">
    <div class="alert alert-success alert-dismissible fade show" role="alert">
        <strong>編輯完成</strong>
        <button type="button" class="close" (click)="resetConfirmState()">
            <span aria-hidden="true">×</span>
        </button>
    </div>
</div>
<div class="my-3" *ngIf="isError">
    <div class="alert alert-danger alert-dismissible fade show" role="alert">
        <strong>發生錯誤</strong> {{ errorMessage }}
        <button type="button" class="close" (click)="resetErrorState()">
          <span aria-hidden="true">×</span>
        </button>
    </div>
</div>

設置離職日之元件

透過 AngularCLI 產生元件:

ng g c modules/main/company/components/date/company-experience-end --flat

設計company-experience-end.component.ts

import { Component, Injector } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { take } from 'rxjs/operators';
import { ComponentBase } from 'src/app/base/component.base';

@Component({
  selector: 'app-company-experience-end',
  templateUrl: './company-experience-end.component.html',
  styleUrls: ['./company-experience-end.component.scss']
})
export class CompanyExperienceEndComponent extends ComponentBase {
    public endDateForm: FormGroup;

    constructor(
        private injector: Injector,
        private formBuilder: FormBuilder
    ) {
        super(injector);
        this.endDateForm = this.formBuilder.group({
            contract: ['', [Validators.required, this.addressValidator]],
            endDate: ['', [Validators.required]]
        });
    }

    public setEndDate(data: any): void {
        data.endDate = new Date(data.endDate.year, data.endDate.month - 1, data.endDate.day).valueOf();
        this.isPending = true;
        this.setFormDisabled(this.endDateForm);
        const resume = this.providerSvc.getResume(data.contract);
        this.providerSvc.executeMethod(
            resume.methods.setJobEndDate(data.endDate)
            .send({ from: this.providerSvc.defaultAccount })
        ).pipe(
            take(1)
        ).subscribe(
            receipt => {
                this.transactionConfirmed();
                this.endDateForm.reset();
                this.setFormDisabled(this.endDateForm, false);
            },
            err => {
                this.transactionError();
                this.endDateForm.reset();
                this.setFormDisabled(this.endDateForm, false);
            }
        );
    }

}

設計頁面:

<div class="my-3" *ngIf="!isConfirmed && !isError">
    <form
        [formGroup]="endDateForm"
        (ngSubmit)="setEndDate(endDateForm.value)"
    >
        <div class="form-group">
            <label for="contract">履歷位址</label>
            <input
                type="text"
                class="form-control"
                id="contract"
                placeholder="輸入履歷位址"
                [formControlName]="'contract'"
            >
        </div>
        <div class="form-group">
            <label>離職日</label>
            <div class="input-group">
                <input class="form-control" placeholder="yyyy-mm-dd"
                    ngbDatepicker #d="ngbDatepicker" [formControlName]="'endDate'">
                <div class="input-group-append">
                  <button class="btn btn-outline-secondary" (click)="d.toggle()" type="button">+</button>
                </div>
            </div>
        </div>
        <div class="text-right">
            <button type="submit" class="btn btn-primary" [disabled]="endDateForm.invalid || isPending">確定</button>
        </div>
    </form>
</div>
<div class="my-3" *ngIf="isConfirmed">
    <div class="alert alert-success alert-dismissible fade show" role="alert">
        <strong>編輯完成</strong>
        <button type="button" class="close" (click)="resetConfirmState()">
            <span aria-hidden="true">×</span>
        </button>
    </div>
</div>
<div class="my-3" *ngIf="isError">
    <div class="alert alert-danger alert-dismissible fade show" role="alert">
        <strong>發生錯誤</strong> {{ errorMessage }}
        <button type="button" class="close" (click)="resetErrorState()">
          <span aria-hidden="true">×</span>
        </button>
    </div>
</div>

添加到Company元件

修改 company.componemnt.ts

<div class="container my-3">
  <div class="jumbotron jumbotron-fluid">
      <div class="container">
          <h1 class="display-4">企業單位專區</h1>
          <p class="lead">提供企業單位編輯職位與工作期間</p>
      </div>
  </div>
</div>
<div class="container">
  <ngb-tabset>
      <ngb-tab title="設置職位">
          <ng-template ngbTabContent>
              <app-company-experience-add></app-company-experience-add>
          </ng-template>
      </ngb-tab>
      <ngb-tab title="設置離職日">
          <ng-template ngbTabContent>
              <app-company-experience-end></app-company-experience-end>
          </ng-template>
      </ngb-tab>
  </ngb-tabset>
</div>

成果展示

設置工作經歷之頁面:
https://ithelp.ithome.com.tw/upload/images/20190925/20119338EkF0SDtogA.png
設置離職日之頁面:
https://ithelp.ithome.com.tw/upload/images/20190925/20119338yZF6dKyjdP.png


今日小結

與上一篇做的事情差不多,但經過前一篇的特訓(?),相信各位在這一篇已經十分理解設計的思維。今天的內容完成後,基本上該有的操作都有了,接下來就剩下履歷頁面了!


上一篇
[區塊練起來-智能合約與DApp開發] DAY 28 - 實戰DApp!區塊鏈履歷應用(2)
下一篇
[區塊練起來-智能合約與DApp開發] DAY 30 - 實戰DApp!區塊鏈履歷應用(4)
系列文
區塊練起來-智能合約與DApp開發31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
tytom2003
iT邦新手 5 級 ‧ 2021-03-20 18:40:39

在"設置工作經歷之元件"裏的company-experience-add.component.html的"<button class="btn btn-outline-secondary" (click)="d.toggle()" type="button">+",
請問"(click)="d.toggle()"的d 是什麼?
因為vscode 提示我"Identifier 'd' is not defined. The component declaration, template variable declarations, and element references do not contain such a member",我怎樣解決? Thank you very much.

tytom2003 iT邦新手 5 級 ‧ 2021-03-20 19:08:48 檢舉

原來要在app.module.ts 裏加
import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap';

import:[.., NgbModule],

HAO iT邦研究生 3 級 ‧ 2021-03-22 14:55:00 檢舉

你好,我的作法是把 BootStrap 另外包裝成 SharedModule,當然不這麼做也是可以的,如果需要的話,可以參考前面這篇:
https://ithelp.ithome.com.tw/articles/10224125

我要留言

立即登入留言