Using Ecology from Angular
Most ESA apps are Angular. The interactive half of this kit is standards-based Web Components, which Angular can consume directly — so component pages show the Angular binding beside the HTML one. This page is the part that does not fit on a component page: which components you can actually use, and what does not work yet.
Only half the kit is an element you can use
This is the first thing to get straight, because the names run against intuition.
Of the 66 components, 35 are Lit web
components that register a real custom element, and 31
are .astro — compiled to HTML at build time, with
no runtime element behind them at all. Writing
<esa-badge> in an Angular template does not render a badge. It
renders nothing, and reports no error.
The presentational ones are the ones you would reach for first:
esa-button, esa-badge, esa-icon, esa-form-field, esa-card are all in that bucket.
So the Angular tab appears only on pages for components that really are elements. Its absence is information: that component is a markup-and-tokens contract to reproduce, not a package to import. The rest of this kit — the tokens — is plain CSS and works in any stack today.
Binding: attributes, properties, events
Add CUSTOM_ELEMENTS_SCHEMA to the component that hosts the element, and
import the element once for its side effect — each one self-registers, so there is
no module to configure and no customElements.define to call.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import '@esa/ecology/esa-select';
@Component({
selector: 'app-region-picker',
standalone: true,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<esa-select label="Region" [options]="regions" (change)="onChange($event)"></esa-select>
`,
})
export class RegionPicker {
regions = [{ value: 'n', label: 'North' }];
onChange(event: Event) {
const { value } = (event as CustomEvent).detail;
}
} Three rules cover almost everything, and each one fails silently when broken:
| Rule | Why it bites |
|---|---|
Read the payload from $event.detail |
These are CustomEvents, including the ones named
change. $event.target.value is empty — no error,
just nothing.
|
| Write event names exactly as documented |
They are lowercase, even when multi-word: pagechange,
tabchange, featureselect. Angular passes the name
straight to addEventListener, so (pageChange) binds
a listener that never fires.
|
Arrays and objects need [property] binding | [options], [items], [data]. Each
component page's API table names the property and says when it has no
attribute at all.
|
One caution about CUSTOM_ELEMENTS_SCHEMA: it silences unknown-element
errors and unknown-attribute errors. A typo in an attribute name compiles
clean and does nothing, so check names against the API table rather than the
compiler.
What does not work yet
You cannot npm install this into a stock Angular CLI build
today. The snippets on the component pages are the interop
contract — element name, attribute versus property, event name and
detail shape — and they are generated from the live specimen beside
them, so they cannot drift from what the component really does. They are not yet a
copy-paste install. Three things are in the way:
-
@esa/ecologypublishes raw TypeScript fromsrc/with no build output and notsconfig, and the Angular compiler does not compile TypeScript insidenode_modules. -
Its relative imports carry
.jsextensions — correct ESM, and awkward for an unbuilt package. -
astrois a hard peer dependency of the package, even for the Lit half that has nothing to do with Astro.
None of this is Angular-specific and none of it is in the components themselves — the elements are standards-only and self-registering. It is a packaging job on the hub: a real build step and a loosened peer dependency. Until then, treat the Angular tab as the specification you are implementing against.
Migrating an existing Angular app
Adopting the design system across an app that already exists is a different job from
using one element, and it has its own tooling: the
migrate-kit plugin (plugins/migrate-kit/) — an audit, a
per-component playbook, a per-screen playbook, and a verify stage, driven by the
app's design spoke.
It takes the opposite approach to this page on purpose. Rather than importing the Lit elements, it has the app build its own permanent Angular component library against the same token and structure contract. That suits a large app with an established component layer; importing the elements suits a new surface or a gradual adoption. Both target the same tokens, so they converge.