本系列之前的文章:
-
第三篇 Part III: Model类
有了上一篇的基础,可以增强这个Angular程序让它能真正访问之前创建的Knowledge Builder API了。
步骤一、添加Module
增加几个Module,分别对于Welcome,Knowledge Item和Question Bank Item。
ng g m pages\Welcome --routing
ng g m pages\KnowledgeItems --routing
ng g m pages\QuestionBankItems --routing
执行完指令后,src文件夹下会多出一个pages的子文件夹。
步骤二、添加Components
现在,在刚刚创建文件夹下添加对应的底层component。
如果执行下面这条指令,它会在src\pages\welcome文件夹下又创建一个welcome的文件夹。
ng g c pages\welcome\Welcome -m pages\welcome\Welcome
所以,修正该指令:
ng g c pages\Welcome -m pages\welcome\Welcome
现在,该WelcomeComponent与WelcomeModule在同一层了。
类似地,可以加入其它两个底层component:
ng g c pages\KnowledgeItems -m pages\knowledge-items
ng g c pages\QuestionBankItems -m pages\question-bank-items
步骤三、创建List和Detail页面
如果把KnowledgeItemsComponent和QuestionBankItemsComponent分别用于List View来展示Knowledge Items和Question Bank Items的话,那么还需要创建单个的Knowledge Item以及Question Bank Item的编辑和查看页面。
创建KnowledgeItemDetailComponent来实现对Knowledge Item的Create,Display和Update:
ng g c pages\knowledge-items\KnowledgeItemDetail -m pages\knowledge-items
类似的,创建QuestionBankItemDetailComponent。
ng g c pages\question-bank-items\QuestionBankItemDetail -m pages\question-bank-items
上述指令会分别创建Detail的文件夹,为了简化import操作,可以在新创建的文件夹下定义index.ts来export定义:
export * from './knowledge-item-detail.component';
步骤四、更新Routing
这里需要更新Module的Routes。
对于Knowledge Items Module,它的routes定义如下:
const routes: Routes = [
{ path: '', component: KnowledgeItemsComponent },
{ path: 'create', component: KnowledgeItemDetailComponent },
{ path: 'display/:id', component: KnowledgeItemDetailComponent },
{ path: 'edit/:id', component: KnowledgeItemDetailComponent },
];
上述定义既定义了List View(默认Component),也定义了Create, Display和Edit的跳转。
类似地,可以定义Question Bank Item的Routes:
const routes: Routes = [
{ path: '', component: QuestionBankItemsComponent },
{ path: 'create', component: QuestionBankItemDetailComponent },
{ path: 'display/:id', component: QuestionBankItemDetailComponent },
{ path: 'edit/:id', component: QuestionBankItemDetailComponent },
];
同时,需要在Root Module的routes定义:
const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: '/welcome' },
{ path: 'welcome', loadChildren: () => import('./pages/welcome/welcome.module').then(m => m.WelcomeModule) },
{
path: 'knowledge-item',
loadChildren: () => import('./pages/knowledge-items/knowledge-items.module').then(m => m.KnowledgeItemsModule),
},
{
path: 'question-bank-item',
loadChildren: () => import('./pages/question-bank-items/question-bank-items.module').then(m => m.QuestionBankItemsModule),
},
];
需要确保Routing Module都已经被import进各自的Module。
步骤五、创建Service来负责前后台通讯
接下来,定义Service来,主要用于前后台通讯。
ng g service services\OData
上述命令将创建一个service文件夹,并在其中生成odataservice.ts的文件。
在真正让service工作之前,添加代码来获取Knowledge Builder API的OData metadata。
@Injectable({
providedIn: 'root'
})
export class ODataService {
apiUrl = `https://localhost:44355/odata/`;
constructor(private http: HttpClient,
) { }
public getMetadata(): Observable<any> {
let headers: HttpHeaders = new HttpHeaders();
headers = headers.append('Content-Type', 'application/json')
.append('Accept', 'application/json');
// let params: HttpParams = new HttpParams();
// params = params.append('$count', 'true');
return this.http.get(`${this.apiUrl}$metadata`, {
headers,
// params,
})
.pipe(map((response: HttpResponse<any>) => {
const rjs: any = response as any;
return rjs;
}),
catchError((error: HttpErrorResponse) => {
return throwError(error.statusText + '; ' + error.error + '; ' + error.message);
}));
}
}
这里的URL取决于API project的配置。
步骤六、调用新建的Service
要使得页面能够调用新建的ODataService,需要将该Service注册到根Module。
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
HttpClientModule,
MaterialModulesModule,
],
providers: [ODataService],
bootstrap: [AppComponent]
})
export class AppModule { }
然后在页面中就可以调用了。当然,基于Dependence Injection的原理,需要在页面的Constructor加入dependence。
试着在Welcome页面中加入调用。
export class WelcomeComponent implements OnInit {
constructor(private odataSrv: ODataService) {
console.log('Entering WelcomeComponent constructor');
}
ngOnInit(): void {
console.log('Entering WelcomeComponent ngOnInit');
this.odataSrv.getMetadata().subscribe({
next: val => {
console.log(val);
},
error: err => {
console.error(err);
},
});
}
}
步骤七、貌似可以联调
到目前为止,Service已经创建,页面都已经创建,并已经调用API了。
然而,执行如下命令,
ng serve -o
页面并未加载成功,Console的log如下:
Access to XMLHttpRequest at 'https://localhost:44355/odata/$metadata' from origin 'http://localhost:4200' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
简单来说,API并没有启用CORS,所以,调用失败。
那么,如果修正API呢?
是为之记。
Alva Chien
2020.07.15