51 |
52 |
53 |
54 | `
55 | })
56 | export class AppComponent {}
57 | ```
58 |
--------------------------------------------------------------------------------
/snippets/reusing-code-in-template.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Reusing code in template
3 | author: fetis
4 | twitter: fetis26
5 | level: intermediate
6 | tags:
7 | - templates
8 | links:
9 | - https://angular.io/api/common/NgTemplateOutlet
10 | - https://angular.io/guide/structural-directives#the-ng-template
11 | ---
12 |
13 | # Content
14 | While the best way of reusing your code is creating a component, it's also possible to do it in a template.
15 |
16 | To do this you can use `ng-template` along with `*ngTemplateOutlet` directive.
17 |
18 | ```html
19 |
`
51 | })
52 | export class AppComponent {
53 |
54 | formGroup: FormGroup = new FormGroup({
55 | 'fieldOne': new FormControl(null, Validators.required),
56 | 'fieldTwo': new FormControl(null, Validators.required)
57 | });
58 |
59 | onSubmit(): void {
60 | if (this.formGroup.valid) {
61 | // Work on your validated data
62 | } else {
63 | this.markFieldsAsTouched(this.formGroup);
64 | }
65 | }
66 |
67 | markFieldsAsTouched(form: AbstractControl): void {
68 | form.markAsTouched({onlySelf: true});
69 | if (form instanceof FormArray || form instanceof FormGroup) {
70 | Object.values(form.controls).forEach(this.markFieldsAsTouched);
71 | }
72 | }
73 | }
74 | ```
75 |
76 | On 'Submit' trigger function 'markFieldsAsTouched' with your reactive form passed as arg.
77 | It will mark form controls as 'touch' and in case of non-validity control will get error property.
78 |
79 | Based on control error property you can add validation notification via, for example, *ngIf directive in template
80 |
81 | # Bonus
82 |
83 | It's very useful to check out more general method [Accessing all nested form controls](#accessing-all-nested-form-controls) by [Thekiba](https://twitter.com/thekiba_io) to work with controls.
84 |
--------------------------------------------------------------------------------
/snippets/accessing-all-nested-form-controls.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Accessing all nested form controls
3 | level: intermediate
4 | author: thekiba
5 | twitter: thekiba_io
6 | tags:
7 | - reactive forms
8 | - tips
9 | - good to know
10 | links:
11 | - https://angular.io/guide/reactive-forms
12 | ---
13 |
14 | # Content
15 | Sometimes we need to work with every single Control is a form. Here's how it can be done:
16 |
17 | ```typescript
18 | function flattenControls(form: AbstractControl): AbstractControl[] {
19 | let extracted: AbstractControl[] = [ form ];
20 | if (form instanceof FormArray || form instanceof FormGroup) {
21 | const children = Object.values(form.controls).map(flattenControls);
22 | extracted = extracted.concat(...children);
23 | }
24 | return extracted;
25 | }
26 | ```
27 |
28 | For examples use:
29 | ```typescript
30 | // returns all dirty abstract controls
31 | flattenControls(form).filter((control) => control.dirty);
32 |
33 | // mark all controls as touched
34 | flattenControls(form).forEach((control) =>
35 | control.markAsTouched({ onlySelf: true }));
36 | ```
37 |
38 | # file:app.component.ts
39 | ```typescript
40 | import { Component } from '@angular/core';
41 | import { AbstractControl, FormControl, FormGroup, FormArray, FormBuilder } from '@angular/forms';
42 |
43 | @Component({
44 | selector: 'my-app',
45 | template: `
46 |
Count of dirty controls: {{ dirtyControls.length }}
47 |
48 | `
49 | })
50 | export class AppComponent {
51 |
52 | form: AbstractControl;
53 |
54 | get dirtyControls(): AbstractControl[] {
55 | return flattenControls(this.form).filter(control => control.dirty);
56 | }
57 |
58 | constructor(private fb: FormBuilder) {
59 | this.form = fb.group({
60 | a: fb.control(''),
61 | b: fb.array([
62 | fb.control(''),
63 | fb.group({}),
64 | fb.array([])
65 | ])
66 | });
67 | }
68 |
69 | markAsDirty(form: AbstractControl): void {
70 | for (const control of flattenControls(this.form)) {
71 | control.markAsDirty({ onlySelf: true });
72 | }
73 | }
74 | }
75 |
76 | function flattenControls(form: AbstractControl): AbstractControl[] {
77 | let extracted: AbstractControl[] = [ form ];
78 | if (form instanceof FormArray || form instanceof FormGroup) {
79 | const children = Object.values(form.controls).map(flattenControls);
80 | extracted = extracted.concat(...children);
81 | }
82 | return extracted;
83 | }
84 | ```
85 |
86 | # file:app.module.ts
87 | ```typescript
88 | import { BrowserModule } from '@angular/platform-browser';
89 | import { NgModule } from '@angular/core';
90 | import { AppComponent } from './app.component';
91 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
92 | @NgModule({
93 | imports: [BrowserModule, FormsModule, ReactiveFormsModule],
94 | declarations: [AppComponent],
95 | bootstrap: [AppComponent]
96 | })
97 | export class AppModule {}
98 | ```
99 |
--------------------------------------------------------------------------------
/snippets/getting-components-of-different-types-with-viewchild.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Getting components of different types with ViewChild
3 | level: advanced
4 | author: thekiba
5 | twitter: thekiba_io
6 | tags:
7 | - good-to-know
8 | - tips
9 | - components
10 | - dependency-injection
11 | links:
12 | - https://www.youtube.com/watch?v=PRRgo6F0cjs
13 | ---
14 |
15 | # Content
16 | It's possible to use `@ViewChild` (also `@ViewChildren` and `@ContentChild/Children`) to query for components of different types using dependency injection.
17 |
18 | In the example below we can use `@ViewChildren(Base)` to get instances of `Foo` and `Bar`.
19 |
20 | ```typescript
21 | abstract class Base {}
22 |
23 | @Component({
24 | selector: 'foo',
25 | providers: [{ provide: Base, useExisting: Foo }]
26 | })
27 | class Foo extends Base {}
28 |
29 | @Component({
30 | selector: 'bar',
31 | providers: [{ provide: Base, useExisting: Bar }]
32 | })
33 | class Bar extends Base {}
34 |
35 | // Now we can require both types of components using Base.
36 | @Component({ template: `` })
37 | class AppComponent {
38 | @ViewChildren(Base) components: QueryList;
39 | }
40 | ```
41 |
42 | # file:app.component.ts
43 | ```typescript
44 | import { Component, ViewChildren, QueryList, AfterViewInit } from '@angular/core';
45 |
46 | export abstract class Animal {
47 | abstract say();
48 | }
49 |
50 | @Component({
51 | selector: 'fox',
52 | template: `🦊`,
53 | providers: [{ provide: Animal, useExisting: Fox }]
54 | })
55 | export class Fox extends Animal {
56 | say() {
57 | console.log('Joff-tchoff-tchoffo-tchoffo-tchoff!');
58 | }
59 | }
60 |
61 | @Component({
62 | selector: 'rice',
63 | template: `🍚`,
64 | providers: [{ provide: Animal, useExisting: Rice }]
65 | })
66 | export class Rice extends Animal {
67 | say() {
68 | console.log('lol');
69 | }
70 | }
71 |
72 | @Component({
73 | selector: 'dragon',
74 | template: `🐉`,
75 | providers: [{ provide: Animal, useExisting: Dragon }]
76 | })
77 | export class Dragon extends Animal {
78 | say() {
79 | console.log('Wa-pa-pa-pa-pa-pa-pow!');
80 | }
81 | }
82 |
83 | @Component({
84 | selector: 'my-app',
85 | template: `
86 |
87 |
88 |
89 | `
90 | })
91 | export class AppComponent implements AfterViewInit {
92 | @ViewChildren(Animal) animals: QueryList;
93 |
94 | ngAfterViewInit() {
95 | animals.forEach((animal) => animal.say());
96 | }
97 | }
98 | ```
99 |
100 | # file:app.module.ts
101 | ```typescript
102 | import { BrowserModule } from '@angular/platform-browser';
103 | import { NgModule } from '@angular/core';
104 | import { AppComponent, Fox, Rice, Dragon } from './app.component';
105 |
106 | @NgModule({
107 | imports: [BrowserModule],
108 | declarations: [AppComponent, Fox, Rice, Dragon],
109 | bootstrap: [AppComponent]
110 | })
111 | export class AppModule {}
112 | ```
113 |
--------------------------------------------------------------------------------
/snippets/svg.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: SVG
3 | author: kirjs
4 | twitter: kirjs
5 | level: advanced
6 | tags:
7 | - tip
8 | - SVG
9 | ---
10 |
11 | # Content
12 | It is possible to use SVG tags in your Angular component, to create beautiful graphs and visualizations. There are 3 things you need to know:
13 |
14 | 1. When binding an SVG attribute, use `attr`
15 | ```html
16 |
17 | ```
18 |
19 | 2. When creating sub-components, use attribute and not tag selector:
20 | ```html
21 | // Not:
22 |
23 | ```
24 | ```typescript
25 | @Component({selector: '[child-component]' })
26 | ```
27 |
28 | 3. When using SVG tags in sub-components use svg prefix:
29 | ```typescript
30 | @Component({
31 | selector: '[child-component]',
32 | template: ``
33 | })
34 | ```
35 |
36 | # file:app.component.ts
37 | ```typescript
38 | import { Component, Input } from '@angular/core';
39 |
40 | function generateData() {
41 | return Array.from(new Array(10)).map(index => ({
42 | index,
43 | value: Math.round(Math.random() * 80)
44 | }));
45 | }
46 |
47 | @Component({
48 | selector: '[kirjs-ticks]',
49 | template: `
50 |
56 | {{ i }}
57 |
58 | `
59 | })
60 | export class TicksComponent {
61 | @Input() data;
62 | @Input() barWidth = 30;
63 | padding = 10;
64 | barSpace = this.padding + this.barWidth;
65 |
66 | getIndex(i: number) {
67 | return i;
68 | }
69 | }
70 |
71 | @Component({
72 | selector: 'my-app',
73 | template: `
74 |
93 | `
94 | })
95 | export class AppComponent {
96 | barWidth = 30;
97 | padding = 10;
98 | barSpace = this.padding + this.barWidth;
99 | data = generateData();
100 |
101 | constructor() {
102 | window.setInterval(() => {
103 | this.data = generateData();
104 | }, 1000);
105 | }
106 |
107 | getIndex(a, b) {
108 | return a;
109 | }
110 | }
111 |
112 | ```
113 |
114 | # file:app.module.ts
115 | ```typescript
116 | import { BrowserModule } from '@angular/platform-browser';
117 | import { NgModule } from '@angular/core';
118 | import { AppComponent, TicksComponent } from './app.component';
119 |
120 | @NgModule({
121 | imports: [BrowserModule],
122 | declarations: [AppComponent, TicksComponent],
123 | bootstrap: [AppComponent]
124 | })
125 | export class AppModule {}
126 | ```
127 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at 30secondsofcode@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/COLLABORATING.md:
--------------------------------------------------------------------------------
1 | # Collaborator team rules and guidelines for community moderation
2 |
3 | **As a contributor/member of the community, remember that you can always open issues about moderation and problems with how community moderation is done. We are always open to constructive criticism and feedback!**
4 |
5 | ## Responsibilities of a collaborator
6 |
7 | As a member of the team that manages **30 seconds of code**, you have the following responsibilities:
8 |
9 | - **Be part of the conversation in the issue tracker.** That includes (but is not limited to) helping out new members, discussing new features and explaining decisions to people.
10 | - **Review pull requests.** You do not have to read through all of the pull requests and review them, but taking the time each day to review a few can help a great deal.
11 | - **Be civil and polite.** If you are about to lose your temper, take a step back and do something else. We want our interactions with the community to be polite so that more people can join the project and contribute in any way they can. Remember to always thank contributors for their help, even if it's minor changes or changes that did not make it into the project. This way we can reward and encourage people to keep being part of the community.
12 | - **Contribute when you want, moderate when you can.** If you have a lot on your plate outside of this project, it's alright. It's better to take a break for a few days rather than hastily deal with issues and pull requests that might break things.
13 |
14 | ## Guidelines for merging pull requests and making changes to the project
15 |
16 | - **[Usual guidelines](https://github.com/30-seconds/30-seconds-of-code/blob/master/CONTRIBUTING.md) apply.** Make sure to follow them, like everybody else.
17 | - **For a pull request to be considered ready to merge, there should be at least 2 (preferably 3) reviews approving it for merge.** There are, however, certain exceptions:
18 | - **If a pull request only fixes typos**, there is no need to wait for a second reviewer (unless you are not certain these were not typos in the first place).
19 | - **If a pull request only clarifies a snippet's description or enforces the style guide for an existing snippet**, you might be able to merge it without getting a second reviewer to review it, but only if you are certain about it.
20 | - **Make sure pull requests pass the Travis CI build**, otherwise try and find out what's wrong and inform the author of the pull request.
21 | - **Changes to build scripts, guidelines and things that might break the processes we have in place need to be reviewed by [@Chalarangelo](https://github.com/Chalarangelo)** (this is temporary, but we need a baseline to make sure we break as few things as possible in the beginning).
22 | - **After merging a pull request, make sure to check for untagged snippets and tag them appropriately.** Try to keep all snippets tagged, so that the list and website are up to date.
23 | - **If you make changes or additions to existing snippets or if you want to add your own snippets, you will go through the pull request process that everyone else goes.** Exceptions apply similarly to the ones mentioned above about merging pull requests (i.e. typos, description clarification and the way script and build process changes are handled). Pull requests suggested by collaborators should be reviewed by at least two other collaborators to be considered ready to merge.
24 | - **Pull requests that are inactive for over a week should be closed or put on hold.**
25 |
--------------------------------------------------------------------------------
/snippets/router-custom-preloading.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Router Custom Preloading
3 | author: maktarsis
4 | twitter: maktarsis
5 | level: advanced
6 |
7 | tags:
8 | - router
9 | links:
10 | - https://angular.io/api/router/PreloadingStrategy
11 | - https://vsavkin.com/angular-router-preloading-modules-ba3c75e424cb
12 | - https://medium.com/@adrianfaciu/custom-preloading-strategy-for-angular-modules-b3b5c873681a
13 | - https://coryrylan.com/blog/custom-preloading-and-lazy-loading-strategies-with-angular
14 |
15 | ---
16 |
17 | # Content
18 | Angular allows us to control the way module preloading is handled.
19 |
20 | There are 2 strategies provided by **@angular/router**: `PreloadAllModules` and `NoPreloading`. The latter enabled by default, only preloading lazy modules on demand.
21 |
22 | We can override this behavior by providing custom preloading strategy: In the example below we preload all included modules if the connection is good.
23 |
24 | ```typescript
25 | import { Observable, of } from 'rxjs';
26 |
27 | export class CustomPreloading implements PreloadingStrategy {
28 | public preload(route: Route, load: () => Observable): Observable {
29 | return preloadingConnection() ? load() : of(null);
30 | }
31 | }
32 |
33 | const routing: ModuleWithProviders = RouterModule.forRoot(routes, {
34 | preloadingStrategy: CustomPreloading
35 | });
36 | ```
37 | > Note that that the example above would not be very efficient for larger apps, as it'll preload all the modules.
38 |
39 | # file:app.component.ts
40 | ```typescript
41 | import { Component } from '@angular/core';
42 | @Component({
43 | selector: 'my-app',
44 | template: ``
45 | })
46 | export class AppComponent {}
47 | ```
48 |
49 | # file:app.module.ts
50 | ```typescript
51 | import { BrowserModule } from '@angular/platform-browser';
52 | import { NgModule, ModuleWithProviders } from '@angular/core';
53 | import { PreloadingStrategy, Route, Routes, RouterModule } from '@angular/router';
54 | import { Observable, of } from 'rxjs';
55 | import { AppComponent } from './app.component';
56 |
57 | function preloadingConnection(): boolean {
58 | const connection = navigator['connection'];
59 | if (connection) {
60 | const effectiveType = connection.effectiveType || '';
61 | if (connection.saveData || effectiveType.includes('2g')) {
62 | return false;
63 | }
64 | }
65 | return true;
66 | }
67 |
68 | class CustomPreloading implements PreloadingStrategy {
69 | public preload(route: Route, load: () => Observable): Observable {
70 | return preloadingConnection() ? load() : of(null);
71 | }
72 | }
73 |
74 | const routes: Routes = [
75 | {
76 | path: '',
77 | redirectTo: 'items',
78 | pathMatch: 'full'
79 | },
80 | {
81 | path: 'items',
82 | loadChildren: 'app/items/items.module#ItemsModule'
83 | },
84 | {
85 | path: 'item',
86 | loadChildren: 'app/details/details.module#DetailsModule'
87 | }
88 | ];
89 |
90 | const routing: ModuleWithProviders = RouterModule.forRoot(routes, {
91 | preloadingStrategy: CustomPreloading
92 | });
93 |
94 | @NgModule({
95 | imports: [routing],
96 | exports: [RouterModule],
97 | providers: [CustomPreloading]
98 | })
99 | class RoutingModule {}
100 |
101 | @NgModule({
102 | imports: [BrowserModule, RoutingModule],
103 | declarations: [AppComponent],
104 | bootstrap: [AppComponent]
105 | })
106 | export class AppModule {}
107 | ```
108 |
--------------------------------------------------------------------------------
/snippets/hammerjs-gestures.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: hammerjs-gestures
3 | level: beginner
4 | author: MichaelSolati
5 | twitter: MichaelSolati
6 | tags:
7 | - good-to-know
8 | - tips
9 | - components
10 | - gestures
11 | links:
12 | - https://github.com/angular/angular/blob/master/packages/platform-browser/src/dom/events/hammer_gestures.ts
13 | - http://hammerjs.github.io/api/#hammer.manager
14 | - https://angular.io/api/platform-browser/HammerGestureConfig
15 | ---
16 |
17 | # Content
18 |
19 | To act upon swipes, pans, and pinhces as well as the other mobile gestures, you can use `hammerjs` with `HostListener` decorator, or an event binding,
20 |
21 | ```bash
22 | npm install hammerjs
23 | ```
24 |
25 | ```typescript
26 | @HostListener('swiperight')
27 | public swiperight(): void {
28 | // Run code when a user swipes to the right
29 | }
30 | ```
31 |
32 |
33 | # Bonus
34 |
35 | Here are samples on how to use all of the `hammerjs` event bindings, you can use these events with a `HostListener` as well:
36 |
37 | ```HTML
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | ```
79 |
80 | # file:app.component.ts
81 |
82 | ```typescript
83 | // Using a HostListener
84 | import { Component, HostListener } from '@angular/core';
85 |
86 | @Component({
87 | selector: 'my-app',
88 | template: 'Please do not swipe right'
89 | })
90 | export class AppComponent {
91 | @HostListener('swiperight')
92 | public swiperight(): void {
93 | // Run code when a user swipes to the right
94 | alert('STOP SWIPING TO THE RIGHT');
95 | }
96 | }
97 | ```
98 |
99 | # file:main.ts
100 |
101 | ```typescript
102 | import 'https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js';
103 | import { enableProdMode } from '@angular/core';
104 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
105 |
106 | import { AppModule } from './app/app.module';
107 | import { environment } from './environments/environment';
108 |
109 | if (environment.production) {
110 | enableProdMode();
111 | }
112 |
113 | platformBrowserDynamic().bootstrapModule(AppModule)
114 | .catch(err => console.error(err));
115 |
116 | ```
117 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | CC0 1.0 Universal
2 |
3 | Statement of Purpose
4 |
5 | The laws of most jurisdictions throughout the world automatically confer
6 | exclusive Copyright and Related Rights (defined below) upon the creator and
7 | subsequent owner(s) (each and all, an "owner") of an original work of
8 | authorship and/or a database (each, a "Work").
9 |
10 | Certain owners wish to permanently relinquish those rights to a Work for the
11 | purpose of contributing to a commons of creative, cultural and scientific
12 | works ("Commons") that the public can reliably and without fear of later
13 | claims of infringement build upon, modify, incorporate in other works, reuse
14 | and redistribute as freely as possible in any form whatsoever and for any
15 | purposes, including without limitation commercial purposes. These owners may
16 | contribute to the Commons to promote the ideal of a free culture and the
17 | further production of creative, cultural and scientific works, or to gain
18 | reputation or greater distribution for their Work in part through the use and
19 | efforts of others.
20 |
21 | For these and/or other purposes and motivations, and without any expectation
22 | of additional consideration or compensation, the person associating CC0 with a
23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
25 | and publicly distribute the Work under its terms, with knowledge of his or her
26 | Copyright and Related Rights in the Work and the meaning and intended legal
27 | effect of CC0 on those rights.
28 |
29 | 1. Copyright and Related Rights. A Work made available under CC0 may be
30 | protected by copyright and related or neighboring rights ("Copyright and
31 | Related Rights"). Copyright and Related Rights include, but are not limited
32 | to, the following:
33 |
34 | i. the right to reproduce, adapt, distribute, perform, display, communicate,
35 | and translate a Work;
36 |
37 | ii. moral rights retained by the original author(s) and/or performer(s);
38 |
39 | iii. publicity and privacy rights pertaining to a person's image or likeness
40 | depicted in a Work;
41 |
42 | iv. rights protecting against unfair competition in regards to a Work,
43 | subject to the limitations in paragraph 4(a), below;
44 |
45 | v. rights protecting the extraction, dissemination, use and reuse of data in
46 | a Work;
47 |
48 | vi. database rights (such as those arising under Directive 96/9/EC of the
49 | European Parliament and of the Council of 11 March 1996 on the legal
50 | protection of databases, and under any national implementation thereof,
51 | including any amended or successor version of such directive); and
52 |
53 | vii. other similar, equivalent or corresponding rights throughout the world
54 | based on applicable law or treaty, and any national implementations thereof.
55 |
56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of,
57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
59 | and Related Rights and associated claims and causes of action, whether now
60 | known or unknown (including existing as well as future claims and causes of
61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum
62 | duration provided by applicable law or treaty (including future time
63 | extensions), (iii) in any current or future medium and for any number of
64 | copies, and (iv) for any purpose whatsoever, including without limitation
65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
66 | the Waiver for the benefit of each member of the public at large and to the
67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver
68 | shall not be subject to revocation, rescission, cancellation, termination, or
69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work
70 | by the public as contemplated by Affirmer's express Statement of Purpose.
71 |
72 | 3. Public License Fallback. Should any part of the Waiver for any reason be
73 | judged legally invalid or ineffective under applicable law, then the Waiver
74 | shall be preserved to the maximum extent permitted taking into account
75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
76 | is so judged Affirmer hereby grants to each affected person a royalty-free,
77 | non transferable, non sublicensable, non exclusive, irrevocable and
78 | unconditional license to exercise Affirmer's Copyright and Related Rights in
79 | the Work (i) in all territories worldwide, (ii) for the maximum duration
80 | provided by applicable law or treaty (including future time extensions), (iii)
81 | in any current or future medium and for any number of copies, and (iv) for any
82 | purpose whatsoever, including without limitation commercial, advertising or
83 | promotional purposes (the "License"). The License shall be deemed effective as
84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the
85 | License for any reason be judged legally invalid or ineffective under
86 | applicable law, such partial invalidity or ineffectiveness shall not
87 | invalidate the remainder of the License, and in such case Affirmer hereby
88 | affirms that he or she will not (i) exercise any of his or her remaining
89 | Copyright and Related Rights in the Work or (ii) assert any associated claims
90 | and causes of action with respect to the Work, in either case contrary to
91 | Affirmer's express Statement of Purpose.
92 |
93 | 4. Limitations and Disclaimers.
94 |
95 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
96 | surrendered, licensed or otherwise affected by this document.
97 |
98 | b. Affirmer offers the Work as-is and makes no representations or warranties
99 | of any kind concerning the Work, express, implied, statutory or otherwise,
100 | including without limitation warranties of title, merchantability, fitness
101 | for a particular purpose, non infringement, or the absence of latent or
102 | other defects, accuracy, or the present or absence of errors, whether or not
103 | discoverable, all to the greatest extent permissible under applicable law.
104 |
105 | c. Affirmer disclaims responsibility for clearing rights of other persons
106 | that may apply to the Work or any use thereof, including without limitation
107 | any person's Copyright and Related Rights in the Work. Further, Affirmer
108 | disclaims responsibility for obtaining any necessary consents, permissions
109 | or other rights required for any use of the Work.
110 |
111 | d. Affirmer understands and acknowledges that Creative Commons is not a
112 | party to this document and has no duty or obligation with respect to this
113 | CC0 or use of the Work.
114 |
115 | For more information, please see
116 |
117 |
118 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/nycJSorg/30-seconds-of-angular)
2 |
3 |
4 | [](https://travis-ci.com/nycJSorg/30-seconds-of-angular) [](http://makeapullrequest.com)
5 |
6 | > Curated collection of useful Angular snippets that you can understand in 30 seconds or less.
7 |
8 |
9 |
10 | * Use Ctrl + F or command + F to search for a snippet.
11 | * Snippets are written in Angular 8+.
12 |
13 | # 30 Seconds of Angular
14 |
15 | ## Table of contents
16 |
17 | Beginner snippets
18 |
19 | * [Accessing Enums in template](#accessing-enums-in-template)
20 | * [Cheat Sheets and Checklists](#cheat-sheets-and-checklists)
21 | * [Component State Debugging](#component-state-debugging)
22 | * [Default ViewEncapsulation value](#default-viewencapsulation-value)
23 | * [hammerjs-gestures](#hammerjs-gestures)
24 | * [Loader Component](#loader-component)
25 | * [ng-content](#ng-content)
26 | * [ngIf else](#ngif-else)
27 | * [Optional parameters in the middle](#optional-parameters-in-the-middle)
28 | * [Renaming inputs and outputs](#renaming-inputs-and-outputs)
29 | * [Safe Navigation Operator](#safe-navigation-operator)
30 | * [trackBy in for loops](#trackby-in-for-loops)
31 | * [Understanding Microsyntax](#understanding-microsyntax)
32 |
33 | Intermediate snippets
34 |
35 | * [Accessing all nested form controls](#accessing-all-nested-form-controls)
36 | * [Adding keyboard shortcuts to elements](#adding-keyboard-shortcuts-to-elements)
37 | * [Bind to host properties with host binding](#bind-to-host-properties-with-host-binding)
38 | * [Component level providers](#component-level-providers)
39 | * [Global event listeners](#global-event-listeners)
40 | * [Injecting document](#injecting-document)
41 | * [Mark reactive fields as touched](#mark-reactive-fields-as-touched)
42 | * [Observables as outputs](#observables-as-outputs)
43 | * [Passing template as an input](#passing-template-as-an-input)
44 | * [Preseving whitespaces](#preseving-whitespaces)
45 | * [Reusing code in template](#reusing-code-in-template)
46 | * [Reusing existing custom pipes](#reusing-existing-custom-pipes)
47 | * [Style bindings](#style-bindings)
48 | * [Two-way binding any property](#two-way-binding-any-property)
49 | * [Using APP_INITIALIZER to delay app start](#using-app_initializer-to-delay-app-start)
50 | * [Window Location injection](#window-location-injection)
51 |
52 | Advanced snippets
53 |
54 | * [Getting components of different types with ViewChild](#getting-components-of-different-types-with-viewchild)
55 | * [Router Custom Preloading](#router-custom-preloading)
56 | * [SVG](#svg)
57 |
58 |
59 |
60 | ## Beginner snippets
61 |
62 | ### Accessing Enums in template
63 | Enums are great but they are not visible in Angular templates by default.
64 | With this little trick you can make them accessible.
65 |
66 | ```typescript
67 | enum Animals {
68 | DOG,
69 | CAT,
70 | DOLPHIN
71 | }
72 |
73 | @Component({
74 | ...
75 | })
76 | export class AppComponent {
77 | animalsEnum: typeof Animals = Animals;
78 | }
79 | ```
80 |
81 |
82 |
83 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/accessing-enums-in-template) | [⬆ Back to top](#table-of-contents) | tags: [enums](https://30.codelab.fun/tags/enums) [templates](https://30.codelab.fun/tags/templates)
84 |
85 | ### Cheat Sheets and Checklists
86 | Check out [Angular Cheat Sheet](https://angular.io/guide/cheatsheet) or ([alternative version](https://malcoded.com/angular-cheat-sheet)) containing lots of useful information condensed in one place.
87 |
88 | Also [Angular Checklist](https://angular-checklist.io) contains is curated list of common mistakes made when developing Angular applications.
89 |
90 |
91 | #### Links
92 | https://malcoded.com/angular-cheat-sheet/,https://angular.io/guide/cheatsheet,https://angular.io/guide/styleguide
93 |
94 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/cheat-sheets-and-checklists) | [⬆ Back to top](#table-of-contents) | tags: [tip](https://30.codelab.fun/tags/tip) [cheat sheet](https://30.codelab.fun/tags/cheat-sheet)
95 |
96 | ### Component State Debugging
97 | Debug the component state in the browser console by running:
98 | ```typescript
99 | ng.probe($0).componentInstance
100 | ```
101 |
102 | > `$0` - is the DOM node currently selected in dev tools (`$1` for the previous one and so on).
103 |
104 |
105 | Bonus
106 |
107 | With Ivy renderer engine:
108 | ```typescript
109 | ng.getComponent($0)
110 | ```
111 |
112 |
113 | #### Links
114 | https://blog.angularindepth.com/everything-you-need-to-know-about-debugging-angular-applications-d308ed8a51b4
115 |
116 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/component-state-debugging) | [⬆ Back to top](#table-of-contents) | tags: [good-to-know](https://30.codelab.fun/tags/good-to-know) [tips](https://30.codelab.fun/tags/tips)
117 |
118 | ### Default ViewEncapsulation value
119 | If you're using `ViewEncapsulation` value which is different than default, it might be daunting to set the value manually for every component.
120 |
121 | Luckily you can configure it globally when bootstrapping your app:
122 |
123 | ```TypeScript
124 | platformBrowserDynamic().bootstrapModule(AppModule, [
125 | {
126 | // NOTE: Use ViewEncapsulation.None only if you know what you're doing.
127 | defaultEncapsulation: ViewEncapsulation.None
128 | }
129 | ]);
130 | ```
131 |
132 |
133 |
134 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/default-viewencapsulation-value) | [⬆ Back to top](#table-of-contents) | tags: [configuration](https://30.codelab.fun/tags/configuration) [styling](https://30.codelab.fun/tags/styling)
135 |
136 | ### hammerjs-gestures
137 | To act upon swipes, pans, and pinhces as well as the other mobile gestures, you can use `hammerjs` with `HostListener` decorator, or an event binding,
138 |
139 | ```bash
140 | npm install hammerjs
141 | ```
142 |
143 | ```typescript
144 | @HostListener('swiperight')
145 | public swiperight(): void {
146 | // Run code when a user swipes to the right
147 | }
148 | ```
149 |
150 |
151 | Bonus
152 |
153 | Here are samples on how to use all of the `hammerjs` event bindings, you can use these events with a `HostListener` as well:
154 |
155 | ```HTML
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 | ```
197 |
198 |
199 | #### Links
200 | https://github.com/angular/angular/blob/master/packages/platform-browser/src/dom/events/hammer_gestures.ts,http://hammerjs.github.io/api/#hammer.manager,https://angular.io/api/platform-browser/HammerGestureConfig
201 |
202 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/hammerjs-gestures) | [⬆ Back to top](#table-of-contents) | tags: [good-to-know](https://30.codelab.fun/tags/good-to-know) [tips](https://30.codelab.fun/tags/tips) [components](https://30.codelab.fun/tags/components) [gestures](https://30.codelab.fun/tags/gestures)
203 |
204 | ### Loader Component
205 | You can create own helper component and use it instead of `*ngIf`.
206 |
207 | ```typescript
208 | @Component({
209 | selector: 'loader',
210 | template: `
211 |
212 | 🕚 Wait 10 seconds!
213 | `
214 | })
215 | class LoaderComponent {
216 | @Input() loading: boolean;
217 | }
218 | ```
219 |
220 | For usage example:
221 | ```html
222 | 🦊 🦄 🐉
223 | ```
224 |
225 | > Note that the content will be eagerly evaluated, e.g. in the snippet below `destroy-the-world` will be created before the loading even starts:
226 |
227 | ```html
228 |
229 | ```
230 |
231 |
232 | #### Links
233 | https://medium.com/claritydesignsystem/ng-content-the-hidden-docs-96a29d70d11b,https://blog.angularindepth.com/https-medium-com-thomasburleson-animated-ghosts-bfc045a51fba
234 |
235 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/loader-component) | [⬆ Back to top](#table-of-contents) | tags: [tips](https://30.codelab.fun/tags/tips) [good-to-know](https://30.codelab.fun/tags/good-to-know) [components](https://30.codelab.fun/tags/components) [templates](https://30.codelab.fun/tags/templates)
236 |
237 | ### ng-content
238 | With `ng-content` you can pass any elements to a component.
239 | This simplifies creating reusable components.
240 |
241 | ```typescript
242 | @Component({
243 | selector: 'wrapper',
244 | template: `
245 |
270 |
271 | not loading
272 | ```
273 |
274 |
275 |
276 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/ngif-else) | [⬆ Back to top](#table-of-contents) | tags: [ngif](https://30.codelab.fun/tags/ngif) [templates](https://30.codelab.fun/tags/templates)
277 |
278 | ### Optional parameters in the middle
279 | Navigate with matrix params:
280 |
281 | the router will navigate to `/first;name=foo/details`
282 | ```html
283 |
284 | link with params
285 |
286 | ```
287 |
288 |
289 | #### Links
290 | https://stackblitz.com/edit/angular-xvy5pd
291 |
292 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/optional-parameters-in-the-middle) | [⬆ Back to top](#table-of-contents) | tags: [routing](https://30.codelab.fun/tags/routing)
293 |
294 | ### Renaming inputs and outputs
295 | In certain cases `@Input` and `@Output` properties can be named differently than the actual inputs and outputs.
296 |
297 | ```html
298 |
302 |
303 | ```
304 |
305 | ```typescript
306 | @Directive({ selector: '[pagination]'})
307 | class PaginationComponent {
308 | @Input('paginationShowFirst')
309 | showFirst: boolean = true;
310 |
311 | @Output('paginationPageChanged')
312 | pageChanged = new EventEmitter();
313 | }
314 | ```
315 | > Note: Use this wisely, see [StyleGuide recommedation](https://angular.io/guide/styleguide#style-05-13)
316 |
317 |
318 | #### Links
319 | https://angular.io/guide/styleguide#style-05-13
320 |
321 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/renaming-inputs-and-outputs) | [⬆ Back to top](#table-of-contents) | tags: [components](https://30.codelab.fun/tags/components) [templates](https://30.codelab.fun/tags/templates)
322 |
323 | ### Safe Navigation Operator
324 | The [Safe Navigation Operator](https://angular.io/guide/template-syntax#the-safe-navigation-operator----and-null-property-paths) helps with preventing null-reference exceptions in component template expressions. It returns object property value if it exists or null otherwise.
325 |
326 | ```html
327 |
I will work even if student is null or undefined: {{student?.name}}
347 | ### trackBy in for loops
348 | To avoid the expensive operations, we can help Angular to track which items added or removed i.e. customize the default tracking algorithm by providing a trackBy option to NgForOf.
349 |
350 | So you can provide your custom trackBy function that will return unique identifier for each iterated item.
351 | For example, some key value of the item. If this key value matches the previous one, then Angular won't detect changes.
352 |
353 | **trackBy** takes a function that has _index_ and _item_ args.
354 |
355 | ```typescript
356 | @Component({
357 | selector: 'my-app',
358 | template: `
359 |
360 |
{{item.id}}
361 |
362 | `
363 | })
364 | export class AppComponent {
365 | trackByFn(index, item) {
366 | return item.id;
367 | }
368 | }
369 | ```
370 | If trackBy is given, Angular tracks changes by the return value of the function.
371 |
372 | Now when you change the collection, Angular can track which items have been added or removed according to the unique identifier and create/destroy only changed items.
373 |
374 |
375 | #### Links
376 | https://angular.io/api/common/NgForOf,https://angular.io/api/core/TrackByFunction
377 |
378 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/trackby-in-for-loops) | [⬆ Back to top](#table-of-contents) | tags: [good-to-know](https://30.codelab.fun/tags/good-to-know) [tips](https://30.codelab.fun/tags/tips) [components](https://30.codelab.fun/tags/components) [performance](https://30.codelab.fun/tags/performance)
379 |
380 | ### Understanding Microsyntax
381 | Under the hood Angular compiles structural directives into ng-template elements, e.g.:
382 |
383 | ```html
384 |
385 |
386 |
387 |
388 |
389 | ```
390 |
391 | The value passed to *ngFor directive is written using microsyntax. You can learn about it [in the docs](https://angular.io/guide/structural-directives#microsyntax).
392 |
393 | Also check out an [interactive tool](https://alexzuza.github.io/ng-structural-directive-expander/) that shows the expansion by [Alexey Zuev](https://twitter.com/yurzui)
394 |
395 |
396 | #### Links
397 | https://angular.io/guide/structural-directives#microsyntax,https://alexzuza.github.io/ng-structural-directive-expander/,https://angular.io/guide/structural-directives#inside-ngfor
398 |
399 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/understanding-microsyntax) | [⬆ Back to top](#table-of-contents) | tags: [tip](https://30.codelab.fun/tags/tip) [structural directive](https://30.codelab.fun/tags/structural-directive) [microsyntax](https://30.codelab.fun/tags/microsyntax)
400 |
401 |
402 | ## Intermediate snippets
403 |
404 | ### Accessing all nested form controls
405 | Sometimes we need to work with every single Control is a form. Here's how it can be done:
406 |
407 | ```typescript
408 | function flattenControls(form: AbstractControl): AbstractControl[] {
409 | let extracted: AbstractControl[] = [ form ];
410 | if (form instanceof FormArray || form instanceof FormGroup) {
411 | const children = Object.values(form.controls).map(flattenControls);
412 | extracted = extracted.concat(...children);
413 | }
414 | return extracted;
415 | }
416 | ```
417 |
418 | For examples use:
419 | ```typescript
420 | // returns all dirty abstract controls
421 | flattenControls(form).filter((control) => control.dirty);
422 |
423 | // mark all controls as touched
424 | flattenControls(form).forEach((control) =>
425 | control.markAsTouched({ onlySelf: true }));
426 | ```
427 |
428 |
429 | #### Links
430 | https://angular.io/guide/reactive-forms
431 |
432 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/accessing-all-nested-form-controls) | [⬆ Back to top](#table-of-contents) | tags: [reactive forms](https://30.codelab.fun/tags/reactive-forms) [tips](https://30.codelab.fun/tags/tips) [good to know](https://30.codelab.fun/tags/good-to-know)
433 |
466 | ### Bind to host properties with host binding
467 | Every rendered angular component is wrapped in a host element (which is the same as component's selector).
468 |
469 | It is possible to bind properties and attributes of host element using @HostBinding decorators, e.g.
470 |
471 | ```typescript
472 | import { Component, HostBinding } from '@angular/core';
473 |
474 | @Component({
475 | selector: 'my-app',
476 | template: `
477 |
Use the input below to select host background-color:
493 | ### Component level providers
494 | Generally we get one service instance per the whole application.
495 | It is also possible to create an instance of service per component or directive.
496 |
497 | ```typescript
498 | @Component({
499 | selector: 'provide',
500 | template: '',
501 | providers: [ Service ]
502 | })
503 | export class ProvideComponent {}
504 | ```
505 |
506 | ```typescript
507 | @Directive({
508 | selector: '[provide]',
509 | providers: [ Service ]
510 | })
511 | export class ProvideDirective {}
512 | ```
513 |
514 |
515 | #### Links
516 | https://angular.io/guide/hierarchical-dependency-injection#component-level-injectors,https://stackblitz.com/edit/angular-cdk-happy-animals
517 |
518 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/component-level-providers) | [⬆ Back to top](#table-of-contents) | tags: [tips](https://30.codelab.fun/tags/tips) [components](https://30.codelab.fun/tags/components) [dependency-injection](https://30.codelab.fun/tags/dependency-injection)
519 |
520 | ### Global event listeners
521 | It is possible to add global event listeners in your Components/Directives with `HostListener`. Angular will take care of unsubscribing once your directive is destroyed.
522 |
523 | ```typescript
524 | @Directive({
525 | selector: '[rightClicker]'
526 | })
527 | export class ShortcutsDirective {
528 | @HostListener('window:keydown.ArrowRight')
529 | doImportantThings() {
530 | console.log('You pressed right');
531 | }
532 | }
533 | ```
534 |
535 |
536 | Bonus
537 |
538 | You can have multiple bindings:
539 |
540 | ```typescript
541 | @HostListener('window:keydown.ArrowRight')
542 | @HostListener('window:keydown.PageDown')
543 | next() {
544 | console.log('Next')
545 | }
546 | ```
547 |
548 | You can also pass params:
549 |
550 | ```typescript
551 | @HostListener('window:keydown.ArrowRight', '$event.target')
552 | next(target) {
553 | console.log('Pressed right on this element: ' + target)
554 | }
555 | ```
556 |
557 |
558 |
559 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/global-event-listeners) | [⬆ Back to top](#table-of-contents) | tags: [events](https://30.codelab.fun/tags/events) [components](https://30.codelab.fun/tags/components)
560 |
561 | ### Injecting document
562 | Sometimes you need to get access to global `document`.
563 |
564 | To simplify unit-testing, Angular provides it through dependency injection:
565 |
566 | ```typescript
567 | import { DOCUMENT } from '@angular/common';
568 | import { Inject } from '@angular/core';
569 |
570 | @Component({
571 | selector: 'my-app',
572 | template: `
Edit me
`
573 | })
574 | export class AppComponent {
575 |
576 | constructor(@Inject(DOCUMENT) private document: Document) {
577 | // Word with document.location, or other things here....
578 | }
579 | }
580 | ```
581 |
582 |
583 | #### Links
584 | https://angular.io/api/common/DOCUMENT
585 |
586 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/injecting-document) | [⬆ Back to top](#table-of-contents) | tags: [dependency injection](https://30.codelab.fun/tags/dependency-injection)
587 |
588 | ### Mark reactive fields as touched
589 | Here is the way to notify user that there are fields with non-valid values.
590 |
591 | `markFieldsAsTouched` function FormGroup or FormArray as an argument.
592 |
593 | ```typescript
594 | function markFieldsAsTouched(form: AbstractControl): void {
595 | form.markAsTouched({ onlySelf: true });
596 | if (form instanceof FormArray || form instanceof FormGroup) {
597 | Object.values(form.controls).forEach(markFieldsAsTouched);
598 | }
599 | }
600 | ```
601 |
602 |
603 | Bonus
604 |
605 | It's very useful to check out more general method [Accessing all nested form controls](#accessing-all-nested-form-controls) by [Thekiba](https://twitter.com/thekiba_io) to work with controls.
606 |
607 |
608 | #### Links
609 | https://angular.io/guide/reactive-forms
610 |
611 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/mark-reactive-fields-as-touched) | [⬆ Back to top](#table-of-contents) | tags: [reactive forms validation](https://30.codelab.fun/tags/reactive-forms-validation) [tips](https://30.codelab.fun/tags/tips) [good to know](https://30.codelab.fun/tags/good-to-know)
612 |
613 | ### Observables as outputs
614 | `EventEmitters` used for `@Output`'s are just Observables with an emit method.
615 |
616 | This means that you can just use `Observable` instance instead, e.g. we can wire up FormControl value changes directly:
617 |
618 | ```TypeScript
619 | readonly checkbox = new FormControl();
620 | @Output() readonly change = this.checkbox.valueChanges;
621 | ```
622 |
623 |
624 |
625 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/observables-as-outputs) | [⬆ Back to top](#table-of-contents) | tags: [tip](https://30.codelab.fun/tags/tip) [outputs](https://30.codelab.fun/tags/outputs)
626 |
627 | ### Passing template as an input
628 | It's possible to take a template as `@Input` for a component to customize the render
629 |
630 |
631 | ```typescript
632 | @Component({
633 | template: `
634 |
637 | `,
638 | })
639 | export class SiteMenuComponent {
640 | @Input() template: TemplateRef;
641 | }
642 | ```
643 | ```html
644 |
645 |
646 |
647 |
649 |
650 | ```
651 | > Note: `ng-content` should be used for most of the cases and it's simpler and more declarative.
652 | > Only use this approach if you need extra flexibility that can't be achieved with ng-content.
653 |
654 |
655 | #### Links
656 | https://blog.angular-university.io/angular-ng-template-ng-container-ngtemplateoutlet
657 |
658 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/passing-template-as-an-input) | [⬆ Back to top](#table-of-contents) | tags: [template](https://30.codelab.fun/tags/template)
659 |
660 | ### Preseving whitespaces
661 | By default Angular strips all whitespaces in templates to save bytes. Generally it's safe.
662 |
663 | For rare cases when you need to preserve spaces you can use special `ngPreserveWhitespaces` attribute:
664 |
665 | ```html
666 |
671 | ```
672 |
673 | > You can also use [preserveWhitespaces](https://angular.io/api/core/Component#preserveWhitespaces) option on a component.
674 |
675 |
676 | #### Links
677 | https://twitter.com/mgechev/status/1108913389277839360
678 |
679 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/preseving-whitespaces) | [⬆ Back to top](#table-of-contents) | tags: [tip](https://30.codelab.fun/tags/tip)
680 |
681 | ### Reusing code in template
682 | While the best way of reusing your code is creating a component, it's also possible to do it in a template.
683 |
684 | To do this you can use `ng-template` along with `*ngTemplateOutlet` directive.
685 |
686 | ```html
687 |
706 | ### Reusing existing custom pipes
707 | If you need a custom `pipe`, before creating one, consider checking out the [NGX Pipes package](https://github.com/danrevah/ngx-pipes) which has 70+ already implemeted custom pipes.
708 |
709 | Here are some examples:
710 |
711 | ```html
712 |
{{ date | timeAgo }}
713 |
714 |
715 |
{{ 'foo bar' | ucfirst }}
716 |
717 |
718 |
3 {{ 'Painting' | makePluralString: 3 }}
719 |
720 |
721 |
{{ [1, 2, 3, 1, 2, 3] | max }}
722 |
723 | ```
724 |
725 |
726 | #### Links
727 | https://github.com/danrevah/ngx-pipes
728 |
729 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/reusing-existing-custom-pipes) | [⬆ Back to top](#table-of-contents) | tags: [tip](https://30.codelab.fun/tags/tip) [pipes](https://30.codelab.fun/tags/pipes) [library](https://30.codelab.fun/tags/library)
730 |
731 | ### Style bindings
732 | You can use advanced property bindings to set specific style values based on component property values:
733 |
734 | ```html
735 |
753 |
754 |
755 |
756 | ```
757 |
758 |
759 |
760 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/style-bindings) | [⬆ Back to top](#table-of-contents) | tags: [styles](https://30.codelab.fun/tags/styles)
761 |
762 | ### Two-way binding any property
763 | Similar to how you can two-way bind `[(ngModel)]` you can two-way bind custom property on a component, for example `[(value)]`. To do it use appropriate Input/Output naming:
764 |
765 | ```typescript
766 | @Component({
767 | selector: 'super-input',
768 | template: `...`,
769 | })
770 | export class AppComponent {
771 | @Input() value: string;
772 | @Output() valueChange = new EventEmitter();
773 | }
774 | ```
775 |
776 | Then you can use it as:
777 | ```html
778 |
779 | ```
780 |
781 |
782 |
783 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/two-way-binding-any-property) | [⬆ Back to top](#table-of-contents) | tags: [tip](https://30.codelab.fun/tags/tip) [binding](https://30.codelab.fun/tags/binding)
784 |
785 | ### Using APP_INITIALIZER to delay app start
786 | It is possible to execute asynchronous task before the app start by providing a function returning promise using `APP_INITIALIZER` token.
787 |
788 | ```typescript
789 | @NgModule({
790 | providers: [
791 | {
792 | provide: APP_INITIALIZER,
793 | useValue: functionReturningPromise
794 | multi: true
795 | },
796 | })
797 | export class AppModule {}
798 |
799 |
800 | ```
801 |
802 |
803 | #### Links
804 | https://hackernoon.com/hook-into-angular-initialization-process-add41a6b7e,https://angular.io/api/core/APP_INITIALIZER
805 |
806 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/using-app_initializer-to-delay-app-start) | [⬆ Back to top](#table-of-contents) | tags: [tip](https://30.codelab.fun/tags/tip)
807 |
808 | ### Window Location injection
809 | For testing purposes you might want to inject `window.location` object in your component.
810 | You can achieve this with custom `InjectionToken` mechanism provided by Angular.
811 |
812 | ```typescript
813 | export const LOCATION_TOKEN = new InjectionToken('Window location object');
814 |
815 | @NgModule({
816 | providers: [
817 | { provide: LOCATION_TOKEN, useValue: window.location }
818 | ]
819 | })
820 | export class SharedModule {}
821 |
822 | //...
823 |
824 | @Component({
825 | })
826 | export class AppComponent {
827 | constructor(
828 | @Inject(LOCATION_TOKEN) public location: Location
829 | ) {}
830 | }
831 | ```
832 |
833 |
834 | #### Links
835 | https://itnext.io/testing-browser-window-location-in-angular-application-e4e8388508ff,https://angular.io/guide/dependency-injection
836 |
837 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/window-location-injection) | [⬆ Back to top](#table-of-contents) | tags: [dependency-injection](https://30.codelab.fun/tags/dependency-injection) [testing](https://30.codelab.fun/tags/testing)
838 |
839 |
840 | ## Advanced snippets
841 |
842 | ### Getting components of different types with ViewChild
843 | It's possible to use `@ViewChild` (also `@ViewChildren` and `@ContentChild/Children`) to query for components of different types using dependency injection.
844 |
845 | In the example below we can use `@ViewChildren(Base)` to get instances of `Foo` and `Bar`.
846 |
847 | ```typescript
848 | abstract class Base {}
849 |
850 | @Component({
851 | selector: 'foo',
852 | providers: [{ provide: Base, useExisting: Foo }]
853 | })
854 | class Foo extends Base {}
855 |
856 | @Component({
857 | selector: 'bar',
858 | providers: [{ provide: Base, useExisting: Bar }]
859 | })
860 | class Bar extends Base {}
861 |
862 | // Now we can require both types of components using Base.
863 | @Component({ template: `` })
864 | class AppComponent {
865 | @ViewChildren(Base) components: QueryList;
866 | }
867 | ```
868 |
869 |
870 | #### Links
871 | https://www.youtube.com/watch?v=PRRgo6F0cjs
872 |
873 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/getting-components-of-different-types-with-viewchild) | [⬆ Back to top](#table-of-contents) | tags: [good-to-know](https://30.codelab.fun/tags/good-to-know) [tips](https://30.codelab.fun/tags/tips) [components](https://30.codelab.fun/tags/components) [dependency-injection](https://30.codelab.fun/tags/dependency-injection)
874 |
875 | ### Router Custom Preloading
876 | Angular allows us to control the way module preloading is handled.
877 |
878 | There are 2 strategies provided by **@angular/router**: `PreloadAllModules` and `NoPreloading`. The latter enabled by default, only preloading lazy modules on demand.
879 |
880 | We can override this behavior by providing custom preloading strategy: In the example below we preload all included modules if the connection is good.
881 |
882 | ```typescript
883 | import { Observable, of } from 'rxjs';
884 |
885 | export class CustomPreloading implements PreloadingStrategy {
886 | public preload(route: Route, load: () => Observable): Observable {
887 | return preloadingConnection() ? load() : of(null);
888 | }
889 | }
890 |
891 | const routing: ModuleWithProviders = RouterModule.forRoot(routes, {
892 | preloadingStrategy: CustomPreloading
893 | });
894 | ```
895 | > Note that that the example above would not be very efficient for larger apps, as it'll preload all the modules.
896 |
897 |
898 | #### Links
899 | https://angular.io/api/router/PreloadingStrategy,https://vsavkin.com/angular-router-preloading-modules-ba3c75e424cb,https://medium.com/@adrianfaciu/custom-preloading-strategy-for-angular-modules-b3b5c873681a,https://coryrylan.com/blog/custom-preloading-and-lazy-loading-strategies-with-angular
900 |
901 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/router-custom-preloading) | [⬆ Back to top](#table-of-contents) | tags: [router](https://30.codelab.fun/tags/router)
902 |
903 | ### SVG
904 | It is possible to use SVG tags in your Angular component, to create beautiful graphs and visualizations. There are 3 things you need to know:
905 |
906 | 1. When binding an SVG attribute, use `attr`
907 | ```html
908 |
909 | ```
910 |
911 | 2. When creating sub-components, use attribute and not tag selector:
912 | ```html
913 | // Not:
914 |
915 | ```
916 | ```typescript
917 | @Component({selector: '[child-component]' })
918 | ```
919 |
920 | 3. When using SVG tags in sub-components use svg prefix:
921 | ```typescript
922 | @Component({
923 | selector: '[child-component]',
924 | template: ``
925 | })
926 | ```
927 |
928 |
929 |
930 | [⭐ Interactive demo of this snippet](https://30.codelab.fun/svg) | [⬆ Back to top](#table-of-contents) | tags: [tip](https://30.codelab.fun/tags/tip) [SVG](https://30.codelab.fun/tags/svg)
931 |