-
Notifications
You must be signed in to change notification settings - Fork 0
/
Readme.txt
4130 lines (3488 loc) · 179 KB
/
Readme.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
b# Angular---The-Complete-Guide-2021-Edition-
You can find the source code of each section attached to the last lecture of that section!
Getting Started
***************
Updating npm:
Run [sudo] npm install -g npm (sudo is only required on Mac/ Linux)
Updating the CLI
[sudo] npm uninstall -g angular-cli @angular/cli
npm cache verify
[sudo] npm install -g @angular/cli
Installing angular
1.install node from wwww.nodejs.com
2.node -v
3.npm -v
4.set environmental variable path
->To search it to go c:\users\APPData\
->angular/ng path ->C:\Users\Imam Hulagur\AppData\Roaming\npm\node_modules\@angular\cli\bin
npm ->C:\Users\Imam Hulagur\AppData\Roaming\npm\
5.ng --version
6.create new project
cmd>ng new my-first-app
Project Hands on
Component creation and Data binding
-model is just a ts file, which represents blueprint of a object we create[like a bean object in java]
-it will not contain @Model decorator
The Basics
**********
App startup
-----------
<app-root>Loading...</app-root>
here on compilation we wont be able to see Loading instead because Angular overrides this tah with app component at run time.
The first which gets executed is main.ts ->which inturn import AppModule.
platformBrowserDynamics.bootstrapModule(AppModule)
The it will go to app.module.ts -> here the bootstrap: [AppComponent] inside @NgModule({}) will load the app component using selector tag <app-rot></app-rot> inside index.html.
The index.html - contains the executed code(which also included out own component typescript code) compiled to javascript and imported with script tag.
selector
--------
preferred selector is select by element.
Element selector -> selector: 'app-servers'
in html <app-servers></app-servers>
Attribute selector-> selector: '[app-servers]'
in html <div app-servers></div>
Class selector -> selector: '.app-servers;
in html <div class="app-servers"></div>
note: selecting by id wont work in angular and all those hover and so on psuedo selectors wont work here.
Data binding
-----------
sharing data between behavior ts code(business login) and template(html)
One way data binding
-------------------
output data -> from .ts to .html
1.string interpolation
{{data}}
2.property binding
[property]='data'
input data->from .html to .ts
React to user events - when user click a button we need to change some behavior of login.
Event binding
(event)='customFunction()'
Two way data binding
--------------------
Instead of listening to (input)="someMethod()" changing value by listening to $event and rewriting the value to DOM using string interpolation we can achieve it by using ngModel directive provided in FormModule.
Important: For Two-Way-Binding to work, you need to enable the ngModel directive. This is done by adding the FormsModule to the imports[] array in the AppModule.
You then also need to add the import from @angular/forms in the app.module.ts file:
import { FormsModule } from '@angular/forms';
[(ngModel)]="data"
Directives
-----------
Directives are instructions in the DOM!
Builtin directive
-----------------
The directives with templates.
1.structural directives : affect whole area in DOM
ex : *ngIF, *ngFor, *ngSwitch
2.Attribute directives : only affect/changes the element that they are added to
ex: [ngClass], [ngStyle], [ngSwitch] with *ngSwitchCase's and ngSwitchDefault
* represent in directive it will compile down to
before
<div *ngIf="someClass">
//some prop
</div>
on compilation - it will converted into property binding inside <ng-template> </ng-template> tag by angular de compiler
<ng-template>
<div [ngIf] = "someClass">
//some prop
</div>
</ng-template>
-we can write our own custom directives using @Input, tRef:templateRef, and vfRef: ViewContainerRef
-imp* we need to know the the name of our property should have same name as our selector
custom directives/Attribute directive
-------------------------------------
Directives without templates
ex: <p appSomeDirective>testing</p>
@Directive({
selector: '[appSomeDirective]'
})
export class AppSomeDirective {
//logic
}
COURSE PROJECTS
***************
Make sure, you do create that app by also adding the --no-strict flag to the ng new command - otherwise you will run into issues later on (we'll still dive into that "Strict Mode" later in the course of course, no worries)!
We'll also install the Bootstrap CSS Framework and in this course, we use version 3 of the framework. Install it via npm install --save bootstrap@3 => The @3 is important!
Additionally, when using a project created with Angular CLI 6+ (check via ng v ), you'll have an angular.json file instead of an .angular-cli.json file. In that file, you still need to add Bootstrap to the styles[] array as shown in the next video, but the path should be node_modules/bootstrap/dist/css/bootstrap.min.css , NOT ../node_modules/bootstrap/dist/css/bootstrap.min.css . The leading ../ must not be included.
Installing Bootstrap Correctly
------------------------------
1.cmd>npm install --save bootstrap
it will install bootstrap locally not globally
2.To make angular aware of our installed bootstrap
->go to angular.json
->paste path of bootstrap inside styles array
->C:\Users\Imam Hulagur\Desktop\JavascriptWorld\AngularWorld\Angular---The-Complete-Guide-2021-Edition-\my-first-app\node_modules\bootstrap\dist\css\bootstrap.min.css
->changes \(backward slashes) to /(forward slashes)
->cross verification
run application using >ng serve
go to inside Sources in developer tools and u ll see the installed bootstraps version insides style.css
Model
-----
just a typescript class, its a blueprint for objects that we create.
we wont provide any decorator for model class.
we will instantiate objects calling constructor using new.(use typescript shortcut for instantiation)
Debugging
---------
js supports source
source maps->which allows browser to translate js code-> typescript code or simply map the js code to ts code only development.
we can directly access our ts files(instead of searching in main.bundles.js sourcemap) under webpack:// > dot(.) > src as same structure as in out project.
In Production these source maps wont be available hence we cant be able to debug production code in browser i.e source code wont be available.
Section 5: Components & Databinding Deep Dive
***************************
components communication
------------------------
parent components
------------------
By Default all the properties of a component are only accessible inside that component from other components.
PC->CC - property binding - make it bindable from outside.
Then we can expose the properties using decorator @Input('aliasName')
aliasName ->if you want to output alias name prop outside of comp.
CC-> PC - event emit - make it listenable from outside
@Output() eventName = new EventEmitter();
this.eventName.emit(data);
(eventName)="methodName(@event)
two diff comp
------------
issue - chain of events
<-> app <->
| |
one comp events other components
View Encapsulation
------------------
shadow dom ->where each element had its own property/dom since this technique is not supported by all the browsers angular encapsulate such styles by giving strange attributes p[_ngContent-ejo-n] {color: blue}
we can override these encapsulation(remove these strange attributes getting added by angular to each elements)
@Component({
...
encapsulation: ViewEncapsulation.None(or Native or Emulated)
})
Native again uses Shadow DOM technique
Using local reference in template - without 2 way binding
---------------------------------
way of accessing html element in template and pass the element value by calling method to ts code.
Reference can be used only in template!, we cant in ts file. But we can pass it to ts.
can be placed on any HTML element, prefix with hash(#temp)
test: HTMLInputElement
temp will hold reference to whole HTML element not just the value.
@ViewChild()
-----------
Another way of getting access to element directly from within our ts code. since sometimes we actually want to access element before we call the method.
<p #serverContentInput></p>
Angular 8+
@ViewChild('nameOfLocalReference', {static: true}) nameOfLocalReference:elementRef
@ViewChild('ServerComponentInput', {static: true}) serverComponent
instead of Angular 6+
@ViewChild('ServerComponentInput') serverComponent
access->this.nameOfLocalReference.nativeElement.value
ng-content
----------
when i want to render complex html content of other template.
app.custom-comp.html
<ng-content></ng-content> -> it directive which serves as a hook in our template
app.component.html
<app-custom-comp>
//here you can insert that completed template instead of app.custom-comp.html
</app-custom-comp>
Angular life cycle hooks
------------------------
first constructor(){} gets called!
1.ngOnChanges(changes: SimpleChanges) -> called after input property changes
2.ngOnInit() -> called once the component is initialized (our properties has been initialized, object has been created)
it wil run after the constructor.
3.ngDoCheck() -> called during every change detection run.
4.ngAfterContentInit() -> called after content(ng-content) has been projected into view i.e some part of parent component template gets added to our component.
5.ngAfterContentChecked() -> called every time the projected content has been checked. runs when change detection detect the content changed.
6.ngAfterViewInit() ->called after components view(and child views) hab been initialized. after view get rendered.
7.ngAfterViewChecked() -> called every time the view(and child views) has been checked by change detection.
8. ngDestroy() -> called once the component about to destroyed. Best place for cleanup activity.
@ContentChild()
---------------
In Angular 8+, the @ContentChild()
Instead of:
@ContentChild('serverContentInput') serverContentInput: ElementRef;
use
In Angular 6
@ContentChild('serverContentInput', {static: true}) serverContentInput: ElementRef;
The same change (add { static: true } as a second argument) needs to be applied to ALL usages of @ViewChild() (and also @ContentChild() which you'll learn about later) IF you plan on accessing the selected element inside of ngOnInit().
If you DON'T access the selected element in ngOnInit (but anywhere else in your component), set static: false instead!
If you're using Angular 9+, you only need to add { static: true } (if needed) but not { static: false }.
Section 6: Course Project - Components & Databinding
****************************************************
Section 7: Directives Deep Dive
*******************************
we cant have more than one structural directives on same element.
creating our own attribute directive
>ng generate directive directiveName or >ng g d directiveName
@Directive({
selector: '[appBasicHighlight]'
})
export class BasicHighlightDirective {}
add BasicHighlightDirective to declaration: [] in app.module.ts
<p appBasicHighlight>Style me with basic highlight directive</p>
note: here we dont use [] to while calling directive, since we selecting as a attribute.
renderer
--------
we should not directly manipulate the DOM
this.elementRef.nativeElement.backgroundColor = 'green'
instead we need to make use of renderer
constructor(renderer: Renderer2, private elementRef: ElementRef){}
this.renderer.setStyle(this.elementRef.nativeElement, 'background-color', 'green')
In the last lecture, we used the Angular Renderer class to change the style of a HTML element. As explained in that lecture, you should use the Renderer for any DOM manipulations.
Of course, you can do more than simply change the styling of an element via setStyle()
https://angular.io/api/core/Renderer2
@HostListener() - reactive directive
--------------
since renderer style are static and are not interactive. To make renderer more interactive we use @HostListener()
@HostLister('mouseenter') mouseEnter(eventData: Event) {
this.renderer.setStyle(this.elementRef.nativeElement, 'background-color', 'green')
}
@HostBinding() - dont use render
-------------
easy way to manipulate DOM without using renderer
@HostBinding('style.backgroundColor') backgroundColor: string = 'transparent'
@HostListener('mouseenter') mouseEnter(eventData: Event) {
this.backgroundColor = 'green'
}
custom property binding
-----------------------
binding properties of a directive to make changes from outside.
@Input('alias') highlightColor: string = 'green';
@Input('alias') defaultColor: string = 'transparent';
structural directives behind the scenes(*)
-----------------------------------------
there is no * syntax in angular, behind the scenes angular needs to transform this * into something which is aware of.
before
<div *ngIf='testCondition'>
<p>testing...</p>>
</div>
after ->converts structural directive into property binding
<ng-content [ngIf]='testCondition'>
<p>testing...</p>
</ng-content>
custom structural directive implementation
------------------------------------------
make sure our property name and name of the directive must be similar
@Directive({
selector: 'ngIfCustom'
})
export NgIfCustom {
constructor(private templateRef: TemplateRef<any>,
private viewContainerRef: ViewContainerRef)
@Input() set ngIfCustom(condition: boolean) {
if(!condition) {
this.viewContainerRef.createEmbeddedView(templateRef);
} else {
this.viewContainerRef.clear();
}
}
}
make use of it in other component template like
<div *ngIfCustom='condition'>
</div>
ngSwitch
--------
instead of creating lot of ngIf conditions use ng switch.
if we want to show multiple templates based on certain conditional value.
8: Course Project - Directives
******************************
Section 9: Using Services & Dependency Injection
*************************************************
services - To avoid complex chain of event and property binding
- DRY{Don't Repeat Yourself}
- angular classes/business object which used to share the data, acts as centralized data outsourcing elements
- To avoid/reduce code redundancy/duplication
-centralized data management ex services in RxJS, Store concept in NgRx
-*while creating service we will not add any type of decorators
-*We should not create instances of services manually using new keyword, instead we need to inject it{so that angular will create services instances on the fly} inside constructor.
Thats the best practice.
-*The services which injected in app.module.ts providers[] share the data across all the components.
-*The highest possible level of injecting service is app.module.ts
Steps to create inject any service automatically by angular not manually.
-create service class
-inject it inside required components{that tells angular where we need the service}
-declare the service inside providers[] inside @component decorator.
-we can also declare from service class itself
@Injectable({
providedIn: 'root'
})
Hierarchical injection
----------------------
-The instances of services will only propagate downwards/child compo but not upwards/parent components
-*While the services injected inside app.components.ts providers[] will only share data with app and its child components.
we can access data using same service reference.
-*If you want to share the data as of parents components then just remove local services declaration inside providers[]
but not from constructor since angular need to know this present component need same data instead of
overriding data by creating other local component ref to injected service.
Injecting service into other service
------------------------------------
-for this its not possible in the component level,instead it should happen in app.module.ts i.e global level
-using decorator @Injectable() i.e the service to which module you want to inject not for the service which you are injecting.
-good practice always declare a service with @Injectable decorator since at least in future you may inject something to this service.
What happens if we wont use services
------------------------------------
-we need to emit even and listen to that emitted events using event binding
-we have pass the data to other components via property binding.
-To avoid we use services using the concept "Cross component communication" with event emitter.
Note : In Angular 6+ injecting services in app.module.ts can be done using below
@Injectable({
providedIn: 'root'
})
instead of providing it in app.module.ts like older way
@NgModule({
declaration:[],
import:[],
bootstrap[AppComponent],
providers:[
desiredService
]
})
Services in Angular 6+
----------------------
If you're using Angular 6+ (check your package.json to find out), you can provide application-wide services in a different way.
Instead of adding a service class to the providers[] array in AppModule , you can set the following config in @Injectable() :
@Injectable({providedIn: 'root'})
export class MyService { ... }
This is exactly the same as:
export class MyService { ... }
and
import { MyService } from './path/to/my.service';
@NgModule({
...
providers: [MyService]
})
export class AppModule { ... }
Using this new syntax is completely optional, the traditional syntax (using providers[] ) will still work. The "new syntax" does offer one advantage though: Services can be loaded lazily by Angular (behind the scenes) and redundant code can be removed automatically. This can lead to a better performance and loading speed - though this really only kicks in for bigger services and apps in general.
@Inject()
OtherServiceName
To inject other service into this service
Section 10: Course Project - Services & Dependency Injection
************************************************************
Section 11: Changing Pages with Routing
***************************************
To make user look like they are shifting from one page to another, but in reality
we are loading single page with different component template based dynamic routing.
instead of calling statically from HTML templates of other components.
app.model.ts is a good place to inform angular about the routes of our application.
each route is just a javascript object inside appRoutes: Routes[]
which will accept path and components as a keys
add 'RouterModule' inside @NgModule and the register appRoutes to RouterModule inside imports.
To inform angular where to put our routes, use <router-outlet/>(this will mark the place in our document where we want angular to load currently loaded compo)
put your routes values into special type of directive called routeLink = ['routeName']
Absolute path - with '/' in the beginning, which will always gets appended to root domain.
with './ or ../' will go back one path, or got back another path.
Active route style - dynamically
--------------------------------
if we provide routes inside href='' it will restart our app.
The angular provided an directive, routerLinkActive=""
for empty path i.e /nothing, is always present in all the path so to avoid this
we need to use directive - routerLinkActiveOptions
this need [] because just can not pass JS object directly to directive, so we need to wrap it property binding using[];
ex: [routerLinkActiveOptions] = "{exact : true}"
Navigate Programmatically
--------------------------
If you want to redirect to any other page in other components
1.first inject the Router
2.use this.Router.navigate(['pathName'], {relativeTo: this.route}) method
Relative path -> starts without '/' ->it will append the path to current path.
Absolute path -> start with '/absolutePath' will come back to base directory and then add absolutePath.
passing parameters to route
---------------------------
/: will tell router that 'this is our dynamic part of the route'
Fetching route parameters
------------------------
inject Activated router
Since it had lot of meta data related to route, it will give access to id passed in the URL of selected user using
snapshot property i.e this.router.snapshot.params['id'];
Fetching route parameters reactively.
-----------------------------------
Angular would not create/render new component and destroy the old if you are already on same component(because of performance issue)
Still if you need to update the current component with update values you can do it by following
we need a different approach instead of snapshot(which is for initial routing purpose)
need to use 'param' observable, since you dont know when, if or how long it will take to change url change.
we cant block our routing. So need to subscribe to observable.
observable is 3rd party packages(rxjs) which are used to perform asynchronous task, without having to wait for the task which might happen in future.
ex : this.router.params
.subscribe(
(params: Params)=> {
this.user.id = params['id'];
this.user.name = params['name'];
}
)
Now this trigger only when there is changes in url/params
Important note about observables
---------------------------------
Observable will not be destroyed even if your component get destroyed from the memory if you moved to other component.
So we need to unsubscribe observable in onDestroy() of angular.
create subscription object
paramSubscription: Subscription;
unsubscribe inside ngDestroy()
this.paramSubscription.unsubscribe();
passing query parameter and fragments
-------------------------------------
{ path: "servers/:id/edit", component: EditServerComponent}
[routerLink] = "['/servers', 5, 'edit']" //http://localhost:4200/servers/5/edit
?key=value&key2=value2
[queryParams] = "{allowEdit : '1'}" //http://localhost:4200/servers/5/edit?allowEdit=1
#loading - route with extra information
[fragment] = "'loading'" //http://localhost:4200/servers/5/edit?allowEdit=1#loading
since fragment directive string as argument not JS, we can also write as
fragment = "loading"
Programmatically
this.router.navigate(['./servers', id, 'edit'], {queryParams: { allowEdit: '1'}, fragment: 'loading'});
(click) = "onLoadServer(1)"
Retrieving queryParams and fragments
------------------------------------
inject route: ActivatedRouter
console.log(this.route.snapshot.queryParams);
console.log(this.route.snapshot.fragment);
this.route.queryParams.subscribe();
this.route.fragment.subscribe();
Practicing some common gotchas
------------------------------
this.route.snapshot.queryParams(['id']) -> will return string, convert it to number by adding + in front of it.
nested/child routing
--------------------
const appRoutes:Routes = [
{
path: 'parent', component: 'ParentsComponent', children: [
{ path: ':id', component: 'ParentComponent'},
{path: ':id/edit', component: 'ParentEditComponent'}
]
}
]
if only nest we get error
the <router-outlet></router-outlet> is only available to top level routes
child routes also needs a separate outlet, because they simply cant override, instead they should be loaded nested.
<router-outlet></router-outlet>//this will be new route, which will use on all child routes.
configuring handling of queryParams
-----------------------------------
<a [queryParams] = '{allowEdit: server.id === 3 ? '1' : '0'}>{{server.name}}</a>
onEdit() {
this.router.navigate(['edit'], {relativeTo: this.route})
}
how to preserve the queryParams when we visit other component, by making sure old ones are kept.
queryParamsHandling: 'merge'/'preserve - to make sure, we dont loose param info we have before.
onEdit() {
this.router.navigate(['edit'], {relativeTo: this.route, queryParamsHandling: 'preserve'})
}
redirecting and wildcard routing and redirectTo
-----------------------------------------------
what if user enters un handled url/routes
create separate component to handle such kind of scenarios
{ path: 'not-found', component: PageNotFoundComponent },
{ path: 'something', redirectTo: '/not-found'}
wildcard route(**)
That mean catch all possible routes which are unknown to Angular and redirect to mentioned component.
The order is super important here -
If it was in the beginning, you will always redirect to '/not-found'
make sure it should be last, because our array gets parsed from top to bottom.
'pathMatch' ing while redirecting
--------------------------------
By default, Angular matches paths by prefix. That means, that the following route will match both /recipes and just /
{ path: '', redirectTo: '/somewhere-else' }
Actually, Angular will give you an error here, because that's a common gotcha: This route will now ALWAYS redirect you! Why?
Since the default matching strategy is "prefix" , Angular checks if the path you entered in the URL does start with the path specified in the route. Of course every path starts with '' (Important: That's no whitespace, it's simply "nothing").
To fix this behavior, you need to change the matching strategy to "full" :
{ path: '', redirectTo: '/somewhere-else', pathMatch: 'full' }
Now, you only get redirected, if the full path is '' (so only if you got NO other content in your path in this example).
Creating separate routing Module -> outsourcing the route configuration
-----------------------------------------------------------------------
Typically if you have more than 2 or 3 route configuration i.e if route configuration taking much space in app.module.ts
we need to add them in new file for whole application i.e app-routing.module.ts
No need to add Declaration[], since they all declared inside app.module.ts otherwise we ll get error.
*we need to export/outsource these routes from app.routing.module.ts
exports: [
RouterModule
]
to add in imports[AppRoutingModule] of app.module.ts
imports:[AppRoutingModule]
Route Guards
************
Functionality, logic or code executed before loading of route.
Manually checking this in NgOnInit(){} of a particular component will be very cumbersome.
so need to use angular feature called angular router.
protecting routes with canActivate
----------------------------------
->create service, which implements CanActivate interface
(it will forces us to implements and override canActivate() compulsory)
it wil take two input ActivatedRouteSnapshot, RouterStateSnapshot
it return observable<boolean> alternatively it can return promise<boolean> or boolean
it can run synchronously and also asynchronous.
create fake auth service since we have not implemented login functionality
inject auth service into auth guard, to do we need to make auth guard as injectable
if user is authenticated then return true, or else we should not allow user to access that page
redirect him to other page by injecting router and navigate.
->To use created guard
We need to found out which route should be protected by this guard(ex: here servers compo)
add canActivate[] property, it will take all the guard to which it should apply to all teh child compo.
and add both services namely authService, AuthGuard to providers[] of app.module.ts
protecting only child/nested routes with canActivateChild
----------------------------------------------------------
implement new interface canActivateChild in AuthGuard
just declare method call back canActivate method, now we can write different hook in our routes i.e canActivateChild
Now we can protect single route(using canActivate) and child routes(using canActivateChild) inside app.module
Controlling navigation with canDeactivate
-----------------------------------------
when user accidentally navigating on other tabs need ask do need to save current changes.
create one flag to keep track of changes
changesSaved = false;
As soon as update one level up and ask.
this.changesSaved = true;
this.router.navigate(['../'], {relativeTo: this.route})
create a can-deactivate-guard.service.ts since a guard should always be service.
import { ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router";
import { Observable } from "rxjs";
export interface CanComponentDeactivate {
//since interface contains only method declaration which need to taken care by child classes
//it had only one method, canDeactivate without any parameters
canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}
//write our class now by implementing above interface
export class canDeactivateGuard implements canDeactivate<CanComponentDeactivate> {
//this is method will be called by angular router once user tries to navigate to other component.
// ?: is an optional argument
canDeactivate(component: CanComponentDeactivate,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState?: RouterStateSnapshot
): Observable<boolean> | Promise<boolean> | boolean {
return component.canDeactivate();
}
}
define it inside providers[] in app.module
providers: [ServersService, AuthService, AuthGuard,canDeactivateGuard],
implement it in our own compo
canDeactivate():Observable<boolean> | Promise<boolean> | boolean {
if(!this.allowEdit) {
return true;
}
if((this.serverName !== this.server.name || this.serverStatus !== this.server.status) && !this.changesSaved) {
return confirm('Do you want to discard the changes?');
} else {
return true;
}
}
passing a static data to Route
------------------------------
even though we are receiving data or how to get param out of route URL,
still some of our routes depend on your data they received statically(each time they have loaded) or dynamically
for example to display particular error message
crete error page
ng g c error-page
go to that route, define your own data object
{ path: 'not-found', component: ErrorPageComponent, data: {message: 'Page not found!'} },
subscribe assign and make use it of it in html.
ngOnInit(): void {
//this.errorMessage = this.route.snapshot.data['message'];
this.route.data.subscribe(
(data: Data)=> {
this.errorMessage = data['message'];
}
)
}
passing a dynamic data to Route/ Resolving Dynamic data with resolve Guard.
---------------------------------------------------------------------------
if i want to load from backend, so we need a resolver.
resolver
its an service similar to canActivate and canDeactivate will help us run some code before a route is rendered.
create resolver server
ex server-resolver.service.ts
it should need to implement Resolve interface.
interface Server {
id: number,
name: string,
status: string
}
//here we want to inject ServersService into ServerResolver service so @Injectable()
@Injectable()
export class ServerResolver implements Resolve<{id: number, name: string, status: string}> {
constructor(private serversService: ServersService) {}
resolve(route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<Server> |Promise<Server> | Server {
//inject ServersService, reach out to getServer
return this.serversService.getServer(+route.params['id']);
}
}
config the route with resolver
{
path: ":id",
component: ServerComponent,
resolve: {server: ServerResolver}
},
import resolver server in app module
providers: [ServersService, AuthService, AuthGuard,canDeactivateGuard, ServerResolver],
subscribe to resolver and access asynchronous data from server
this.route.data
.subscribe(
(data: Data)=> {
this.server = data['server'];//make sure the property name should match the one which you have provided in route resolver config
}
);
Understanding the location strategies(when you are hosting on real server)
--------------------------------------------------------------------------
it works fine here in our local setup, this is not something we should taken for granted.
if we have route like ourDomain/
if we are hosting it in real server in web this might not work out of the box.
because there the route are always parsed/handled by the server which host our application.
but here in our local environment i.e in our development environment we are using development sever which has one special configuration than real life server also has to have.
The server hosting our SPA/single page application has to be configured such that in case of 404 error it return index.html file starting an containing our angular app. why..?
because all our URLs are parsed by the server first(not by the a angular)
Now if we have /servers here, it will look for /servers on real server on web.
now chances are we dont really have /servers route here, we only have only one file index.html containing our angular app.
and we want our angular to take over and parse these routes, but it will never get a chance if our real server decide no i dont know this route, here is your 404 error page.
So in such cases we need to make sure your real web server need to return index.html file.
If for some reason we can get to work or we need to support very old browsers which are not able to parse like this in the client in which angular does than.
we have an alternate approach using these nice url in web, we need to use older technique which was used in couple of year ago.
using # signs in our routes i.e hash mode routing
we can enable it in app-routing.module
RouterModule.forRoot(appRoutes, {useHash: true})
it will inform our real web server that, hey only care about the part before this # key.
all the part after # can be ignored by our web server.
therefore it can run on web server even if its not returning index.html file.
After # tag can be parsed by our client i,e by Angular.
Routing - Course project
************************
@NgModule({}) - will added to convert normal typescript class into angular module, which take JS object.
The order of the route place should following
first static routes
The routes with dynamic params
ex
{
path: 'new', component: RecipeEditComponent // /recipe/new
},
{
path: ':id', component: RecipeDetailsComponent
},
*The Observable which we have created should need to unsubscribe manually inside onDestroy()
The Observable which angular created will be gets unsubscribed automatically
Section 13: Understanding Observables
*************************************
Understanding Observable
------------------------
Its a data source(Events, Http Requests, Triggers) in a code.
its from 3rd party package rxjs, need to import it from rxjs.
It work as following
OBSERVABLE
---------------------------------------------------------> in between we have stream/timeline, we will get data packets/triggers
Handle data Handle error Handle completion We have 3 ways to handle data
---------------------------------------------------------> in between we have stream/timeline, we will get data packets/triggers
OBSERVER(our code)/subscriber
An observable does not have to complete like observable for button click.
But some of the observable will complete like Observable for Http Requests.
We will use these observables to handle asynchronous tasks.
since we dont know when they will happen.
we dont know how long will it take.
So if we are executing normal code, we should not to wait for these event response/completion. bcz that will block our program.
we can use call back or Promises, but Observable the just diff approach to handling async's. also angular embraces to use lot.
Observable have one major advantage i.e Operators.
creating our own observable
----------------------------
import interval(utility function) method from rxjs
subscribe to it ngOnInit method, print into console.
to prevent memory leak, we need to stop it by unsubscribing.
to do this, first we need to store that subscription of type Subscription variable.
inside onDestroy method just unsubscribe it.
For the observable provided by angular for us like params will be taken care by angular.
building custom observable
-------------------------
ngOnInit() {
this.firstObsSubscription = interval(1000)
.subscribe(count => {
console.log(count);
})
}
angular wont unsubscribe custom observables unlike its builtin, we need to do inside onDestroy() to avoid memory leaks.
onDestroy() {
this.firstObsSubscription.unsubscribe();
}
create observable using crate()
-----------------------------
Here observer is an listen we need to inform observe about the subscription.
ngOnInit() {
const customObservable = Observable.create(observer => {6
let count = 0;
setInterval(() => {
observer.next(count);//observer.error(), observer.complete()
count++;
}, 1000)
});
//subscribe that custom created observable
this.firstObsSubscription = customObservable.subscribe( data => {
console.log(data);
});
}
error and completion
--------------------
error
fake an error as of now, but in real time we will get from api call error.
observer.error(new Error('Counter greater than 3!'));
Handle it 2nd arg fun with error message as an arg
this.firstObsSubscription = customObservable.subscribe( data => {
console.log(data);
}, error => {
console.log(error);
});
complete -> after this observable halts there are no other values gets emitted. even though there is error condition after it.
check the condition
if(count === 2) {
observer.complete();
}
write 3rd arg clear fun without any arg
()=> {
console.log('Counter completed!');
}
Operators(https://www.learnrxjs.io/learn-rxjs/operators)
---------------------------------------------------------
To change the format of data.
we can call them using a pipe() method. every observable has this method.
now we will import Operators from rxjs/Operators lib not from observable, thousands of Operators are there.
ex pipe(), map()
note: import { map } from 'rxjs/operators'
first customize
customObservable.pipe(map(
(data: number) => {
//customize the data format which you needed.
return 'Round ' + (data + 1);
}));
//subscribe that custom created observable
this.firstObsSubscription = customObservable.subscribe( data => {
console.log(data);
}, error => {
console.log(error);
}, ()=> {
console.log('Counter completed!');
});
replace it.
//subscribe that custom created observable
this.firstObsSubscription = customObservable.pipe(map(
(data: number) => {
//customize the data format which you needed.
return 'Round ' + (data + 1);
}));.subscribe( data => {
console.log(data);
}, error => {
console.log(error);
}, ()=> {
console.log('Counter completed!');
});
using filter
since pipe() will take long list of operator, we can send filter a 1nd argument and map() 2nd.
filter( data => {
return data > 0;// it will return boolean based on condition, based on this next method execution takes place.
})
Subject(Dont use EventEmitter use Subject)
*****************************************
we can create/show something in other component when we click button.
This is used achieve by using EventEmitter.
inside EventEmitter we used to emit using function emit()
ex : inside app component
<p *ngIf="userActivated"> Activate</p>
ngOnInit() {
this.userService.activatedEmitter.subscribe(didActivated => {
this.userActivated = didActivated;
})
}
inside user compo
<button class="btn btn-primary" (click)="onActivate()">Active</button>
onActivate() {
this.userService.activatedEmitter.emit(true);
}
create one service to emit event
import { EventEmitter, Injectable } from "@angular/core";
@Injectable({
providedIn: 'root' //This is the new way of declaring service inside app.module.ts providedIn[]
})
export class UserService {
activatedEmitter = new EventEmitter<boolean>();
}
But now we achieve that by using Subject.
create Subject inside user service
activatedEmitter = new Subject<boolean>();
call that Subject from user compo ts using 'next()'
onActivate() {
this.userService.activatedEmitter.next(true);
}
but in Subject we make use of function next()
In observables we used to call next() inside subscription function only.
but next() of Subject we can call even from outside function.
just like our own observables we need to unsubscribe Subject also.
Subject is only recommended while you are listening to 'cross component' events
*To listen to event in same component i.e using @Output() we again need to use EventEmitter instead of Subject.
https://rxjs.dev/guide/overview
Section 14: Course Project - Observables
****************************************
Section 15: Handling Forms in Angular App
*****************************************
form is something which we used to submit to server with action or method, but here since Angular is SPA there
no submitting to server instead we will need to handle the form through Angular.
and the if you want to submit to server, we need to reach out via Angular Https service.
Why do we need Angular in Forms
-------------------------------
normal HTML form -> Angular givers us JS object representation of form making it simple for us to retrieve user values.
to see the state of form and to work with it.
Also it will store some meta data of form.
JS Object representation of a FORM
Normal HTML form
<form>
<label>Name</label>
<input type="text" name="name">
<label>mail</label>
<input type="mail" name="mail">
</form>
Angular provide create JS object for us including some meta data
{
value: {
name: 'imam',
mail: 'test@gmail.com'
}
//other meta data
valid: true,
controls: {}
}
2 ways handling HTML form
Template Driven(TD) Forms and Reactive Forms
********************************************
Template Driven(TD)
-------------------
Here we simply setup the form in template, Angular will automatically infer the Form Object from the DOM.
Reactive/Complex approach
-------------------------
Here we actually define the structure of our form i.e form is created Programmatically manually connect with HTML
and the it will synchronized with DOM.
Here we can fine tune each and every little piece of our form.
TD: creating forms and registering controls
---------------------------------------
no action='' and method attributes on form since we dont want to submit form as soon as user clicks on submit instead angular should handle this form.