Angular学习笔记(一)
创始人
2025-05-30 14:11:30

以下内容基于Angular 文档中文版的学习

目录

使用Angular CLI 工具创建项目

HTML标签中{{}}插入值,[]绑定属性,()绑定事件,[(ngModel)]双向绑定

绑定属性

类和样式绑定

事件绑定

双向绑定

循环

IF

定义输入属性

定义输出事件

特殊符号

模板引用变量

页面跳转(路由)

  路由表定义

  路由模块占位

  跳转

  参数接收

  自定义标题策略

  路由守卫

  LocationStrategy 和浏览器的网址样式

  自定义路由匹配器

  用命名出口(outlet)显示多重路由

管道处理

HttpClient

  使用例

  从服务器请求数据

  处理请求错误

  拦截请求和响应

  将元数据传递给拦截器

  跟踪和显示请求进度

  安全:XSRF 防护

异步取值


使用Angular CLI 工具创建项目

  npm install -g @angular/cli@11.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 --module=app
       在当前目录生成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 是不会生效的。
  绑定到单一样式
   


    
  绑定到多个样式
    [style]="styleExpression"
      styleExpression 可以是如下格式之一:
        样式的字符串列表,比如 "width: 100px; height: 100px; background-color: cornflowerblue;"
        一个对象,其键名是样式名,其值是样式值,比如 {width: '100px', height: '100px', backgroundColor: 'cornflowerblue'}
      注意,不支持把数组绑定给 [style]
      当把 [style] 绑定到对象表达式时,该对象的引用必须改变,这样 Angular 才能更新这个类列表。在不改变对象引用的情况下更新其属性值是不会生效的。

事件绑定

 
 
  绑定到键盘事件
    可以指定要绑定到键盘事件的键值或代码。它们的 key 和 code 字段是浏览器键盘事件对象的原生部分。默认情况下,事件绑定假定你要使用键盘事件上的 key 字段。你还可以用 code 字段。
    键的组合可以用点(.)分隔。例如, keydown.enter 将允许你将事件绑定到 enter 键。你还可以用修饰键,例如 shift 、 alt 、 control 和 Mac 中的 command 键。
   
    根据操作系统的不同,某些组合键可能会创建特殊字符,而不是你期望的组合键。
    例如,当你同时使用 option 和 shift 键时,MacOS 会创建特殊字符。如果你绑定到 keydown.shift.alt.t ,在 macOS 上,该组合会生成 ˇ 而不是 t ,它与绑定不匹配,也不会触发你的事件处理程序。
    要绑定到 macOS 上的 keydown.shift.alt.t ,请使用 code 键盘事件字段来获取正确的行为。
    

双向绑定

  Angular 的双向绑定语法是方括号和圆括号的组合 [()]。[] 进行属性绑定,() 进行事件绑定
  为了使双向数据绑定有效,@Output() 属性的名字必须遵循 inputChange 模式,其中 input 是相应 @Input() 属性的名字。比如,如果 @Input() 属性为 size,则 @Output() 属性必须为 sizeChange。
  子:

      @Input()  size!: number | string;@Output() sizeChange = new EventEmitter();resize(delta: number) {this.size = Math.min(40, Math.max(8, +this.size + delta));this.sizeChange.emit(this.size);}


  父:

      等同于


  表单中的双向绑定
    因为没有任何原生 HTML 元素遵循了 x 值和 xChange 事件的命名模式,所以与表单元素进行双向绑定需要使用 NgModel

循环

  

IF

  

定义输入属性

  子:

@Input() product!: Product;

  父:

定义输出事件

  子:

@Output() notify = new EventEmitter();

  父:

特殊符号

  ?. 用来检查问号前面的变量是否为null或者undefined时,程序不会出错;
     {{currentHero?.name}}
  !. 用来检查感叹号后面的变量为null或者undefined时,程序不会出错
     {{hero!.name}}
  ?: 可以把某个属性声明为可选的。选参数与默认参数一定要放在必选参数之后声明。
     interface Person {
       name: string;
       age?: number;
     }
  !: 断言属性为non-null,non-undefined,开启了strictNullChecks时不会报相应的警告
     hero!: Hero
  $: 声明属性为Observable流对象
     heroes$!: Observable
  ?? 空值合并运算符,当左侧操作数为 null 或 undefined 时,其返回右侧的操作数,否则返回左侧的操作数。
     const v1 = expr1 ?? expr2
  !! 强制转化为bool值
     let flag1 = !!message

模板引用变量

  Angular 根据你所声明的变量的位置给模板变量赋值:
    如果在组件上声明变量,该变量就会引用该组件实例。
    如果在标准的 HTML 标记上声明变量,该变量就会引用该元素。
    如果你在 元素上声明变量,该变量就会引用一个 TemplateRef 实例来代表此模板。

    

  指定名称的变量
    如果该变量在右侧指定了一个名字,比如 #var="ngModel",那么该变量就会引用所在元素上具有这个 exportAs 名字的指令或组件。

    

{{ submitMessage }}


  模板输入变量
 

    
  • Hero number {{i}}: {{hero.name}}

页面跳转(路由)

  路由表定义

    路由的顺序很重要,因为 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 },])

  路由模块占位

    

  跳转

    链接跳转

    {{ product.name }}Crisis Center参数对象作为可选参数拼接到URL中,变成/heroes;id=15;foo=fooCart通过添加 routerLinkActive 指令,可以通知你的应用把一个特定的 CSS 类应用到当前的活动路由中。

    代码跳转

	this.router.navigate(['/heroes', { id: heroId, foo: 'foo' }]);// 跳转添加信息// ../list?page=1&name=ab#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')));
    // 路由链接my profile

  用命名出口(outlet)显示多重路由

    路由表信息填写outlet名称

      { path: 'compose', component: ComposeMessageComponent, outlet: 'popup' },

    跳转链接

      Contact

    路由占位

      

    跳转后浏览器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 }[]>;
        输出:
          


      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.handleError('updateHero')));// 添加return this.httpClient.post(this.heroesUrl, hero, this.httpOptions).pipe(tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)),catchError(this.handleError('addHero')));// 删除return this.httpClient.delete(url, this.httpOptions).pipe(tap(_ => this.log(`deleted hero id=${id}`)),catchError(this.handleError('deleteHero')));// 搜索return this.httpClient.get(`${this.heroesUrl}/?name=${term}`).pipe(tap(_ => this.log(`found heroes matching "${term}"`)),catchError(this.handleError('searchHeroes', [])));// 错误处理private handleError(operation = 'operation', result?: T) {return (error: any): Observable => {// 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 | ReadonlyArray},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(): Observable> {return this.http.get(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.get(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: HttpRequest, 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: HttpRequest, 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 推断此令牌的类型:HttpContextToken。这个请求上下文是类型安全的 —— 从请求上下文中读取令牌将返回适当类型的值。
          它会设置令牌的默认值。如果尚未为此令牌设置其他值,那么这就是请求上下文返回的值。使用默认值可以避免检查是否已设置了特定值。


    在发起请求时设置上下文值

      this.httpClient.get('/data/feed', {context: new HttpContext().set(RETRY_COUNT, 5),}).subscribe(results => {/* ... */});

    在拦截器中读取上下文值

      export class RetryInterceptor implements HttpInterceptor {intercept(req: HttpRequest, next: HttpHandler): Observable> {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: HttpRequest, next: HttpHandler): Observable> {const retryCount = req.context.get(RETRY_COUNT);return next.handle(req).pipe(tap({// An error has occurred, so increment this request's 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: HttpEvent, 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();
	

相关内容

热门资讯

【实验报告】实验一 图像的... 实验目的熟悉Matlab图像运算的基础——矩阵运算;熟悉图像矩阵的显示方法࿰...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
大模型落地比趋势更重要,NLP... 全球很多人都开始相信,以ChatGPT为代表的大模型,将带来一场NLP领...
Linux学习之端口、网络协议... 端口:设备与外界通讯交流的出口 网络协议:   网络协议是指计算机通信网...
kuernetes 资源对象分... 文章目录1. pod 状态1.1 容器启动错误类型1.2 ImagePullBackOff 错误1....
STM32实战项目-数码管 程序实现功能: 1、上电后,数码管间隔50ms计数; 2、...
TM1638和TM1639差异... TM1638和TM1639差异说明 ✨本文不涉及具体的单片机代码驱动内容,值针对芯...
Qt+MySql开发笔记:Qt... 若该文为原创文章,转载请注明原文出处 本文章博客地址:https://h...
Java内存模型中的happe... 第29讲 | Java内存模型中的happen-before是什么? Java 语言...
《扬帆优配》算力概念股大爆发,... 3月22日,9股封单金额超亿元,工业富联、鸿博股份、鹏鼎控股分别为3.0...
CF1763D Valid B... CF1763D Valid Bitonic Permutations 题目大意 拱形排列࿰...
SQL语法 DDL、DML、D... 文章目录1 SQL通用语法2 SQL分类3 DDL 数据定义语言3.1 数据库操作3.2 表操作3....
文心一言 VS ChatGPT... 3月16号,百度正式发布了『文心一言』,这是国内公司第一次发布类Chat...
CentOS8提高篇5:磁盘分...        首先需要在虚拟机中模拟添加一块新的硬盘设备,然后进行分区、格式化、挂载等...
Linux防火墙——SNAT、... 目录 NAT 一、SNAT策略及作用 1、概述 SNAT应用环境 SNAT原理 SNAT转换前提条...
部署+使用集群的算力跑CPU密... 我先在开头做一个总结,表达我最终要做的事情和最终环境是如何的,然后我会一...
Uploadifive 批量文... Uploadifive 批量文件上传_uploadifive 多个上传按钮_asing1elife的...
C++入门语法基础 文章目录:1. 什么是C++2. 命名空间2.1 域的概念2.2 命名...
2023年全国DAMA-CDG... DAMA认证为数据管理专业人士提供职业目标晋升规划,彰显了职业发展里程碑及发展阶梯定义...
php实现助记词转TRX,ET... TRX助记词转地址网上都是Java,js或其他语言开发的示例,一个简单的...
【分割数据集操作集锦】毕设记录 1. 按要求将CSV文件转成json文件 有时候一些网络模型的源码会有data.json这样的文件里...
Postman接口测试之断言 如果你看文字部分还是不太理解的话,可以看看这个视频,详细介绍postma...
前端学习第三阶段-第4章 jQ... 4-1 jQuery介绍及常用API导读 01-jQuery入门导读 02-JavaScri...
4、linux初级——Linu... 目录 一、用CRT连接开发板 1、安装CRT调试工具 2、连接开发板 3、开机后ctrl+c...
Urban Radiance ... Urban Radiance Fields:城市辐射场 摘要:这项工作的目标是根据扫描...
天干地支(Java) 题目描述 古代中国使用天干地支来记录当前的年份。 天干一共有十个,分别为:...
SpringBoot雪花ID长... Long类型精度丢失 最近项目中使用雪花ID作为主键,雪花ID是19位Long类型数...
对JSP文件的理解 JSP是java程序。(JSP本质还是一个Servlet) JSP是&#...
【03173】2021年4月高... 一、单向填空题1、大量应用软件开发工具,开始于A、20世纪70年代B、20世纪 80年...
LeetCode5.最长回文子... 目录题目链接题目分析解题思路暴力中心向两边拓展搜索 题目链接 链接 题目分析 简单来说࿰...