Angular Guidline
-
simple, long‑term style guide you can apply across list/add/edit.
simple, long‑term style guide you can apply across list/add/edit.
Core Pattern (Recommended for your project)
- Use rxResource() for fetching data.
- Use signal() for form fields and UI state.
- Use computed() for derived state like loading.
- Use effect() only for side effects (snackbar, logging).
- Use lastValueFrom() (or firstValueFrom()) for mutations (add/update/delete) so you can await.
1. List Page Pattern
const listResource = rxResource({
stream: () => api.getContact()
});
const deleting = signal(false);
const loading = computed(() => listResource.isLoading() || deleting());
const showError = effect(() => {
const err = listResource.error();
if (err) snackbar.open(err.message, 'Close', { duration: 3000 });
});
2. Add Page Pattern
const firstName = signal('');
const lastName = signal('');
const phone = signal<number | null>(null);
const email = signal('');
const saving = signal(false);
async function onSave() {
saving.set(true);
await lastValueFrom(api.addContact({ firstName: firstName(), ... }));
saving.set(false);
router.navigate(['/doc/crud']);
}
3) Edit Page Pattern
const id = signal(route.snapshot.paramMap.get('id') ?? '');
const contactResource = rxResource({
params: id,
stream: ({ params }) => api.getContactId(params)
});
// keep form values synced with fetched data but editable
const firstName = linkedSignal(() => contactResource.value()?.firstName ?? '');
const lastName = linkedSignal(() => contactResource.value()?.lastName ?? '');
const phone = linkedSignal(() => contactResource.value()?.phoneNumber ?? '');
const email = linkedSignal(() => contactResource.value()?.email ?? '');
const saving = signal(false);
const loading = computed(() => contactResource.isLoading() || saving());
signal
A piece of reactive state. Read with signal() and write with signal.set(value).
computed
A derived signal that re-calculates automatically when dependencies change. Example: loading = computed(() => resource.isLoading() || saving()).
effect
Runs a side effect when its dependencies change. Example: showError runs when contactsResource.error() changes.
linkedSignal
A signal that syncs from a source (like a resource or input) but stays editable. In your edit form, it starts from fetched contact fields but can be edited by the user.
toSignal
Converts an Observable into a signal. Example pattern: const user = toSignal(api.getUser()). You import it in edit-contact but don’t use it yet.
rxResource
Wraps an Observable stream into a resource with built-in value(), error(), isLoading(), reload(). It’s ideal when you want declarative data fetching that “feels like” signals. Observable
RxJS stream of async values (HTTP, events, etc). They are lazy and don’t do work until subscribed. rxResource and lastValueFrom are both ways to subscribe.
Input form
onchange : run when an input's value changed and then commited