[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F2ZE7ZAE0503D7C57DA4PD":3,"skill-same:01M6F2ZE7ZAE0503D7C57DA4PD":65},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":37,"linkOnly":38,"markdownContent":39,"markdownBody":40,"frontMatter":41,"refName":42,"commitSha":43,"skillDirectory":44,"forksCount":45,"repositoryHtmlUrl":46,"repositoryHomepageUrl":47,"ownerHtmlUrl":48,"ownerBlogUrl":6,"ownerDescription":49,"updatedAt":50,"install":51,"resources":64},"01M6F2ZE7ZAE0503D7C57DA4PD","angular-http",null,"Implement HTTP data fetching in Angular v20+ using resource(), httpResource(), and HttpClient. Use for API calls, data loading with signals, request\u002Fresponse handling, and interceptors. Triggers on data fetching, API integration, loading states, error handling, or converting Observable-based HTTP to signal-based patterns.","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fanalogjs\u002Fangular-skills\u002Fangular-http\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36],"http","angular http","angular","data loading","integration","data","error handling","api","use","api integration","patterns","signals","triggers","error","states","loading","with","implement","2026-11-10 10:12:30",false,"---\nname: angular-http\ndescription: Implement HTTP data fetching in Angular v20+ using resource(), httpResource(), and HttpClient. Use for API calls, data loading with signals, request\u002Fresponse handling, and interceptors. Triggers on data fetching, API integration, loading states, error handling, or converting Observable-based HTTP to signal-based patterns.\n---\n\n# Angular HTTP & Data Fetching\n\nFetch data in Angular using signal-based `resource()`, `httpResource()`, and the traditional `HttpClient`.\n\n## httpResource() - Signal-Based HTTP\n\n`httpResource()` wraps HttpClient with signal-based state management:\n\n```typescript\nimport { Component, signal } from '@angular\u002Fcore';\nimport { httpResource } from '@angular\u002Fcommon\u002Fhttp';\n\ninterface User {\n  id: number;\n  name: string;\n  email: string;\n}\n\n@Component({\n  selector: 'app-user-profile',\n  template: `\n    @if (userResource.isLoading()) {\n      \u003Cp>Loading...\u003C\u002Fp>\n    } @else if (userResource.error()) {\n      \u003Cp>Error: {{ userResource.error()?.message }}\u003C\u002Fp>\n      \u003Cbutton (click)=\"userResource.reload()\">Retry\u003C\u002Fbutton>\n    } @else if (userResource.hasValue()) {\n      \u003Ch1>{{ userResource.value().name }}\u003C\u002Fh1>\n      \u003Cp>{{ userResource.value().email }}\u003C\u002Fp>\n    }\n  `,\n})\nexport class UserProfile {\n  userId = signal('123');\n  \n  \u002F\u002F Reactive HTTP resource - refetches when userId changes\n  userResource = httpResource\u003CUser>(() => `\u002Fapi\u002Fusers\u002F${this.userId()}`);\n}\n```\n\n### httpResource Options\n\n```typescript\n\u002F\u002F Simple GET request\nuserResource = httpResource\u003CUser>(() => `\u002Fapi\u002Fusers\u002F${this.userId()}`);\n\n\u002F\u002F With full request options\nuserResource = httpResource\u003CUser>(() => ({\n  url: `\u002Fapi\u002Fusers\u002F${this.userId()}`,\n  method: 'GET',\n  headers: { 'Authorization': `Bearer ${this.token()}` },\n  params: { include: 'profile' },\n}));\n\n\u002F\u002F With default value\nusersResource = httpResource\u003CUser[]>(() => '\u002Fapi\u002Fusers', {\n  defaultValue: [],\n});\n\n\u002F\u002F Skip request when params undefined\nuserResource = httpResource\u003CUser>(() => {\n  const id = this.userId();\n  return id ? `\u002Fapi\u002Fusers\u002F${id}` : undefined;\n});\n```\n\n### Resource State\n\n```typescript\n\u002F\u002F Status signals\nuserResource.value()      \u002F\u002F Current value or undefined\nuserResource.hasValue()   \u002F\u002F Boolean - has resolved value\nuserResource.error()      \u002F\u002F Error or undefined\nuserResource.isLoading()  \u002F\u002F Boolean - currently loading\nuserResource.status()     \u002F\u002F 'idle' | 'loading' | 'reloading' | 'resolved' | 'error' | 'local'\n\n\u002F\u002F Actions\nuserResource.reload()     \u002F\u002F Manually trigger reload\nuserResource.set(value)   \u002F\u002F Set local value\nuserResource.update(fn)   \u002F\u002F Update local value\n```\n\n## resource() - Generic Async Data\n\nFor non-HTTP async operations or custom fetch logic:\n\n```typescript\nimport { resource, signal } from '@angular\u002Fcore';\n\n@Component({...})\nexport class Search {\n  query = signal('');\n  \n  searchResource = resource({\n    \u002F\u002F Reactive params - triggers reload when changed\n    params: () => ({ q: this.query() }),\n    \n    \u002F\u002F Async loader function\n    loader: async ({ params, abortSignal }) => {\n      if (!params.q) return [];\n      \n      const response = await fetch(`\u002Fapi\u002Fsearch?q=${params.q}`, {\n        signal: abortSignal,\n      });\n      return response.json() as Promise\u003CSearchResult[]>;\n    },\n  });\n}\n```\n\n### Resource with Default Value\n\n```typescript\ntodosResource = resource({\n  defaultValue: [] as Todo[],\n  params: () => ({ filter: this.filter() }),\n  loader: async ({ params }) => {\n    const res = await fetch(`\u002Fapi\u002Ftodos?filter=${params.filter}`);\n    return res.json();\n  },\n});\n\n\u002F\u002F value() returns Todo[] (never undefined)\n```\n\n### Conditional Loading\n\n```typescript\nconst userId = signal\u003Cstring | null>(null);\n\nuserResource = resource({\n  params: () => {\n    const id = userId();\n    \u002F\u002F Return undefined to skip loading\n    return id ? { id } : undefined;\n  },\n  loader: async ({ params }) => {\n    return fetch(`\u002Fapi\u002Fusers\u002F${params.id}`).then(r => r.json());\n  },\n});\n\u002F\u002F Status is 'idle' when params returns undefined\n```\n\n## HttpClient - Traditional Approach\n\nFor complex scenarios or when you need Observable operators:\n\n```typescript\nimport { Component, inject } from '@angular\u002Fcore';\nimport { HttpClient } from '@angular\u002Fcommon\u002Fhttp';\nimport { toSignal } from '@angular\u002Fcore\u002Frxjs-interop';\n\n@Component({...})\nexport class Users {\n  private http = inject(HttpClient);\n  \n  \u002F\u002F Convert Observable to Signal\n  users = toSignal(\n    this.http.get\u003CUser[]>('\u002Fapi\u002Fusers'),\n    { initialValue: [] }\n  );\n  \n  \u002F\u002F Or use Observable directly\n  users$ = this.http.get\u003CUser[]>('\u002Fapi\u002Fusers');\n}\n```\n\n### HTTP Methods\n\n```typescript\nprivate http = inject(HttpClient);\n\n\u002F\u002F GET\ngetUser(id: string) {\n  return this.http.get\u003CUser>(`\u002Fapi\u002Fusers\u002F${id}`);\n}\n\n\u002F\u002F POST\ncreateUser(user: CreateUserDto) {\n  return this.http.post\u003CUser>('\u002Fapi\u002Fusers', user);\n}\n\n\u002F\u002F PUT\nupdateUser(id: string, user: UpdateUserDto) {\n  return this.http.put\u003CUser>(`\u002Fapi\u002Fusers\u002F${id}`, user);\n}\n\n\u002F\u002F PATCH\npatchUser(id: string, changes: Partial\u003CUser>) {\n  return this.http.patch\u003CUser>(`\u002Fapi\u002Fusers\u002F${id}`, changes);\n}\n\n\u002F\u002F DELETE\ndeleteUser(id: string) {\n  return this.http.delete\u003Cvoid>(`\u002Fapi\u002Fusers\u002F${id}`);\n}\n```\n\n### Request Options\n\n```typescript\nthis.http.get\u003CUser[]>('\u002Fapi\u002Fusers', {\n  headers: {\n    'Authorization': 'Bearer token',\n    'Content-Type': 'application\u002Fjson',\n  },\n  params: {\n    page: '1',\n    limit: '10',\n    sort: 'name',\n  },\n  observe: 'response', \u002F\u002F Get full HttpResponse\n  responseType: 'json',\n});\n```\n\n## Interceptors\n\n### Functional Interceptor (Recommended)\n\n```typescript\n\u002F\u002F auth.interceptor.ts\nimport { HttpInterceptorFn } from '@angular\u002Fcommon\u002Fhttp';\nimport { inject } from '@angular\u002Fcore';\n\nexport const authInterceptor: HttpInterceptorFn = (req, next) => {\n  const authService = inject(Auth);\n  const token = authService.token();\n  \n  if (token) {\n    req = req.clone({\n      setHeaders: { Authorization: `Bearer ${token}` },\n    });\n  }\n  \n  return next(req);\n};\n\n\u002F\u002F error.interceptor.ts\nexport const errorInterceptor: HttpInterceptorFn = (req, next) => {\n  return next(req).pipe(\n    catchError((error: HttpErrorResponse) => {\n      if (error.status === 401) {\n        inject(Router).navigate(['\u002Flogin']);\n      }\n      return throwError(() => error);\n    })\n  );\n};\n\n\u002F\u002F logging.interceptor.ts\nexport const loggingInterceptor: HttpInterceptorFn = (req, next) => {\n  const started = Date.now();\n  return next(req).pipe(\n    tap({\n      next: () => console.log(`${req.method} ${req.url} - ${Date.now() - started}ms`),\n      error: (err) => console.error(`${req.method} ${req.url} failed`, err),\n    })\n  );\n};\n```\n\n### Register Interceptors\n\n```typescript\n\u002F\u002F app.config.ts\nimport { provideHttpClient, withInterceptors } from '@angular\u002Fcommon\u002Fhttp';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideHttpClient(\n      withInterceptors([\n        authInterceptor,\n        errorInterceptor,\n        loggingInterceptor,\n      ])\n    ),\n  ],\n};\n```\n\n## Error Handling\n\n### With httpResource\n\n```typescript\n@Component({\n  template: `\n    @if (userResource.error(); as error) {\n      \u003Cdiv class=\"error\">\n        \u003Cp>{{ getErrorMessage(error) }}\u003C\u002Fp>\n        \u003Cbutton (click)=\"userResource.reload()\">Retry\u003C\u002Fbutton>\n      \u003C\u002Fdiv>\n    }\n  `,\n})\nexport class UserCmpt {\n  userResource = httpResource\u003CUser>(() => `\u002Fapi\u002Fusers\u002F${this.userId()}`);\n  \n  getErrorMessage(error: unknown): string {\n    if (error instanceof HttpErrorResponse) {\n      return error.error?.message || `Error ${error.status}: ${error.statusText}`;\n    }\n    return 'An unexpected error occurred';\n  }\n}\n```\n\n### With HttpClient\n\n```typescript\nimport { catchError, retry } from 'rxjs';\n\ngetUser(id: string) {\n  return this.http.get\u003CUser>(`\u002Fapi\u002Fusers\u002F${id}`).pipe(\n    retry(2), \u002F\u002F Retry up to 2 times\n    catchError((error: HttpErrorResponse) => {\n      console.error('Error fetching user:', error);\n      return throwError(() => new Error('Failed to load user'));\n    })\n  );\n}\n```\n\n## Loading States Pattern\n\n```typescript\n@Component({\n  template: `\n    @switch (dataResource.status()) {\n      @case ('idle') {\n        \u003Cp>Enter a search term\u003C\u002Fp>\n      }\n      @case ('loading') {\n        \u003Capp-spinner \u002F>\n      }\n      @case ('reloading') {\n        \u003Capp-data [data]=\"dataResource.value()\" \u002F>\n        \u003Capp-spinner size=\"small\" \u002F>\n      }\n      @case ('resolved') {\n        \u003Capp-data [data]=\"dataResource.value()\" \u002F>\n      }\n      @case ('error') {\n        \u003Capp-error \n          [error]=\"dataResource.error()\" \n          (retry)=\"dataResource.reload()\" \n        \u002F>\n      }\n    }\n  `,\n})\nexport class Data {\n  query = signal('');\n  dataResource = httpResource\u003CData[]>(() => \n    this.query() ? `\u002Fapi\u002Fsearch?q=${this.query()}` : undefined\n  );\n}\n```\n\nFor advanced patterns, see [references\u002Fhttp-patterns.md](references\u002Fhttp-patterns.md).\n\n","# Angular HTTP & Data Fetching\n\nFetch data in Angular using signal-based `resource()`, `httpResource()`, and the traditional `HttpClient`.\n\n## httpResource() - Signal-Based HTTP\n\n`httpResource()` wraps HttpClient with signal-based state management:\n\n```typescript\nimport { Component, signal } from '@angular\u002Fcore';\nimport { httpResource } from '@angular\u002Fcommon\u002Fhttp';\n\ninterface User {\n  id: number;\n  name: string;\n  email: string;\n}\n\n@Component({\n  selector: 'app-user-profile',\n  template: `\n    @if (userResource.isLoading()) {\n      \u003Cp>Loading...\u003C\u002Fp>\n    } @else if (userResource.error()) {\n      \u003Cp>Error: {{ userResource.error()?.message }}\u003C\u002Fp>\n      \u003Cbutton (click)=\"userResource.reload()\">Retry\u003C\u002Fbutton>\n    } @else if (userResource.hasValue()) {\n      \u003Ch1>{{ userResource.value().name }}\u003C\u002Fh1>\n      \u003Cp>{{ userResource.value().email }}\u003C\u002Fp>\n    }\n  `,\n})\nexport class UserProfile {\n  userId = signal('123');\n  \n  \u002F\u002F Reactive HTTP resource - refetches when userId changes\n  userResource = httpResource\u003CUser>(() => `\u002Fapi\u002Fusers\u002F${this.userId()}`);\n}\n```\n\n### httpResource Options\n\n```typescript\n\u002F\u002F Simple GET request\nuserResource = httpResource\u003CUser>(() => `\u002Fapi\u002Fusers\u002F${this.userId()}`);\n\n\u002F\u002F With full request options\nuserResource = httpResource\u003CUser>(() => ({\n  url: `\u002Fapi\u002Fusers\u002F${this.userId()}`,\n  method: 'GET',\n  headers: { 'Authorization': `Bearer ${this.token()}` },\n  params: { include: 'profile' },\n}));\n\n\u002F\u002F With default value\nusersResource = httpResource\u003CUser[]>(() => '\u002Fapi\u002Fusers', {\n  defaultValue: [],\n});\n\n\u002F\u002F Skip request when params undefined\nuserResource = httpResource\u003CUser>(() => {\n  const id = this.userId();\n  return id ? `\u002Fapi\u002Fusers\u002F${id}` : undefined;\n});\n```\n\n### Resource State\n\n```typescript\n\u002F\u002F Status signals\nuserResource.value()      \u002F\u002F Current value or undefined\nuserResource.hasValue()   \u002F\u002F Boolean - has resolved value\nuserResource.error()      \u002F\u002F Error or undefined\nuserResource.isLoading()  \u002F\u002F Boolean - currently loading\nuserResource.status()     \u002F\u002F 'idle' | 'loading' | 'reloading' | 'resolved' | 'error' | 'local'\n\n\u002F\u002F Actions\nuserResource.reload()     \u002F\u002F Manually trigger reload\nuserResource.set(value)   \u002F\u002F Set local value\nuserResource.update(fn)   \u002F\u002F Update local value\n```\n\n## resource() - Generic Async Data\n\nFor non-HTTP async operations or custom fetch logic:\n\n```typescript\nimport { resource, signal } from '@angular\u002Fcore';\n\n@Component({...})\nexport class Search {\n  query = signal('');\n  \n  searchResource = resource({\n    \u002F\u002F Reactive params - triggers reload when changed\n    params: () => ({ q: this.query() }),\n    \n    \u002F\u002F Async loader function\n    loader: async ({ params, abortSignal }) => {\n      if (!params.q) return [];\n      \n      const response = await fetch(`\u002Fapi\u002Fsearch?q=${params.q}`, {\n        signal: abortSignal,\n      });\n      return response.json() as Promise\u003CSearchResult[]>;\n    },\n  });\n}\n```\n\n### Resource with Default Value\n\n```typescript\ntodosResource = resource({\n  defaultValue: [] as Todo[],\n  params: () => ({ filter: this.filter() }),\n  loader: async ({ params }) => {\n    const res = await fetch(`\u002Fapi\u002Ftodos?filter=${params.filter}`);\n    return res.json();\n  },\n});\n\n\u002F\u002F value() returns Todo[] (never undefined)\n```\n\n### Conditional Loading\n\n```typescript\nconst userId = signal\u003Cstring | null>(null);\n\nuserResource = resource({\n  params: () => {\n    const id = userId();\n    \u002F\u002F Return undefined to skip loading\n    return id ? { id } : undefined;\n  },\n  loader: async ({ params }) => {\n    return fetch(`\u002Fapi\u002Fusers\u002F${params.id}`).then(r => r.json());\n  },\n});\n\u002F\u002F Status is 'idle' when params returns undefined\n```\n\n## HttpClient - Traditional Approach\n\nFor complex scenarios or when you need Observable operators:\n\n```typescript\nimport { Component, inject } from '@angular\u002Fcore';\nimport { HttpClient } from '@angular\u002Fcommon\u002Fhttp';\nimport { toSignal } from '@angular\u002Fcore\u002Frxjs-interop';\n\n@Component({...})\nexport class Users {\n  private http = inject(HttpClient);\n  \n  \u002F\u002F Convert Observable to Signal\n  users = toSignal(\n    this.http.get\u003CUser[]>('\u002Fapi\u002Fusers'),\n    { initialValue: [] }\n  );\n  \n  \u002F\u002F Or use Observable directly\n  users$ = this.http.get\u003CUser[]>('\u002Fapi\u002Fusers');\n}\n```\n\n### HTTP Methods\n\n```typescript\nprivate http = inject(HttpClient);\n\n\u002F\u002F GET\ngetUser(id: string) {\n  return this.http.get\u003CUser>(`\u002Fapi\u002Fusers\u002F${id}`);\n}\n\n\u002F\u002F POST\ncreateUser(user: CreateUserDto) {\n  return this.http.post\u003CUser>('\u002Fapi\u002Fusers', user);\n}\n\n\u002F\u002F PUT\nupdateUser(id: string, user: UpdateUserDto) {\n  return this.http.put\u003CUser>(`\u002Fapi\u002Fusers\u002F${id}`, user);\n}\n\n\u002F\u002F PATCH\npatchUser(id: string, changes: Partial\u003CUser>) {\n  return this.http.patch\u003CUser>(`\u002Fapi\u002Fusers\u002F${id}`, changes);\n}\n\n\u002F\u002F DELETE\ndeleteUser(id: string) {\n  return this.http.delete\u003Cvoid>(`\u002Fapi\u002Fusers\u002F${id}`);\n}\n```\n\n### Request Options\n\n```typescript\nthis.http.get\u003CUser[]>('\u002Fapi\u002Fusers', {\n  headers: {\n    'Authorization': 'Bearer token',\n    'Content-Type': 'application\u002Fjson',\n  },\n  params: {\n    page: '1',\n    limit: '10',\n    sort: 'name',\n  },\n  observe: 'response', \u002F\u002F Get full HttpResponse\n  responseType: 'json',\n});\n```\n\n## Interceptors\n\n### Functional Interceptor (Recommended)\n\n```typescript\n\u002F\u002F auth.interceptor.ts\nimport { HttpInterceptorFn } from '@angular\u002Fcommon\u002Fhttp';\nimport { inject } from '@angular\u002Fcore';\n\nexport const authInterceptor: HttpInterceptorFn = (req, next) => {\n  const authService = inject(Auth);\n  const token = authService.token();\n  \n  if (token) {\n    req = req.clone({\n      setHeaders: { Authorization: `Bearer ${token}` },\n    });\n  }\n  \n  return next(req);\n};\n\n\u002F\u002F error.interceptor.ts\nexport const errorInterceptor: HttpInterceptorFn = (req, next) => {\n  return next(req).pipe(\n    catchError((error: HttpErrorResponse) => {\n      if (error.status === 401) {\n        inject(Router).navigate(['\u002Flogin']);\n      }\n      return throwError(() => error);\n    })\n  );\n};\n\n\u002F\u002F logging.interceptor.ts\nexport const loggingInterceptor: HttpInterceptorFn = (req, next) => {\n  const started = Date.now();\n  return next(req).pipe(\n    tap({\n      next: () => console.log(`${req.method} ${req.url} - ${Date.now() - started}ms`),\n      error: (err) => console.error(`${req.method} ${req.url} failed`, err),\n    })\n  );\n};\n```\n\n### Register Interceptors\n\n```typescript\n\u002F\u002F app.config.ts\nimport { provideHttpClient, withInterceptors } from '@angular\u002Fcommon\u002Fhttp';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideHttpClient(\n      withInterceptors([\n        authInterceptor,\n        errorInterceptor,\n        loggingInterceptor,\n      ])\n    ),\n  ],\n};\n```\n\n## Error Handling\n\n### With httpResource\n\n```typescript\n@Component({\n  template: `\n    @if (userResource.error(); as error) {\n      \u003Cdiv class=\"error\">\n        \u003Cp>{{ getErrorMessage(error) }}\u003C\u002Fp>\n        \u003Cbutton (click)=\"userResource.reload()\">Retry\u003C\u002Fbutton>\n      \u003C\u002Fdiv>\n    }\n  `,\n})\nexport class UserCmpt {\n  userResource = httpResource\u003CUser>(() => `\u002Fapi\u002Fusers\u002F${this.userId()}`);\n  \n  getErrorMessage(error: unknown): string {\n    if (error instanceof HttpErrorResponse) {\n      return error.error?.message || `Error ${error.status}: ${error.statusText}`;\n    }\n    return 'An unexpected error occurred';\n  }\n}\n```\n\n### With HttpClient\n\n```typescript\nimport { catchError, retry } from 'rxjs';\n\ngetUser(id: string) {\n  return this.http.get\u003CUser>(`\u002Fapi\u002Fusers\u002F${id}`).pipe(\n    retry(2), \u002F\u002F Retry up to 2 times\n    catchError((error: HttpErrorResponse) => {\n      console.error('Error fetching user:', error);\n      return throwError(() => new Error('Failed to load user'));\n    })\n  );\n}\n```\n\n## Loading States Pattern\n\n```typescript\n@Component({\n  template: `\n    @switch (dataResource.status()) {\n      @case ('idle') {\n        \u003Cp>Enter a search term\u003C\u002Fp>\n      }\n      @case ('loading') {\n        \u003Capp-spinner \u002F>\n      }\n      @case ('reloading') {\n        \u003Capp-data [data]=\"dataResource.value()\" \u002F>\n        \u003Capp-spinner size=\"small\" \u002F>\n      }\n      @case ('resolved') {\n        \u003Capp-data [data]=\"dataResource.value()\" \u002F>\n      }\n      @case ('error') {\n        \u003Capp-error \n          [error]=\"dataResource.error()\" \n          (retry)=\"dataResource.reload()\" \n        \u002F>\n      }\n    }\n  `,\n})\nexport class Data {\n  query = signal('');\n  dataResource = httpResource\u003CData[]>(() => \n    this.query() ? `\u002Fapi\u002Fsearch?q=${this.query()}` : undefined\n  );\n}\n```\n\nFor advanced patterns, see [references\u002Fhttp-patterns.md](references\u002Fhttp-patterns.md).\n\n",{"name":5,"description":7},"main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Fanalogjs\u002Fangular-skills\u002Fangular-http",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":52,"prompt":62,"zipAvailable":63},[53,56,59],{"tool":54,"command":55},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill angular-http",{"tool":57,"command":58},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill angular-http",{"tool":60,"command":61},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill angular-http","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fanalogjs\u002Fangular-skills\u002Fangular-http\u002FSKILL.md",true,[],[66,80,90,99,105,112],{"skillNo":67,"name":68,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":69,"categories":70,"keywords":76,"publishedAt":37},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[71],{"categoryNo":72,"categoryName":73,"taxonomyType":74,"taxonomyTypeDesc":75},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[77,78,79],"model","model hierarchy","model hierarchy skill",{"skillNo":81,"name":82,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":83,"categories":84,"keywords":86,"publishedAt":37},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[85],{"categoryNo":72,"categoryName":73,"taxonomyType":74,"taxonomyTypeDesc":75},[87,88,89],"MCP","zoom","zoom mcp",{"skillNo":91,"name":92,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":93,"categories":94,"keywords":95,"publishedAt":37},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[96,97,98,88],"sdk","zoom apps sdk","apps",{"skillNo":100,"name":101,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":102,"categories":103,"keywords":104,"publishedAt":37},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[101],{"skillNo":106,"name":107,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":108,"categories":109,"keywords":111,"publishedAt":37},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[110],{"categoryNo":72,"categoryName":73,"taxonomyType":74,"taxonomyTypeDesc":75},[107],{"skillNo":113,"name":114,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":115,"categories":116,"keywords":117,"publishedAt":37},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[114]]