4 | This is a simple demo project showing what a standalone
5 | @Component, @Directive, and @Pipe in
6 | Angular could look like in the future. This demo is based on the design
7 | proposed in the
8 | RFC: standalone components, directives and pipes - making Angular’s
10 | NgModules optional
12 |
15 | Demo implementation notes: The demo is built by wrapping an existing
16 | @Component, @Directive, and
17 | @Pipe decorators and generating a hidden (virtual) NgModule. The
18 | application, including the standalone entities, is then compiled using the JIT
19 | compiler. This is not the intended implementation of the design
20 | described in the RFC - it is a quick prototype enabling early experiments with
21 | the new APIs.
22 |
Simple component with no dependencies
8 | 9 |
10 | @Component({
11 | selector: 'first-standalone-component',
12 | standalone: true,
13 | template: \`
14 | I'm first!
15 | <button (click)="confirm()">click to confirm</button>
16 | {{counter}}
17 | \`
18 | })
19 | export class FirstStandaloneComponent {
20 | counter = 0;
21 |
22 | confirm() {
23 | this.counter++;
24 | console.log('confirmed!');
25 | }
26 | }
27 |
28 |
29 |
30 | 39 | This one imports FormsModule - an existing NgModule - to demonstrate 40 | interop capabilities. 41 |
42 | 43 |
44 | @Component({
45 | selector: 'standalone-with-import-component',
46 | standalone: true,
47 | imports: [FormsModule],
48 | template: \`
49 | Forms work!
50 | <input [(ngModel)]="name" />
51 | (name = {{ name }})
52 | \`
53 | })
54 | export class StandaloneWithImportComponent {
55 | name = 'Daft Punk';
56 | }
57 |
58 |
59 |
68 | This one imports FirstStandaloneComponent to show off
69 | composability.
70 |
73 | @Component({
74 | selector: 'standalone-importing-standalone-component',
75 | standalone: true,
76 | imports: [FirstStandaloneComponent],
77 | template: \`
78 | Turtles all the way down:
79 | <first-standalone-component></first-standalone-component>
80 | \`
81 | })
82 | export class StandaloneImportingStandaloneComponent {}
83 |
84 |
85 | 94 | This one provides a multiprovider which it then uses in the template. 95 |
96 | 97 |
98 | export const locale = new InjectionToken<string[]>('locale');
99 |
100 | @NgModule({
101 | providers: [
102 | { provide: locale, multi: true, useValue: 'en' },
103 | { provide: locale, multi: true, useValue: 'sk' }
104 | ]
105 | })
106 | class MyNgModuleWithProvider {}
107 |
108 | @Component({
109 | selector: 'standalone-with-providers-component',
110 | standalone: true,
111 | imports: [CommonModule, MyNgModuleWithProvider],
112 | template: \`
113 | Supported locales:
114 | <ul>
115 | <li *ngFor="let locale of locales">{{ locale }}</li>
116 | </ul>
117 | \`
118 | })
119 | export class StandaloneWithProvidersComponent {
120 | constructor(@Inject(locale) protected locales) {}
121 | }
122 |
123 |
124 |
125 | 132 | This shows that standalone directives work in the same style as standalone 133 | components. 134 |
135 | 136 |
137 | @Directive({
138 | selector: '[standaloneRedBorder]',
139 | standalone: true,
140 | host: {
141 | style: 'border: 2px dashed red'
142 | }
143 | })
144 | export class StandaloneRedBorderDirective {}
145 |
146 | <first-standalone-component standaloneRedBorder></first-standalone-component>
147 |
148 |
149 | 160 | This shows that standalone pipe work just fine as well. 161 |
162 | 163 |
164 | @Pipe({
165 | name: 'standaloneStar',
166 | standalone: true
167 | })
168 | export class StandaloneStarPipe {
169 | transform(value) {
170 | const stars = new Array(value.length);
171 | return stars.fill('*').join('');
172 | }
173 | }
174 |
175 | {{ 'hello there' | standaloneStarPipe }}
176 |
177 |
178 | 187 | Standalone components can be dynamically loaded. 188 |
189 | 190 |
191 | @Component({
192 | selector: 'dynamically-loading-component',
193 | standalone: true,
194 | template: 'dynamically loaded: '
195 | })
196 | export class DynamicallyLoadingComponent {
197 | constructor(
198 | private vcRef: ViewContainerRef,
199 | ) {}
200 |
201 | ngOnInit() {
202 | import('./dynamicallyLoaded.component').then((m) =>
203 | this.vcRef.createComponent(m.DynamicallyLoadedComponent)
204 | );
205 | }
206 | }
207 |
208 |
209 |
210 | 217 | Standalone components can be bootstrapped! 218 |
219 | 220 |
221 | @Component({
222 | selector: 'bootstrapped-standalone-component',
223 | standalone: true,
224 | template: \`
225 | I'm bootstrapped!
226 | <button (click)="confirm()">click to confirm</button>
227 | {{ counter }}
228 | \`
229 | })
230 | export class BootstrappedStandaloneComponent {
231 | counter = 0;
232 |
233 | confirm() {
234 | this.counter++;
235 | console.log('confirmed!');
236 | }
237 | }
238 |
239 |
240 | <bootstrapped-standalone-component></bootstrapped-standalone-component>
241 |
242 |
243 |
244 | bootstrapComponent(BootstrappedStandaloneComponent).then(() => {
245 | console.log('bootstrapped standalone component!');
246 | });
247 |
248 |
249 |