└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # Angular Cheatsheet 2 | Set of basic functionalities from Angular in one place. Thanks to http://foreach.pl for contribute 3 | 4 | **Check also other Cheatsheets:** 5 | 6 | [TypeScript](https://github.com/delprzemo/typescript-cheatsheet) 7 | 8 | [ReactJS](https://github.com/delprzemo/react-cheatsheet) 9 | 10 | # Table of Contents 11 | 12 | 13 | 14 | * [AngularCli](#AngularCLI) 15 | * [Components and Templates](#Components-and-Templates) 16 | * [Sample component ts file](#Sample-component-ts-file) 17 | * [Component life cycles](#Component-life-cycles) 18 | * [Template syntax](#Template-syntax) 19 | * [Input and Output](#Input-and-Output) 20 | * [Content projection](#Content-projection) 21 | * [ViewChild decorator](#ViewChild-decorator) 22 | * [Routing](#Routing) 23 | * [CanActivate and CanDeactivate](#CanActivate-and-CanDeactivate) 24 | * [Modules](#Modules) 25 | * [Services](#Services) 26 | * [HttpClient](#HttpClient) 27 | * [Dependency Injection](#Dependency-Injection) 28 | * [Declare global values](#Declare-global-values) 29 | * [Pipes](#Pipes) 30 | * [Directives](#Directives) 31 | * [Animations](#Animations) 32 | * [Angular Forms](#Angular-Forms) 33 | * [Template driven forms](#Template-driven-forms) 34 | * [Reactive forms](#Reactive-forms) 35 | * [Custom Validator for Reactive forms](#Custom-Validator-for-Reactive-forms) 36 | * [Custom Validator Directive for Template driven forms](#Custom-Validator-Directive-for-Template-driven-forms) 37 | * [ngModel in custom component](#ngModel-in-custom-component) 38 | * [Tests](#Tests) 39 | * [Unit tests](#Unit-tests) 40 | * [Others](#Others) 41 | * [Http interceptor](#Http-interceptor) 42 | * [host](#host) 43 | * [Interview questions](#Interview-questions) 44 | 45 | 46 | 47 | AngularCLI 48 | ================= 49 | 50 | Command line inferface for Angular - set of commands that will help us during development. 51 | 52 | **1. Setup** 53 | 54 | | Command | Description | 55 | | ------------- | ------------- | 56 | | npm install -g @angular/cli | Install Angular CLI globally | 57 | 58 | **2. New application** 59 | 60 | | Command | Description | 61 | | ------------- | ------------- | 62 | | ng new best-practises --dry-run | just simulate ng new | 63 | | ng new best-practises --skip-install | skip install means don't run npm install | 64 | | ng new best-practises --prefix best | set prefix to best | 65 | | ng new --help | check available command list | 66 | 67 | 68 | **3. Lint - check and make sure that our code if free of code smells/ bad formatting** 69 | 70 | | Command | Description | 71 | | ------------- | ------------- | 72 | | ng lint my-app --help | check available command list | 73 | | ng lint my-app --format stylish | format code | 74 | | ng lint my-app --fix | fix code smells | 75 | | ng lint my-app | show warnings | 76 | 77 | 78 | **4. Blueprints** 79 | 80 | | Command | Description | 81 | | ------------- | ------------- | 82 | | ng g c my-component --flat true | don't create new folder for this component | 83 | | --inline-template (-t) | will the template be in .ts file? | 84 | | --inline-style (-s) | will the style be in .ts file? | 85 | | --spec | generate spec? | 86 | | --prefix | assign own prefix | 87 | | ng g d directive-name | create directive | 88 | | ng g s service-name | create service | 89 | | ng g cl models/customer | create customer class in models folder | 90 | | ng g i models/person | create create interface in models folder | 91 | | ng g e models/gender | create create ENUM gender in models folder | 92 | | ng g p init-caps | create create pipe | 93 | 94 | **5. Building&Serving** 95 | 96 | | Command | Description | 97 | | ------------- | ------------- | 98 | | ng build | build app to /dist folder | 99 | | ng build --aot | build app without code that we don't need (optimatization) | 100 | | ng build --prod | build for production | 101 | | ng serve -o | serve with opening a browser | 102 | | ng serve --live-reload | reload when changes occur | 103 | | ng serve -ssl | serving using SSL | 104 | 105 | **6. Add new capabilities** 106 | 107 | | Command | Description | 108 | | ------------- | ------------- | 109 | | ng add @angular/material | add angular material to project | 110 | | ng g @angular/material:material-nav --name nav | create material navigation component | 111 | 112 | # Components and Templates 113 | 114 | Components are the most basic UI building block of an Angular app. An Angular app contains a tree of Angular components. 115 | 116 | ## Sample component ts file 117 | ```ts 118 | import { Component } from '@angular/core'; 119 | 120 | @Component({ 121 | // component attributes 122 | selector: 'app-root', 123 | templateUrl: './app.component.html', 124 | styleUrls: ['./app.component.less'] 125 | }) 126 | 127 | export class AppComponent { 128 | title = 'my-dogs-training'; 129 | } 130 | ``` 131 | 132 | ## Component attributes 133 | 134 | 135 | | Attribute | Description | 136 | | ------------- | ------------- | 137 | | changeDetection | The change-detection strategy to use for this component. | 138 | | viewProviders | Defines the set of injectable objects that are visible to its view DOM children | 139 | | moduleId | The module ID of the module that contains the component | 140 | | encapsulation | An encapsulation policy for the template and CSS styles | 141 | | interpolation | Overrides the default encapsulation start and end delimiters ({{ and }} | 142 | | entryComponents | A set of components that should be compiled along with this component. | 143 | | preserveWhitespaces | True to preserve or false to remove potentially superfluous whitespace characters from the compiled template. | 144 | 145 | ## Component life cycles 146 | 147 | | Life cycle | Description | 148 | | ------------- | ------------- | 149 | | ngOnInit | Called once, after the first ngOnChanges() | 150 | | ngOnChanges | Called before ngOnInit() and whenever one of input properties change. | 151 | | ngOnDestroy | Called just before Angular destroys the directive/component | 152 | | ngDoCheck | Called during every change detection run | 153 | | ngAfterContentChecked | Called after the ngAfterContentInit() and every subsequent ngDoCheck() | 154 | | ngAfterViewChecked | Called after the ngAfterViewInit() and every subsequent ngAfterContentChecked(). | 155 | | ngAfterContentInit | Called once after the first ngDoCheck(). | 156 | | ngAfterViewInit | Called once after the first ngAfterContentChecked(). | 157 | 158 | ## Template syntax 159 | 160 | | Syntax | Description | 161 | | ------------- | ------------- | 162 | | {{user.name}} | Interpolation - just generate user name here | 163 | | | property binding - bind image url for user to src attribute | 164 | | 273 | ``` 274 | 275 | 276 | # Routing 277 | The Angular Router enables navigation from one view to the next as users perform application tasks. 278 | 279 | **Sample routing ts file** 280 | ```ts 281 | const appRoutes: Routes = [ 282 | { path: 'crisis-center', component: CrisisListComponent }, 283 | { path: 'hero/:id', component: HeroDetailComponent }, 284 | { 285 | path: 'heroes', 286 | component: HeroListComponent, 287 | data: { title: 'Heroes List' } 288 | }, 289 | { path: '', 290 | redirectTo: '/heroes', 291 | pathMatch: 'full' 292 | }, 293 | { path: '**', component: PageNotFoundComponent } 294 | ]; 295 | ``` 296 | 297 | Then this should be added inside Angular.module imports 298 | ```ts 299 | RouterModule.forRoot(appRoutes) 300 | ``` 301 | 302 | You can also turn on console tracking for your routing by adding enableTracing 303 | 304 | ```ts 305 | imports: [ 306 | RouterModule.forRoot( 307 | routes, 308 | {enableTracing: true} 309 | ) 310 | ], 311 | ``` 312 | 313 | **Usage** 314 | ```html 315 | Crisis Center 316 | ``` 317 | routerLinkActive="active" will add active class to element when the link's route becomes active 318 | 319 | ```ts 320 | //Navigate from code 321 | this.router.navigate(['/heroes']); 322 | 323 | // with parameters 324 | this.router.navigate(['/heroes', { id: heroId, foo: 'foo' }]); 325 | 326 | // Receive parameters without Observable 327 | let id = this.route.snapshot.paramMap.get('id'); 328 | ``` 329 | 330 | ## CanActivate and CanDeactivate 331 | Interface that a class can implement to be a guard deciding if a route can be activated. If all guards return true, navigation will continue. 332 | 333 | ```ts 334 | 335 | class AlwaysAuthGuard implements CanActivate { 336 | canActivate() { 337 | return true; 338 | } 339 | } 340 | ``` 341 | and assing it in routing module: 342 | 343 | ```ts 344 | { 345 | path: 'artist/:artistId', 346 | component: ArtistComponent, 347 | canActivate: [AlwaysAuthGuard], 348 | children: [ 349 | {path: '', redirectTo: 'tracks'}, 350 | {path: 'tracks', component: ArtistTrackListComponent}, 351 | {path: 'albums', component: ArtistAlbumListComponent}, 352 | ] 353 | } 354 | ``` 355 | 356 | # Modules 357 | Angular apps are modular and Angular has its own modularity system called NgModules. NgModules are containers for a cohesive block of code dedicated to an application domain, a workflow, or a closely related set of capabilities. 358 | 359 | ## Sample module with comments 360 | 361 | ```ts 362 | import { BrowserModule } from '@angular/platform-browser'; 363 | import { NgModule } from '@angular/core'; 364 | import { AppRoutingModule } from './app-routing.module'; 365 | import { AppComponent } from './app.component'; 366 | 367 | @NgModule({ 368 | declarations: [AppComponent], // components, pipes, directives 369 | imports: [BrowserModule, AppRoutingModule], // other modules 370 | providers: [], // services 371 | bootstrap: [AppComponent] // top component 372 | }) 373 | export class AppModule { } 374 | ``` 375 | 376 | # Services 377 | Components shouldn't fetch or save data directly and they certainly shouldn't knowingly present fake data. They should focus on presenting data and delegate data access to a service. 378 | 379 | **Sample service with one function** 380 | ```ts 381 | @Injectable() 382 | export class MyService { 383 | public items: Item[]; 384 | constructor() { } 385 | 386 | getSth() { 387 | // some implementation 388 | } 389 | } 390 | ``` 391 | **Usage** 392 | It should be injected before usage 393 | ```ts 394 | constructor(private dogListService: MyService) 395 | ``` 396 | 397 | and add in module: 398 | 399 | ```ts 400 | providers: [MyService] 401 | ``` 402 | 403 | ## HttpClient 404 | To handle and consume http requests 405 | 406 | 1. Add import to module 407 | ```ts 408 | import { HttpClientModule} from "@angular/common/http"; 409 | ``` 410 | 411 | 2. Usage 412 | ```ts 413 | import {HttpClient} from '@angular/common/http'; 414 | 415 | ... 416 | 417 | // GET 418 | public getData(): Observable { 419 | return this.http.get('api/users/2'); 420 | } 421 | 422 | // POST 423 | public send(val1: any, val2: any): Observable { 424 | const object = new SendModel(val1, val2); 425 | const options = {headers: new HttpHeaders({'Content-type': 'application/json'})}; 426 | return this.http.post(environment.apiUrl + 'api/login', object, options); 427 | } 428 | 429 | 430 | ``` 431 | 432 | ## Dependency Injection 433 | 434 | Inject class into another class 435 | ```ts 436 | @Injectable({ 437 | providedIn: 'root', 438 | }) 439 | export class SomeService {} 440 | ``` 441 | It accepts 'root' as a value or any module of your application 442 | 443 | ## Declare global values 444 | 445 | class: 446 | ```ts 447 | import {InjectionToken} from '@angular/core'; 448 | export const CONTROLS_GLOBAL_CONFIG = new InjectionToken('global-values'); 449 | export interface ControlsConfig {firstGlobalValue: string;} 450 | ``` 451 | 452 | module: 453 | ```ts 454 | providers: [{provide: CONTROLS_GLOBAL_CONFIG, useValue: {firstGlobalValue : 'Some value' }}, 455 | ``` 456 | 457 | usage (for example in component) 458 | ```ts 459 | constructor(@Optional() @Inject(CONTROLS_GLOBAL_CONFIG) globalVlues: ControlsConfig) { 460 | ``` 461 | 462 | # Pipes 463 | Transform data/value to specific format, for example: 464 | 465 | Show date in shortDate format: 466 | ```html 467 | {{model.birthsDay | date:'shortDate'}} 468 | ``` 469 | 470 | **Pipe implementation** 471 | ```ts 472 | @Pipe({name: 'uselessPipe'}) 473 | export class uselessPipe implements PipeTransform { 474 | transform(value: string, before: string, after: string): string { 475 | let newStr = `${before} ${value} ${after}`; 476 | return newStr; 477 | } 478 | } 479 | ``` 480 | 481 | **usage** 482 | ```html 483 | {{ user.name | uselessPipe:"Mr.":"the great" }} 484 | ``` 485 | 486 | # Directives 487 | An Attribute directive changes the appearance or behavior of a DOM element. For example [ngStyle] is a directive 488 | 489 | **Custom directive** 490 | ```ts 491 | import { Directive, ElementRef, HostListener, Input } from '@angular/core'; 492 | 493 | @Directive({ 494 | selector: '[appHighlight]' 495 | }) 496 | export class HighlightDirective { 497 | 498 | constructor(private el: ElementRef) { } 499 | 500 | @Input('appHighlight') highlightColor: string; 501 | @Input('otherPar') otherPar: any; //it will be taken from other attribute named [otherPar] 502 | 503 | @HostListener('mouseenter') onMouseEnter() { 504 | this.highlight(this.highlightColor || 'red'); 505 | } 506 | 507 | private highlight(color: string) { 508 | this.el.nativeElement.style.backgroundColor = color; 509 | } 510 | } 511 | ``` 512 | 513 | ***Usage*** 514 | ```html 515 |

Highlight me!

516 | ``` 517 | 518 | 519 | # Animations 520 | Animations - moving from style state to another style state. Before add BrowserModule and BrowserAnimationsModule to module 521 | 522 | **Implementation:** 523 | ```ts 524 | animations: [ 525 | trigger('openClose', [ 526 | state('open', style({ 527 | height: '400px', 528 | opacity: 1.5, 529 | })), 530 | state('closed', style({ 531 | height: '100px', 532 | opacity: 0.5, 533 | })), 534 | transition('open => closed', [ 535 | animate('1s') 536 | ]), 537 | transition('closed => open', [ 538 | animate('1s') 539 | ]) 540 | ]) 541 | ] 542 | ``` 543 | 544 | **usage** 545 | ```html 546 |
547 | ``` 548 | 549 | # Angular Forms 550 | 551 | ## Template driven forms 552 | Form logic (validation, properties) are kept in template 553 | 554 | **sample html** 555 | ```html 556 |
557 |
558 | 559 | 560 |
561 |
First Name is required
562 |
563 |
564 |
565 | 566 |
567 |
568 | ``` 569 | 570 | **sample component** 571 | ```ts 572 | @ViewChild("f") form: any; 573 | firstName: string = ""; 574 | langs: string[] = ["English", "French", "German"]; 575 | 576 | onSubmit() { 577 | if (this.form.valid) { 578 | console.log("Form Submitted!"); 579 | this.form.reset(); 580 | } 581 | } 582 | ``` 583 | 584 | ## Reactive forms 585 | Form logic (validation, properties) are kept in component 586 | 587 | **sample html** 588 | ```html 589 |
590 |
591 | 592 | 593 |
594 |
Email is required
595 |
Email must be a valid email address
596 |
597 |
598 | 599 |
600 | 601 |
602 |
603 | ``` 604 | 605 | **sample component** 606 | ```ts 607 | registerForm: FormGroup; 608 | submitted = false; 609 | 610 | constructor(private formBuilder: FormBuilder) { } 611 | 612 | ngOnInit() { 613 | this.registerForm = this.formBuilder.group({ 614 | firstName: [{{here default value}}, Validators.required], 615 | lastName: ['', Validators.required], 616 | email: ['', [Validators.required, Validators.email]], 617 | password: ['', [Validators.required, Validators.minLength(6)]] 618 | }); 619 | } 620 | 621 | // convenience getter for easy access to form fields 622 | get f() { return this.registerForm.controls; } 623 | 624 | onSubmit() { 625 | this.submitted = true; 626 | 627 | // stop here if form is invalid 628 | if (this.registerForm.invalid) { 629 | return; 630 | } 631 | 632 | alert('SUCCESS!! :-)') 633 | } 634 | ``` 635 | 636 | ## Custom Validator for Reactive forms 637 | 638 | **Function** 639 | ```ts 640 | validateUrl(control: AbstractControl) { 641 | if (!control.value || control.value.includes('.png') || control.value.includes('.jpg')) { 642 | return null; 643 | } 644 | return { validUrl: true }; 645 | } 646 | ``` 647 | 648 | **Usage** 649 | ```ts 650 | this.secondFormGroup = this._formBuilder.group({ 651 | imageCtrl: ['', [Validators.required, this.validateUrl]] 652 | }); 653 | ``` 654 | 655 | **Multi-field validation** 656 | ```ts 657 | validateNameShire(group: FormGroup) { 658 | if (group) { 659 | if (group.get('isShireCtrl').value && !group.get('nameCtrl').value.toString().toLowerCase().includes('shire')) { 660 | return { nameShire : true }; 661 | } 662 | } 663 | return null; 664 | } 665 | ``` 666 | 667 | **Multi-field validation usage*** 668 | ```ts 669 | this.firstFormGroup.setValidators(this.validateNameShire); 670 | ``` 671 | 672 | **Error handling** 673 | ```html 674 |
Name is too long
675 |
Shire dogs should have "shire" in name
676 | ``` 677 | 678 | ## Custom Validator Directive for Template driven forms 679 | To register our custom validation directive to NG_VALIDATORS service we have to do it like this: 680 | (thanks to multi parameter we won't override NG_VALIDATORS but just add CustomValidator to NG_VALIDATORS) 681 | 682 | ```ts 683 | @Directive({ 684 | selector: '[CustomValidator]', 685 | providers: [{provide: NG_VALIDATORS, useExisting: CustomValidator, multi:true}] 686 | }) 687 | ``` 688 | 689 | Example: 690 | ```ts 691 | @Directive({ 692 | selector: '[customValidation]', 693 | providers: [{provide: NG_VALIDATORS, useExisting: EmailValidationDirective, multi: true}] 694 | }) 695 | export class CustomValidation implements Validator { 696 | constructor() { } 697 | validate(control: AbstractControl): ValidationErrors { 698 | return (control.value && control.value.length <= 300) ? 699 | {myValue : true } : null; 700 | } 701 | } 702 | ``` 703 | 704 | For multiple fields: 705 | ```ts 706 | validate(formGroup: FormGroup): ValidationErrors { 707 | const passwordControl = formGroup.controls["password"]; 708 | const emailControl = formGroup.controls["login"]; 709 | if (!passwordControl || !emailControl || !passwordControl.value || !emailControl.value) { 710 | return null; 711 | } 712 | 713 | if (passwordControl.value.length > emailControl.value.length) { 714 | passwordControl.setErrors({ tooLong: true }); 715 | } else { 716 | passwordControl.setErrors(null); 717 | } 718 | return formGroup; 719 | } 720 | 721 | ``` 722 | 723 | ## ngModel in custom component 724 | 725 | 1. Add to module: 726 | ```ts 727 | providers: [ 728 | { 729 | provide: NG_VALUE_ACCESSOR, 730 | useExisting: forwardRef(() => TextAreaComponent), 731 | multi: true 732 | } 733 | ] 734 | ``` 735 | 736 | 2. Implement ControlValueAccessor interface 737 | ```ts 738 | interface ControlValueAccessor { 739 | writeValue(obj: any): void 740 | registerOnChange(fn: any): void 741 | registerOnTouched(fn: any): void 742 | setDisabledState(isDisabled: boolean)?: void 743 | } 744 | ``` 745 | 746 | 747 | | Function | Description | 748 | | ------------- | ------------- | 749 | | registerOnChange | Register a function to tell Angular when the value of the input changes | 750 | | registerOnTouched | Register a function to tell Angular when the value was touched | 751 | | writeValue | tell Angular how to Write a value to the input | 752 | 753 | Sample implementation: 754 | ```ts 755 | 756 | @Component({ 757 | selector: 'app-text-area', 758 | templateUrl: './text-area.component.html', 759 | styleUrls: ['./text-area.component.less'], 760 | providers: [ 761 | { 762 | provide: NG_VALUE_ACCESSOR, 763 | useExisting: forwardRef(() => TextAreaComponent), 764 | multi: true 765 | } 766 | ] 767 | }) 768 | export class TextAreaComponent implements ControlValueAccessor, OnInit { 769 | @Input() value: string; 770 | 771 | private _onChange = (data: any) => { console.log('changed: ' + data); }; 772 | private _onTouched = (data?: any) => {console.log('touched: ' + data); }; 773 | 774 | ngOnInit(): void { 775 | const self = this; 776 | } 777 | 778 | constructor() {} 779 | 780 | writeValue(obj: any): void { 781 | this.value = obj; 782 | } 783 | 784 | registerOnChange(fn) { 785 | this._onChange = fn; 786 | } 787 | 788 | registerOnTouched(fn: any): void { 789 | this._onTouched = fn; 790 | } 791 | } 792 | 793 | ``` 794 | 795 | # Tests 796 | 797 | ## Unit tests 798 | 799 | **Service** 800 | ```ts 801 | describe('MyService', () => { 802 | let service: MyService; 803 | beforeEach(() => service = new MyService(); 804 | it('#fetch should update data', () => { 805 | service.fetchData(); 806 | expect(service.data.length).toBe(4); 807 | expect(service.data[0].id).toBe(1); 808 | }); 809 | }); 810 | ``` 811 | 812 | For async functions 813 | 814 | ```ts 815 | it('#fetch should update data', (done: DoneFn) => { 816 | // some code 817 | done(); // we need 'done' to avoid test finishing before date was received 818 | // some code 819 | }); 820 | ``` 821 | 822 | example async test: 823 | ```ts 824 | it('http client works', (done: DoneFn) => { 825 | service.getUser().subscribe((data) => { 826 | expect(data).toBe('test'); 827 | done(); 828 | }); 829 | }); 830 | ``` 831 | 832 | **Spy and stub** 833 | 834 | Spy: 835 | ```ts 836 | // create spied object by copy getDataAsync from HttpService 837 | const valueServiceSpy = 838 | jasmine.createSpyObj('HttpService', ['getDataAsync']); 839 | ``` 840 | Stub: 841 | ```ts 842 | const stubValue = of('StubValue'); 843 | valueServiceSpy.getDataAsync.and.returnValue(stubValue); 844 | ``` 845 | 846 | **TestBed** 847 | Mock whole module/environment for unit tests 848 | 849 | ```ts 850 | beforeEach(() => { 851 | let httpClientMock = TestBed.configureTestingModule({ providers: [{ provide: MyService, useValue: new MyService(httpClientMock)}] }); 852 | }); 853 | ``` 854 | 855 | Then use tested object (for example service) like this: 856 | 857 | ```ts 858 | service = TestBed.get(MyService); 859 | ``` 860 | we can add schemas: [NO_ERRORS_SCHEMA]. This means that we don’t have to mock children component dependencies of this component as Angular won’t yell at us anymore for our lack of doing so. 861 | 862 | 863 | # Others 864 | 865 | ## Http interceptor 866 | Intercepts and handles an HttpRequest or HttpResponse. 867 | 868 | Class: 869 | ```ts 870 | @Injectable() 871 | export class MyInterceptor implements HttpInterceptor { 872 | 873 | constructor() { } 874 | 875 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 876 | // do sth (like check and throw error) 877 | return next.handle(request); //if want continue 878 | } 879 | } 880 | 881 | ``` 882 | 883 | Module: 884 | ```ts 885 | { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true }, 886 | ``` 887 | 888 | ## host 889 | Refer to host element/component 890 | 891 | 892 | | Value | Description | 893 | | ------------- | ------------- | 894 | | :host(selector) { ... } | to match attributes, classes on the host element and add styling to it | 895 | | :host-context(selector) { ... } | to match elements, classes on parent components and add styling to it | 896 | | :host ::ng-deep | styling will be applied also to all child components | 897 | 898 | 899 | # Interview questions 900 | 901 | **When would you use the useFactory provider method?** 902 | 903 | With useFactory we can use a factory at runtime to decide which kind of service we want to return if it got requested by any other class in our application or you need to parameterize the construction of a service 904 | 905 | ```ts 906 | export function sampleFactory() { 907 | return new Service1(); 908 | } 909 | 910 | export class Service1 {} 911 | 912 | @Injectable({ 913 | providedIn: 'root', 914 | useFactory: xyzFactory 915 | }) 916 | export class Service2 {} 917 | ``` 918 | 919 | Service1 will be injected into another class 920 | 921 | **What is router-outlet** 922 | 923 | Acts as a placeholder that Angular dynamically fills based on the current router state. Generally in place of your main app component will be generated 924 | 925 | **How to declare global value?** 926 | 927 | Use InjectionToken 928 | 929 | **Which decorator lets you inject a service registered with an Injection Token?** 930 | 931 | @Inject 932 | 933 | for example 934 | ```ts 935 | @Inject(CONTROLS_GLOBAL_CONFIG) globalVlues: ControlsConfig 936 | ``` 937 | 938 | **How to mimick environment for components/services in tests?** 939 | 940 | Use TestBed. See [Unit tests](https://github.com/delprzemo/angular-cheatsheet#unit-tests "Unit tests") 941 | 942 | **What is Resolve interface?** 943 | 944 | Interface that classes can implement to be a data provider for component while routing. 945 | 946 | example: 947 | 948 | ```ts 949 | @Injectable() 950 | class UserResolver implements Resolve { 951 | constructor(private service: MySampleService) {} 952 | 953 | resolve( 954 | route: ActivatedRouteSnapshot, 955 | state: RouterStateSnapshot 956 | ): Observable { 957 | return this.service.fetchData(route.params.id); 958 | } 959 | } 960 | 961 | @NgModule({ 962 | imports: [ 963 | RouterModule.forRoot([ 964 | { 965 | path: 'user/:id', 966 | component: UserComponent, 967 | resolve: { 968 | userData: UserResolver 969 | } 970 | } 971 | ]) 972 | ], 973 | providers: [UserResolver] 974 | }) 975 | ``` 976 | 977 | We can use it to pre-load data for a component before the component is displayed 978 | 979 | **How to begin validation after the user will enter a value and pause?** 980 | 981 | Use debounceTime, for example 982 | 983 | ```ts 984 | this.formCtrlSub = this.firstNameControl.valueChanges 985 | .debounceTime(1000) 986 | .subscribe(newValue => this.firstName = newValue); 987 | ``` 988 | 989 | **What is valueChanges in form control?** 990 | 991 | To catch value changes and implement some logic in observable result. See example above 992 | 993 | **How to execute canActivate if any of child routes will change?** 994 | 995 | Use canActivateChild 996 | 997 | **What are compilation types in Angular?** 998 | 999 | | Type | Description | 1000 | | ------------- | ------------- | 1001 | | AoT | Ahead of time - compile full application / module when application was opened. Used mainly on production | 1002 | | JiT | Just in time - compile specific element when it has been opened. Used mainly while programming | 1003 | | Ivy | Since Angular 8 - engine based on concept Incremental DOM | 1004 | 1005 | 1006 | **What is ng-content in Angular?** 1007 | 1008 | See [ng-content](https://github.com/delprzemo/angular-cheatsheet/blob/master/README.md#content-projection "ng-content") 1009 | 1010 | **How to create application with cutom prefix?** 1011 | 1012 | ``` 1013 | ng new app-name --prefix my-cutom-prefix 1014 | ``` 1015 | 1016 | **What is module lazy loading?** 1017 | 1018 | Instead of loading all modules while app starts we can load particular module when needed. 1019 | 1020 | ```ts 1021 | const routes: Routes = [ 1022 | { 1023 | path: 'users', 1024 | loadChildren: () => import('./users/users.module').then(m => m.UserModule) 1025 | } 1026 | ``` 1027 | 1028 | This is usually used for big apps in order to improve performance 1029 | 1030 | **Why should we consider using ngIf instead of ngClass/ngStyle for hiding element?** 1031 | 1032 | ngIf won't generate element when condition result is fale, so html will be lighter. ngClass/ngStyle will just hide element but it will be still existing in DOM 1033 | 1034 | **What is Done() function in tests?** 1035 | 1036 | We need 'done' to avoid test finishing before date was received 1037 | See [done](https://github.com/delprzemo/angular-cheatsheet#unit-tests "done") 1038 | 1039 | **What "import", "providers" and "declarations" stand for in NgModule?** 1040 | 1041 | ```ts 1042 | declarations: [AppComponent], // components, pipes, directives 1043 | imports: [BrowserModule, AppRoutingModule], // other modules 1044 | providers: [], // services 1045 | ``` 1046 | See [Sample module](https://github.com/delprzemo/angular-cheatsheet#sample-module-with-comments "Sample module") 1047 | 1048 | **Explain the difference between Constructor and ngOnInit** 1049 | 1050 | Constructor is a method assigned to a class, so it is called when class object was initialized. 1051 | ngOnInit is part of Component life cycle and it is dependent on the current state of view initialization. 1052 | Constructor is called before ngOnInit 1053 | 1054 | **What is a difference between ElementRef and TemplateRef?** 1055 | 1056 | ElementRef is reference to particular element while TemplateRef can refer to whole ng-template 1057 | 1058 | ```html 1059 | 1060 | Test 1061 | 1062 | ``` 1063 | 1064 | ```ts 1065 | export class SthComponent { 1066 | @ViewChild('msg') 1067 | private testTempRef : TemplateRef 1068 | } 1069 | ``` 1070 | 1071 | **Point all data biding ways for element** 1072 | 1073 | | Bindint type | Example | 1074 | | ------------- | ------------- | 1075 | | Property binding | [src]="this.src" | 1076 | | Event binding | (click)="this.doSth()" | 1077 | | Two way data bidning | [(ngModel)]="this.form.userName" | 1078 | 1079 | **How to handle ngModel property in custom component?** 1080 | 1081 | Implement ControlValueAccessor interface. 1082 | See [ngModel](https://github.com/delprzemo/angular-cheatsheet#ngmodel-in-custom-component "ngModel") 1083 | 1084 | **What is differenct between default and onPush change detector?** 1085 | 1086 | default change detector will check bidnings in whole application component tree after event 1087 | OnPush change detector informs Angular that our component biding is depend on input parameters. Moreover it won't check bindings in whole application but only in subtree where our component belongs. 1088 | 1089 | **Why is it better to use pure pipes instead of some functions in template view?** 1090 | 1091 | If we want to calculate for example user age and show it on template then we can do it with function: 1092 | 1093 | ```html 1094 | {{this.getUserAge(user.birthDate)}} 1095 | ``` 1096 | 1097 | But it will be calculated every time when change detector is run, so it can affect on performance. 1098 | Instead we can use pipe for that: 1099 | 1100 | ```html 1101 | {{user.birthDate | calculateAge}} 1102 | ``` 1103 | 1104 | Now age calculation won't be performed so many times. 1105 | 1106 | **How to detect change for any @Input property?** 1107 | 1108 | Use ngOnChanges hook 1109 | 1110 | ```ts 1111 | ngOnChanges(values) { 1112 | // logic here 1113 | } 1114 | ``` 1115 | 1116 | **How to detect change for specific @Input in component?** 1117 | 1118 | Use 'set' accessor 1119 | 1120 | ```ts 1121 | @Input() set name(value) { 1122 | // some logic here 1123 | } 1124 | ``` 1125 | --------------------------------------------------------------------------------