网站建设的图片尺寸应该是像素,软件开发流程图片,网站接入网方式,Wordpress微博样式以下内容基于Angular 文档中文版的学习
目录
使用Angular CLI 工具创建项目
HTML标签中{{}}插入值,[]绑定属性,()绑定事件,[(ngModel)]双向绑定
绑定属性
类和样式绑定
事件绑定
双向绑定
循环
IF
定义输入属性
定义输出事件
特殊符号
模板引用变量
页面跳转(路由…以下内容基于Angular 文档中文版的学习
目录
使用Angular CLI 工具创建项目
HTML标签中{{}}插入值,[]绑定属性,()绑定事件,[(ngModel)]双向绑定
绑定属性
类和样式绑定
事件绑定
双向绑定
循环
IF
定义输入属性
定义输出事件
特殊符号
模板引用变量
页面跳转(路由) 路由表定义 路由模块占位 跳转 参数接收 自定义标题策略 路由守卫 LocationStrategy 和浏览器的网址样式 自定义路由匹配器 用命名出口outlet显示多重路由
管道处理
HttpClient 使用例 从服务器请求数据 处理请求错误 拦截请求和响应 将元数据传递给拦截器 跟踪和显示请求进度 安全XSRF 防护
异步取值 使用Angular CLI 工具创建项目 npm install -g angular/cli11.2.14 ng new my-app cd my-app ng serve --open ng build 将Angular应用程序编译到输出目录。 ng serve 构建并提供应用程序并在修改文件时重新构建。 ng generate 根据方案生成或修改文件。 ng generate module app-routing --flat --moduleapp 在当前目录生成app-routing模块并注册到AppModule中 ng generate module heroes/heroes --module app --flat --routing 在 heroes 目录下创建一个带路由的 HeroesModule并把它注册到根模块 AppModule 中 ng generate component product-alerts ng generate component customer-dashboard/CustomerDashboard ng generate service cart ng test 在特定项目上运行单体测试。 ng e2e 构建并提供Angular应用程序并运行端到端测试。
HTML标签中{{}}插入值,[]绑定属性,()绑定事件,[(ngModel)]双向绑定
绑定属性 [property] variable property {{variable}} [attr.property] xxx attribute 是由 HTML 定义的。property 是由 DOM (Document Object Model) 定义的。 少量 HTML attribute 和 property 之间有着 1:1 的映射如id。 有些 HTML attribute 没有对应的 property如rowspan。 有些 DOM property 没有对应的 attribute如textContent。 property: [colSpan] , attribute: [attr.colspan] angular不推荐在初始化后更改 attribute 的值会报警告 。
类和样式绑定 绑定到单个 CSS class [class.sale]onSale 当绑定表达式 onSale 为真值时Angular 会添加类当表达式为假值时它会删除类 —— undefined 除外。 绑定到多个 CSS 类 [class]classExpression 表达式可以是以下之一 用空格分隔的类名字符串 my-class-1 my-class-2 my-class-3 以类名作为键名并将真或假表达式作为值的对象 {foo: true, bar: false} 类名的数组 [foo, bar] 对于任何类似对象的表达式比如 object、Array、Map 或 Set必须更改对象的引用Angular 才能更新类列表。在不更改对象引用的情况下只更新其 Attribute 是不会生效的。 绑定到单一样式 nav [style.background-color]expression/nav nav [style.backgroundColor]expression/nav 绑定到多个样式 [style]styleExpression styleExpression 可以是如下格式之一 样式的字符串列表比如 width: 100px; height: 100px; background-color: cornflowerblue; 一个对象其键名是样式名其值是样式值比如 {width: 100px, height: 100px, backgroundColor: cornflowerblue} 注意不支持把数组绑定给 [style] 当把 [style] 绑定到对象表达式时该对象的引用必须改变这样 Angular 才能更新这个类列表。在不改变对象引用的情况下更新其属性值是不会生效的。
事件绑定 button (click)onSave($event)Save/button button typebutton (myClick)clickMessage$event clickableclick with myClick/button 绑定到键盘事件 可以指定要绑定到键盘事件的键值或代码。它们的 key 和 code 字段是浏览器键盘事件对象的原生部分。默认情况下事件绑定假定你要使用键盘事件上的 key 字段。你还可以用 code 字段。 键的组合可以用点.分隔。例如 keydown.enter 将允许你将事件绑定到 enter 键。你还可以用修饰键例如 shift 、 alt 、 control 和 Mac 中的 command 键。 input (keydown.shift.t)onKeydown($event) / 根据操作系统的不同某些组合键可能会创建特殊字符而不是你期望的组合键。 例如当你同时使用 option 和 shift 键时MacOS 会创建特殊字符。如果你绑定到 keydown.shift.alt.t 在 macOS 上该组合会生成 ˇ 而不是 t 它与绑定不匹配也不会触发你的事件处理程序。 要绑定到 macOS 上的 keydown.shift.alt.t 请使用 code 键盘事件字段来获取正确的行为。 input (keydown.code.shiftleft.altleft.keyt)onKeydown($event) /
双向绑定 Angular 的双向绑定语法是方括号和圆括号的组合 [()]。[] 进行属性绑定() 进行事件绑定 为了使双向数据绑定有效Output() 属性的名字必须遵循 inputChange 模式其中 input 是相应 Input() 属性的名字。比如如果 Input() 属性为 size则 Output() 属性必须为 sizeChange。 子 Input() size!: number | string;Output() sizeChange new EventEmitternumber();resize(delta: number) {this.size Math.min(40, Math.max(8, this.size delta));this.sizeChange.emit(this.size);} 父
app-sizer [(size)]fontSizePx/app-sizer 等同于
app-sizer [size]fontSizePx (sizeChange)fontSizePx$event/app-sizer 表单中的双向绑定 因为没有任何原生 HTML 元素遵循了 x 值和 xChange 事件的命名模式所以与表单元素进行双向绑定需要使用 NgModel
循环 div *ngForlet product of products/div
IF p *ngIfproduct.description/p
定义输入属性 子:
Input() product!: Product; 父:
app-product-alerts [product]product/app-product-alerts
定义输出事件 子:
Output() notify new EventEmitter();
button typebutton (click)notify.emit()Notify Me/button 父:
app-product-alerts [product]product (notify)onNotify()/app-product-alerts
特殊符号 ?. 用来检查问号前面的变量是否为null或者undefined时程序不会出错 {{currentHero?.name}} !. 用来检查感叹号后面的变量为null或者undefined时程序不会出错 {{hero!.name}} ?: 可以把某个属性声明为可选的。选参数与默认参数一定要放在必选参数之后声明。 interface Person { name: string; age?: number; } !: 断言属性为non-null,non-undefined开启了strictNullChecks时不会报相应的警告 hero!: Hero $: 声明属性为Observable流对象 heroes$!: ObservableHero[] ?? 空值合并运算符当左侧操作数为 null 或 undefined 时其返回右侧的操作数否则返回左侧的操作数。 const v1 expr1 ?? expr2 !! 强制转化为bool值 let flag1 !!message
模板引用变量 Angular 根据你所声明的变量的位置给模板变量赋值 如果在组件上声明变量该变量就会引用该组件实例。 如果在标准的 HTML 标记上声明变量该变量就会引用该元素。 如果你在 ng-template 元素上声明变量该变量就会引用一个 TemplateRef 实例来代表此模板。 input #phone placeholderphone number /button typebutton (click)callPhone(phone.value)Call/button 指定名称的变量 如果该变量在右侧指定了一个名字比如 #varngModel那么该变量就会引用所在元素上具有这个 exportAs 名字的指令或组件。 form #itemFormngForm (ngSubmit)onSubmit(itemForm)label fornameName/labelinput typetext idname classform-control namename ngModel required /button typesubmitSubmit/button/formdiv [hidden]!itemForm.form.validp{{ submitMessage }}/p/div 模板输入变量 ulng-template ngFor let-hero let-iindex [ngForOf]heroesliHero number {{i}}: {{hero.name}}/ng-template/ul
页面跳转(路由) 路由表定义 路由的顺序很重要因为 Router 在匹配路由时使用“先到先得”策略所以应该在不那么具体的路由前面放置更具体的路由。 首先列出静态路径的路由然后是一个与默认路由匹配的空路径路由。通配符路由是最后一个因为它匹配每一个 URL只有当其它路由都没有匹配时Router 才会选择它。 RouterModule.forRoot([{ path: productList, component: ProductListComponent, title: Product List },{ path: products/:productId, component: ProductDetailsComponent },{ path: cart, component: CartComponent },{ path: first-component, component: FirstComponent,children: [{ path: child-a, component: ChildAComponent },{ path: child-b, component: ChildBComponent } ]},{ path: , redirectTo: /productList, pathMatch: full },{ path: **, component: PageNotFoundComponent },]) 路由模块占位 router-outlet/router-outlet 跳转 链接跳转 a [routerLink][/products, product.id]{{ product.name }}/aa [routerLink][/heroes, { id: heroId, foo: foo }]Crisis Center/a参数对象作为可选参数拼接到URL中变成/heroes;id15;foofooa routerLink/cart routerLinkActiveactivebutton ariaCurrentWhenActivepageCart/a通过添加 routerLinkActive 指令可以通知你的应用把一个特定的 CSS 类应用到当前的活动路由中。 代码跳转 this.router.navigate([/heroes, { id: heroId, foo: foo }]);// 跳转添加信息// ../list?page1nameab#topthis.router.navigate([../list], {relativeTo: this.route, // 指定要用于相对导航的根 URIqueryParams: { page: 1 }, // 将查询参数设置为 URLfragment: top, // 设置 URL 的哈希片段queryParamsHandling: merge, // 如果处理查询参数preserve 保留当前参数 / merge 将新参数与当前参数合并preserveFragment: false, // 当为 true 时为下一个导航保留 URL 片段}); 参数接收 constructor(private route: ActivatedRoute){ }ngOnInit() {// 第一种方式如果不会发生自页面跳转使用第一次的参数const routeParams this.route.snapshot.paramMap;const productIdFromRoute Number(routeParams.get(productId));// 第二种方式会发生自页面跳转组件会被复用参数会发生变化需要使用Observable监视参数变化this.hero$ this.route.paramMap.pipe(switchMap((params: ParamMap) this.service.getHero(params.get(id)!)));// 接收查询参数this.sessionId this.route.queryParamMap.pipe(map(params params.get(session_id) || None));// 接收书签名this.token this.route.fragment.pipe(map(fragment fragment || None));} 自定义标题策略 Injectable({providedIn: root})export class TemplatePageTitleStrategy extends TitleStrategy {constructor(private readonly title: Title) {super();}override updateTitle(routerState: RouterStateSnapshot) {const title this.buildTitle(routerState);if (title ! undefined) {this.title.setTitle(My Application | ${title});}}}NgModule(...providers: [{provide: TitleStrategy, useClass: TemplatePageTitleStrategy},]}) 路由守卫 使用路由守卫来防止用户未经授权就导航到应用的某些部分。守卫返回一个值以控制路由器的行为 true 导航过程会继续 false 导航过程就会终止且用户留在原地。 UrlTree 取消当前导航并开始导航到所返回的 UrlTree Angular 中提供了以下路由守卫 canActivate 需要身份验证 canActivateChild 保护子路由 canDeactivate 处理未保存的更改 canMatch 根据应用程序中的条件控制 Route 匹配 resolve 预先获取组件数据 canLoad 保护对特性模块的未授权加载 在分层路由的每个级别上你都可以设置多个守卫。 路由器会先按照从最深的子路由由下往上检查的顺序来检查 canDeactivate() 守卫。 然后它会按照从上到下的顺序检查 canActivate() 守卫。 如果特性模块是异步加载的在加载它之前还会检查 canLoad() 守卫。 除 canMatch 之外如果任何一个守卫返回 false其它尚未完成的守卫会被取消这样整个导航就被取消了。如果 canMatch 守卫返回 false那么 Router 就会继续处理这些 Routes 的其余部分以查看是否有别的 Route 配置能匹配此 URL。 canActivate示例 export const yourGuard: CanActivateFn (next: ActivatedRouteSnapshot,state: RouterStateSnapshot) {// your logic goes here}{ path: /your-path, component: YourComponent, canActivate: [yourGuard]} 这里是使用的函数也可以是实现了CanActivate接口的类 LocationStrategy 和浏览器的网址样式 路由器通过两种 LocationStrategy 提供者来支持所有这些风格 PathLocationStrategy 默认的 “HTML 5 pushState” 风格 HashLocationStrategy “hash URL”风格 RouterModule.forRoot() 函数把 LocationStrategy 设置成了 PathLocationStrategy使其成为了默认策略。 你还可以在启动过程中改写override它来切换到 HashLocationStrategy 风格。 RouterModule.forRoot(routes, { useHash: true }) // .../#/crisis-center/ 自定义路由匹配器 // 添加匹配器RouterModule.forRoot({matcher: (url) {if (url.length 1 url[0].path.match(/^[\w]$/gm)) {return {consumed: url,posParams: {username: new UrlSegment(url[0].path.slice(1), {})}};}return null;},component: ProfileComponent})// ProfileComponent读取参数username$ this.route.paramMap.pipe(map((params: ParamMap) params.get(username))); // 路由链接a routerLink/Angularmy profile/a 用命名出口outlet显示多重路由 路由表信息填写outlet名称 { path: compose, component: ComposeMessageComponent, outlet: popup }, 跳转链接 a [routerLink][{ outlets: { popup: [compose] } }]Contact/a 路由占位 router-outlet namepopup/router-outlet 跳转后浏览器URL括号内为第二路由 http://…/crisis-center(popup:compose) 清除第二路由的显示内容 this.router.navigate([{ outlets: { popup: null }}]);
管道处理 管道是在模板表达式中使用的简单函数用于接受输入值并返回转换后的值。 管道用于数据转换用法{{xxxx | 管道1:管道参数1 | 管道2:管道参数2}} 例如 {{ product.price | currency }}{{ product.price | currency:CAD:symbol:4.2-2:fr }}{{ dateTime | date:yyyy-MM-dd HH:mm:ss}} 管道操作符要比三目运算符(?:)的优先级高这意味着 a ? b : c | x 会被解析成 a ? b : (c | x) 官方管道 String - String UpperCasePipe LowerCasePipe TitleCasePipe Number - String DecimalPipe PercentPipe CurrencyPipe Object - String JsonPipe DatePipe Tools SlicePipe AsyncPipe Async pipe 负责订阅和变化检测以及在组件被销毁时取消订阅。 定义 shippingCosts!: Observable{ type: string, price: number }[]; 输出 div classshipping-item *ngForlet shipping of shippingCosts | async I18nPluralPipe I18nSelectPipe
HttpClient 使用例 app.module import { HttpClientModule } from angular/common/http;NgModule({imports: [BrowserModule,HttpClientModule, 服务类 import { HttpClient } from angular/common/http;constructor(private http: HttpClient) {}// 取得列表this.http.get{type: string, price: number}[](/assets/shipping.json);httpOptions {headers: new HttpHeaders({ Content-Type: application/json })};// 更新return this.httpClient.put(this.heroesUrl, hero, this.httpOptions).pipe(tap(_ this.log(updated hero id${hero.id})),catchError(this.handleErrorany(updateHero)));// 添加return this.httpClient.postHero(this.heroesUrl, hero, this.httpOptions).pipe(tap((newHero: Hero) this.log(added hero w/ id${newHero.id})),catchError(this.handleErrorHero(addHero)));// 删除return this.httpClient.deleteHero(url, this.httpOptions).pipe(tap(_ this.log(deleted hero id${id})),catchError(this.handleErrorHero(deleteHero)));// 搜索return this.httpClient.getHero[](${this.heroesUrl}/?name${term}).pipe(tap(_ this.log(found heroes matching ${term})),catchError(this.handleErrorHero[](searchHeroes, [])));// 错误处理private handleErrorT(operation operation, result?: T) {return (error: any): ObservableT {// TODO: リモート上のロギング基盤にエラーを送信するconsole.error(error); // かわりにconsoleに出力// TODO: ユーザーへの開示のためにエラーの変換処理を改善するthis.log(${operation} failed: ${error.message});// 空の結果を返して、アプリを持続可能にするreturn of(result as T);};} 从服务器请求数据 使用 HttpClient.get() 方法从服务器获取数据。 该异步方法会发送一个 HTTP 请求并返回一个 Observable它会在收到响应时发出所请求到的数据。 返回的类型取决于你调用时传入的 observe 和 responseType 参数。 get() 方法有两个参数。要获取的端点 URL以及一个可以用来配置请求的选项对象。 options: {headers?: HttpHeaders | {[header: string]: string | string[]},observe?: body | events | response, // 默认bodyparams?: HttpParams|{[param: string]: string | number | boolean | ReadonlyArraystring | number | boolean},reportProgress?: boolean,responseType?: arraybuffer|blob|json|text, // 默认jsonwithCredentials?: boolean,} observe 和 response 选项的类型是字符串的联合类型而不是普通的字符串。 正确client.get(/foo, {responseType: text}) 错误TypeScript 会把 options 的类型推断为 {responseType: string}。 const options { responseType: text, }; client.get(/foo, options) 正确使用 as const可以让 TypeScript 知道确实想使用常量字符串类型 const options { responseType: text as const, }; client.get(/foo, options); 读取完整的响应体 getConfigResponse(): ObservableHttpResponseConfig {return this.http.getConfig(this.configUrl, { observe: response });}this.configService.getConfigResponse().subscribe(resp {// display its headersconst keys resp.headers.keys();this.headers keys.map(key ${key}: ${resp.headers.get(key)});// access the body directly, which is typed as Config.this.config { ...resp.body! };}); 发起 JSONP 请求 在 Angular 中通过在 NgModule 的 imports 中包含 HttpClientJsonpModule 来使用 JSONP。 searchHeroes(term: string): Observable {term term.trim();const heroesURL ${this.heroesURL}?${term};// callback是服务端接收的要包含回调函数名的参数名称return this.http.jsonp(heroesUrl, callback).pipe(catchError(this.handleError(searchHeroes, [])) // then handle the error);}this.searchHeroes(王).subscribe(data {this.data data;}) 处理请求错误 private handleError(error: HttpErrorResponse) {if (error.status 0) {// 出现客户端或网络错误。相应地处理。console.error(An error occurred:, error.error);} else {// 后端返回了一个不成功的响应代码。// 响应主体可能包含出现问题的线索。console.error(Backend returned code ${error.status}, body was: , error.error);}// Return an observable with a user-facing error message.return throwError(() new Error(Something bad happened; please try again later.));}getConfig() {return this.http.getConfig(this.configUrl).pipe(retry(3), // 最多重试3次失败的请求catchError(this.handleError) // 然后处理错误);} 拦截请求和响应 借助拦截机制你可以声明一些拦截器它们可以检查并转换从应用中发给服务器的 HTTP 请求。 这些拦截器还可以在返回应用的途中检查和转换来自服务器的响应。多个拦截器构成了请求/响应处理器的双向链表。 拦截器可以用一种常规的、标准的方式对每一次 HTTP 的请求/响应任务执行从认证到记日志等很多种隐式任务。 Angular 会按你提供拦截器的顺序应用它们。 请求时HttpClient-拦截器1-拦截器2...-拦截器N-HttpBackend-服务器 响应时HttpClient-拦截器1-拦截器2...-拦截器N-HttpBackend-服务器 如果必须修改请求体请执行以下步骤。 复制请求体并在副本中进行修改。 使用 clone() 方法克隆这个请求对象。 用修改过的副本替换被克隆的请求体。 示例
// 设置默认请求头
Injectable()
export class AuthInterceptor implements HttpInterceptor {constructor(private auth: AuthService) {}intercept(req: HttpRequestany, next: HttpHandler) {const authToken this.auth.getAuthorizationToken();// Clone the request and replace the original headers with// cloned headers, updated with the authorization.const authReq req.clone({headers: req.headers.set(Authorization, authToken)});return next.handle(authReq);}
}
// 请求响应日志
Injectable()
export class LoggingInterceptor implements HttpInterceptor {constructor(private messenger: MessageService) {}intercept(req: HttpRequestany, next: HttpHandler) {const started Date.now();let ok: string;// extend server response observable with loggingreturn next.handle(req).pipe(tap({// Succeeds when there is a response; ignore other eventsnext: (event) (ok event instanceof HttpResponse ? succeeded : ),// Operation failed; error is an HttpErrorResponseerror: (error) (ok failed)}),// Log when response observable either completes or errorsfinalize(() {const elapsed Date.now() - started;const msg ${req.method} ${req.urlWithParams} ${ok} in ${elapsed} ms.;this.messenger.add(msg);}));}
}
// 可以在单独文件中引入所有拦截器
export const httpInterceptorProviders [{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },{ provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true },
];
// 加到 AppModule 的 providers array 中
providers: [ httpInterceptorProviders ], 将元数据传递给拦截器 HttpClient 请求包含一个上下文该上下文可以携带有关请求的元数据。 该上下文可供拦截器读取或修改尽管发送请求时它并不会传输到后端服务器。 这允许应用程序或其他拦截器使用配置参数来标记这些请求比如重试请求的次数。 创建上下文令牌 export const RETRY_COUNT new HttpContextToken(() 3); HttpContextToken 创建期间传递的 lambda 函数 () 3 有两个用途 它允许 TypeScript 推断此令牌的类型HttpContextTokennumber。这个请求上下文是类型安全的 —— 从请求上下文中读取令牌将返回适当类型的值。 它会设置令牌的默认值。如果尚未为此令牌设置其他值那么这就是请求上下文返回的值。使用默认值可以避免检查是否已设置了特定值。 在发起请求时设置上下文值 this.httpClient.get(/data/feed, {context: new HttpContext().set(RETRY_COUNT, 5),}).subscribe(results {/* ... */}); 在拦截器中读取上下文值 export class RetryInterceptor implements HttpInterceptor {intercept(req: HttpRequestany, next: HttpHandler): ObservableHttpEventany {const retryCount req.context.get(RETRY_COUNT);return next.handle(req).pipe(// Retry the request a configurable number of times.retry(retryCount),);}} 上下文是可变的Mutable 并且在请求的其他不可变转换过程中仍然存在。这允许拦截器通过此上下文协调来操作。 export const RETRY_COUNT new HttpContextToken(() 3);export const ERROR_COUNT new HttpContextToken(() 0);export class RetryInterceptor implements HttpInterceptor {intercept(req: HttpRequestany, next: HttpHandler): ObservableHttpEventany {const retryCount req.context.get(RETRY_COUNT);return next.handle(req).pipe(tap({// An error has occurred, so increment this requests ERROR_COUNT.error: () req.context.set(ERROR_COUNT, req.context.get(ERROR_COUNT) 1)}),// Retry the request a configurable number of times.retry(retryCount),);}} 跟踪和显示请求进度 要想发出一个带有进度事件的请求你可以创建一个 HttpRequest 实例并把 reportProgress 选项设置为 true 来启用对进度事件的跟踪。 const req new HttpRequest(POST, /upload/file, file, {reportProgress: true}); 每个进度事件都会触发变更检测所以只有当需要在 UI 上报告进度时你才应该开启它们。 当 HttpClient.request() 和 HTTP 方法一起使用时可以用 observe: events 来查看所有事件包括传输的进度。 接下来把这个请求对象传给 HttpClient.request() 方法该方法返回一个 HttpEvents 的 Observable与 拦截器 部分处理过的事件相同。 return this.http.request(req).pipe(map(event this.getEventMessage(event, file)),tap(message this.showProgress(message)),last(), // return last (completed) message to callercatchError(this.handleError(file)));private getEventMessage(event: HttpEventany, file: File) {switch (event.type) {case HttpEventType.Sent:return Uploading file ${file.name} of size ${file.size}.;case HttpEventType.UploadProgress:// Compute and show the % done:const percentDone event.total ? Math.round(100 * event.loaded / event.total) : 0;return File ${file.name} is ${percentDone}% uploaded.;case HttpEventType.Response:return File ${file.name} was completely uploaded!;default:return File ${file.name} surprising upload event: ${event.type}.;}} 安全XSRF 防护 跨站请求伪造 (XSRF 或 CSRF)是一个攻击技术它能让攻击者假冒一个已认证的用户在你的网站上执行未知的操作。 HttpClient 支持一种通用的机制来防范 XSRF 攻击。 当执行 HTTP 请求时一个拦截器会从 cookie 中读取 XSRF 标记默认名字为 XSRF-TOKEN并且把它设置为一个 HTTP 头 X-XSRF-TOKEN由于只有运行在你自己的域名下的代码才能读取这个 cookie因此后端可以确认这个 HTTP 请求真的来自你的客户端应用而不是攻击者。 默认情况下拦截器会在所有的修改型请求中比如 POST 等把这个请求头发送给使用相对 URL 的请求。但不会在 GET/HEAD 请求中发送也不会发送给使用绝对 URL 的请求。 你的服务器需要在页面加载或首个 GET 请求中把一个名叫 XSRF-TOKEN 的标记写入可被 JavaScript 读到的会话 cookie 中。 这个标记必须对每个用户都是唯一的并且必须能被服务器验证因此不能由客户端自己生成标记。把这个标记设置为你的站点认证信息并且加了盐salt的摘要以提升安全性。 为了防止多个 Angular 应用共享同一个域名或子域时出现冲突要给每个应用分配一个唯一的 cookie 名称。 配置自定义 cookie/header 名称 如果你的后端服务中对 XSRF 标记的 cookie 或头使用了不一样的名字就要使用 HttpClientXsrfModule.withOptions() 来覆盖掉默认值。 imports: [HttpClientModule,HttpClientXsrfModule.withOptions({cookieName: My-Xsrf-Cookie,headerName: My-Xsrf-Header,}),],
异步取值 方法返回Observable属性定义为正常 heroes: Hero[] [];this.heroService.getHeroes().subscribe(heroes this.heroes heroes); 方法返回Observable属性定义为Observable输出时使用async管道 shippingCosts!: Observable{ type: string, price: number }[];this.shippingCosts this.cartService.getShippingCosts(); div classshipping-item *ngForlet shipping of shippingCosts | async