概述與模塊說明
經(jīng)過前面對共享模塊,布局部分的剖析可以看出JHipster的頁面模塊劃分了,剩余的機構(gòu)子模塊,都只是根據(jù)不同的路由,替換掉主頁上<outlet>部分,已完成不同的具體功能。
account目錄包含賬戶管理的功能,有JHipster實現(xiàn)了一套簡單而標準的模型,用戶可以實現(xiàn)(注冊-激活)、(忘記密碼-修改)、密碼修改、用戶信息修改功能。
按照前后端分離的策略,后端僅提供API接口的調(diào)用響應(yīng),用戶界面的展現(xiàn),邏輯都在前端進行。主題上可以分為API服務(wù)調(diào)用,路由控制,組件邏輯與展現(xiàn)3個部分。
在看具體的子功能前,先查看AccountModule的部分。
@NgModule({
//引入共享模塊,并forChild加載accountState路由
imports: [
JhipsterSampleApplicationNg2SharedModule,
RouterModule.forChild(accountState)
],
//聲明內(nèi)部的組件(相對簡單,沒有指令)
declarations: [
ActivateComponent,
RegisterComponent,
PasswordComponent,
PasswordStrengthBarComponent,
PasswordResetInitComponent,
PasswordResetFinishComponent,
SettingsComponent
],
//聲明內(nèi)部的服務(wù),都是與后端通訊的封裝
providers: [
Register,
ActivateService,
PasswordService,
PasswordResetInitService,
PasswordResetFinishService
],
//已分析過,支持‘-‘命名
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class JhipsterSampleApplicationNg2AccountModule {}
路由配置
每個component組件都需占主體部分,因此path各不相同。
activate.route.ts
export const activateRoute: Route = {
//路徑名我active
path: 'activate',
component: ActivateComponent,
data: {
//無需權(quán)限
authorities: [],
pageTitle: 'activate.title'
},
//路由守衛(wèi)為UserRouteAccessService,由于authorities是空數(shù)組,始終是允許的。但是守衛(wèi)會調(diào)用principal.identity()從后端獲取用戶信息。
canActivate: [UserRouteAccessService,]
};
password.route.ts
password-reset-finish.route.ts
password-reset-init.route.ts
settings.route.ts
這幾個路由也類似,password和setting增加需要“'ROLE_USER'”權(quán)限
register.route.ts
export const registerRoute: Route = {
path: 'register',
component: RegisterComponent,
data: {
authorities: [],
pageTitle: 'register.title'
}
};
可以看到registerRoute并沒有聲明路由守衛(wèi),因為要注冊,那么必然沒有用戶信息,也就無需獲取用戶信息了。
服務(wù)調(diào)用
activate.service.ts
@Injectable()
export class ActivateService {
//注入封裝過的http客戶端
constructor(private http: Http) {}
//設(shè)置params,調(diào)用通訊API,返回Observable
get(key: string): Observable<any> {
const params: URLSearchParams = new URLSearchParams();
params.set('key', key);
return this.http.get(SERVER_API_URL + 'api/activate', {
search: params
}).map((res: Response) => res);
}
}
password.service.ts
password-reset-finish.service.ts
password-reset-init.service.ts
register.service.ts.ts
代碼也類似,十分簡單。值得注意的是setting并沒單獨的service。用戶信息保存是AccountService的一部分,直接調(diào)用即可。
功能組件
組件都使用模板表單的構(gòu)造方式,通過在html中定義ngModel進行數(shù)據(jù)綁定,通過定義內(nèi)部狀態(tài),控制表單的提示信息。以下僅分析register組件源碼
register.component.ts
export class RegisterComponent implements OnInit, AfterViewInit {
confirmPassword: string;
doNotMatch: string;
error: string;
errorEmailExists: string;
errorUserExists: string;
registerAccount: any;
success: boolean;
modalRef: NgbModalRef;
//注入語言服務(wù),登錄框,注冊服務(wù),以及標簽本身,繪制器
constructor(
private languageService: JhiLanguageService,
private loginModalService: LoginModalService,
private registerService: Register,
private elementRef: ElementRef,
private renderer: Renderer
) {
}
ngOnInit() {
this.success = false;
this.registerAccount = {};
}
//默認激活login按鈕
ngAfterViewInit() {
this.renderer.invokeElementMethod(this.elementRef.nativeElement.querySelector('#login'), 'focus', []);
}
register() {
//并非實時判斷兩個input的匹配程度,提交的時候才會判斷,如果不匹配,就提示錯誤
if (this.registerAccount.password !== this.confirmPassword) {
this.doNotMatch = 'ERROR';
} else {
//兩個密碼框相符,則進行注冊接口調(diào)用。
this.doNotMatch = null;
this.error = null;
this.errorUserExists = null;
this.errorEmailExists = null;
this.languageService.getCurrent().then((key) => {
this.registerAccount.langKey = key;
this.registerService.save(this.registerAccount).subscribe(() => {
this.success = true;
}, (response) => this.processError(response));
});
}
}
//通用的登錄框顯示
openLogin() {
this.modalRef = this.loginModalService.open();
}
//錯誤的response處理,出錯時候返回type表示不同錯誤
private processError(response) {
this.success = null;
if (response.status === 400 && response.json().type === LOGIN_ALREADY_USED_TYPE) {
this.errorUserExists = 'ERROR';
} else if (response.status === 400 && response.json().type === EMAIL_ALREADY_USED_TYPE) {
this.errorEmailExists = 'ERROR';
} else {
this.error = 'ERROR';
}
}
}
register.component.html
<div>
<div class="row justify-content-center">
<div class="col-md-8">
<h1 jhiTranslate="register.title">Registration</h1>
<!-- 成功提示,僅當注冊成功時顯示 -->
<div class="alert alert-success" *ngIf="success" jhiTranslate="register.messages.success">
<strong>Registration saved!</strong> Please check your email for confirmation.
</div>
<!-- 失敗提示,僅當注冊失敗時顯示 -->
<div class="alert alert-danger" *ngIf="error" jhiTranslate="register.messages.error.fail">
<strong>Registration failed!</strong> Please try again later.
</div>
<!-- 失敗提示,僅當注冊失敗,且服務(wù)器返回用戶已存在時顯示 -->
<div class="alert alert-danger" *ngIf="errorUserExists" jhiTranslate="register.messages.error.userexists">
<strong>Login name already registered!</strong> Please choose another one.
</div>
<!-- 失敗提示,僅當注冊失敗,且服務(wù)器返回注冊Email已存在時顯示 -->
<div class="alert alert-danger" *ngIf="errorEmailExists" jhiTranslate="register.messages.error.emailexists">
<strong>Email is already in use!</strong> Please choose another one.
</div>
<!-- 失敗提示,僅當兩個密碼輸入框不符時顯示 -->
<div class="alert alert-danger" *ngIf="doNotMatch" jhiTranslate="global.messages.error.dontmatch">
The password and its confirmation do not match!
</div>
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-8">
<!-- 窗體form定義,提交方法為register,如果注冊成功,就不顯示了 -->
<form name="form" role="form" (ngSubmit)="register()" #registerForm="ngForm" *ngIf="!success">
<div class="form-group">
<label class="form-control-label" for="login" jhiTranslate="global.form.username">Username</label>
<!-- 標準的模板方式生成窗體方式,使用ngModel綁定值到login,后面定義了一系列angular的驗證器 -->
<input type="text" class="form-control" [(ngModel)]="registerAccount.login" id="login" name="login" #login="ngModel" placeholder="{{'global.form.username.placeholder' | translate}}" required minlength="1" maxlength="50" pattern="^[_'.@A-Za-z0-9-]*$">
<!-- 統(tǒng)一的錯誤提示div,包含多個small提示,根據(jù)不同的錯誤類型,有不同的提示 -->
<div *ngIf="login.dirty && login.invalid">
<small class="form-text text-danger" *ngIf="login.errors.required" jhiTranslate="register.messages.validate.login.required">
Your username is required.
</small>
<small class="form-text text-danger" *ngIf="login.errors.minlength" jhiTranslate="register.messages.validate.login.minlength">
Your username is required to be at least 1 character.
</small>
<small class="form-text text-danger" *ngIf="login.errors.maxlength" jhiTranslate="register.messages.validate.login.maxlength">
Your username cannot be longer than 50 characters.
</small>
<small class="form-text text-danger" *ngIf="login.errors.pattern" jhiTranslate="register.messages.validate.login.pattern">
Your username can only contain lower-case letters and digits.
</small>
</div>
</div>
<!-- 省略email,密碼,確認密碼框 -->
<!-- 如果窗體不合法,就disable提交按鈕 -->
<button type="submit" [disabled]="registerForm.form.invalid" class="btn btn-primary" jhiTranslate="register.form.button">Register</button>
</form>
<p></p>
<!-- 窗體之外,增加默認用戶的說明 -->
<div class="alert alert-warning">
<span jhiTranslate="global.messages.info.authenticated.prefix">If you want to </span>
<a class="alert-link" (click)="openLogin()" jhiTranslate="global.messages.info.authenticated.link">sign in</a><span jhiTranslate="global.messages.info.authenticated.suffix">, you can try the default accounts:<br/>- Administrator (login="admin" and password="admin") <br/>- User (login="user" and password="user").</span>
</div>
</div>
</div>
</div>