用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/tomevault-io/skills-registry --skill momentum-api命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
基于 SOC 职业分类
| name | momentum-api |
| description | Work with Momentum API for data operations in Angular components Use when this capability is needed. |
| metadata | {"author":"donaldmurillo"} |
Guide for using injectMomentumAPI() in Angular components.
Key rules: Always use async/await (never subscribe). Always use instanceof checks for DOM elements (never as type assertions). Always use typed error classes for error handling.
$ARGUMENTS - Operation type: "query", "crud", "typed", or collection nameimport { injectMomentumAPI } from '@momentumcms/admin';
@Component({...})
export class MyComponent {
private readonly api = injectMomentumAPI();
}
Always use async/await with .find() and .findById(). Never use .find$().subscribe() or any Observable pattern.
async loadData(): Promise<void> {
const result = await this.api.collection<Post>('posts').find({ limit: 10 });
this.posts.set(result.docs);
}
Use .findById() to fetch a single document by ID:
async loadPost(id: string): Promise<void> {
const post = await this.api.collection<Post>('posts').findById(id);
this.post.set(post);
}
// Create
const post = await this.api.collection<Post>('posts').create({ title: 'New Post' });
// Read single document
const post = await this.api.collection<Post>('posts').findById('123');
// Update
const updated = await this.api.collection<Post>('posts').update('123', { title: 'Updated' });
// Delete
const result = await this.api.collection<Post>('posts').delete('123');
nx run example-angular:generate-typesimport type { Post, User } from '../types/momentum.generated';
const posts = await this.api.collection<Post>('posts').find();
const users = await this.api.collection<User>('users').find();
Always pass FindOptions to .find() to control queries. All fields are optional:
interface FindOptions {
where?: Record<string, unknown>; // Filter conditions (see examples below)
sort?: string; // Sort field (prefix with - for desc, e.g. '-createdAt')
limit?: number; // Max results (default: 10)
page?: number; // Page number (default: 1)
depth?: number; // Relationship population depth
transfer?: boolean; // TransferState caching (default: true)
}
// Filter by field value
const published = await this.api.collection<Post>('posts').find({
where: { status: { equals: 'published' } },
limit: 20,
sort: '-createdAt',
page: 1,
});
// Paginated query
const page2 = await this.api.collection<Post>('posts').find({
limit: 10,
page: 2,
sort: 'title',
});
// Combined filters
const filtered = await this.api.collection<Post>('posts').find({
where: { category: { equals: categoryId }, _status: { equals: 'published' } },
limit: 50,
sort: '-createdAt',
});
as type assertions// DON'T — causes @typescript-eslint/consistent-type-assertions lint failure
async handleSubmit(event: Event): Promise<void> {
const form = event.target as HTMLFormElement; // LINT ERROR
const input = form.querySelector('input') as HTMLInputElement; // LINT ERROR
}
// DO — use instanceof narrowing
async handleSubmit(event: Event): Promise<void> {
event.preventDefault();
const target = event.target;
if (!(target instanceof HTMLFormElement)) return;
const input = target.querySelector('input');
if (!(input instanceof HTMLInputElement)) return;
const post = await this.api.collection<Post>('posts').create({
title: input.value,
});
this..( [post, ...posts]);
input. = ;
}
// DON'T — Observable subscribe pattern
this.api
.collection<Post>('posts')
.find$({ limit: 10 })
.subscribe((result) => {
this.posts.set(result.docs);
});
// DO — async/await pattern
const result = await this.api.collection<Post>('posts').find({ limit: 10 });
this.posts.set(result.docs);
Important: Error classes live in
@momentumcms/server-core(env:server). Browser components MUST NOT import from server packages. Useerror.namechecks instead.
// DON'T — generic catch with no typed handling
try {
await this.api.collection('posts').create(data);
} catch (e) {
console.error(e);
}
// DON'T — import from @momentumcms/server-core in browser code (env boundary violation)
// import { ValidationError } from '@momentumcms/server-core'; // ❌ server-only
// DO — check error.name for browser-safe error handling
interface MomentumError extends Error {
errors?: Array<{ field: string; message: string }>;
}
try {
await this.api.collection<Post>('posts').create(data);
} catch (error) {
const err = error as MomentumError;
if (err.name === 'ValidationError' && err.errors) {
this.validationErrors.set(err.errors);
} else if (err.name === 'DocumentNotFoundError') {
this..();
} (err. === ) {
..();
}
}
import { Component, signal, ChangeDetectionStrategy } from '@angular/core';
import { injectMomentumAPI } from '@momentumcms/admin';
// Browser-safe error interface (do NOT import from @momentumcms/server-core in browser code)
interface MomentumError extends Error {
errors?: Array<{ field: string; message: string }>;
}
import type { Post } from '../types/momentum.generated';
@Component({
selector: 'app-posts',
template: `
@if (loading()) {
<p>Loading...</p>
} @else if (error()) {
<p>{{ error() }}</p>
} @else {
@for (post of posts(); track post.id) {
<article>
<h2>{{ post.title }}</h2>
<p>{{ post.content }}</p>
<button (click)="deletePost(post.id)">Delete</button>
</article>
}
}
<form (submit)="createPost($event)">
<input #titleInput placeholder="Title" />
<button type="submit">Create Post</button>
</form>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PostsComponent {
private readonly api = injectMomentumAPI();
readonly posts = signal<Post[]>([]);
readonly loading = signal(true);
error = signal< | >();
() {
.();
}
(): <> {
..();
{
result = ..<>().({
: ,
: ,
: { : { : } },
});
..(result.);
} (error) {
err = error ;
(err. === ) {
..();
} {
..();
}
} {
..();
}
}
(: ): <> {
event.();
target = event.;
(!(target )) ;
input = target.();
(!(input )) ;
{
post = ..<>().({
: input.,
});
..( [post, ...posts]);
input. = ;
} (error) {
err = error ;
(err. === && err.) {
.(, err.);
}
}
}
(: ): <> {
..<>().(id);
..( posts.( p. !== id));
}
}
Use error.name checks for browser-safe error handling (do NOT import from @momentumcms/server-core in browser code):
// Browser-safe error interface
interface MomentumError extends Error {
errors?: Array<{ field: string; message: string }>;
}
try {
await this.api.collection<Post>('posts').findById(id);
} catch (error) {
const err = error as MomentumError;
if (err.name === 'DocumentNotFoundError') {
// Document with given ID does not exist
this.notFound.set(true);
} else if (err.name === 'AccessDeniedError') {
// Current user lacks permission
this.accessDenied.set(true);
} else if (err.name === 'ValidationError' && err.errors) {
// err.errors: Array<{ field: string; message: string }>
this.validationErrors.set(err.errors);
} else (err. === ) {
..();
}
}
| Error Class | When Thrown | Useful Properties |
|---|---|---|
ValidationError | Create/update with invalid data | errors: { field, message }[] |
DocumentNotFoundError | findById with non-existent ID | message |
AccessDeniedError | User lacks permission for operation | message |
CollectionNotFoundError | Invalid collection slug | message |
GlobalNotFoundError | Invalid global slug | message |
/api/*Generate types from your collections:
# Generate types
nx run example-angular:generate-types
# Watch mode (auto-regenerate on changes)
nx run example-angular:generate-types --watch
Output file: src/types/momentum.generated.ts
TransferState is enabled by default for all read operations (find, findById, findSignal, findByIdSignal). Data fetched during SSR is automatically cached and reused on browser hydration, eliminating duplicate HTTP calls.
// SSR: Fetches and caches | Browser: Reads from cache (no HTTP)
const posts = await this.api.collection<Post>('posts').find({ limit: 10 });
const post = await this.api.collection<Post>('posts').findById(id);
Use transfer: false to disable TransferState for a specific call:
// Always makes HTTP call on browser (no caching)
const posts = await this.api.collection<Post>('posts').find({
limit: 10,
transfer: false,
});
// Signals also use TransferState by default
readonly posts = this.api.collection<Post>('posts').findSignal({ limit: 10 });
readonly post = this.api.collection<Post>('posts').findByIdSignal(id);
Ensure provideClientHydration() is in your app config:
// app.config.ts
export const appConfig: ApplicationConfig = {
providers: [provideClientHydration()],
};
Converted and distributed by TomeVault — claim your Tome and manage your conversions.