Skip to content

Commit aa77103

Browse files
committed
Add ngPureFor directive sub-classing in Angular 7.2.13.
1 parent fbacdf7 commit aa77103

24 files changed

Lines changed: 7559 additions & 0 deletions

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ with.
1010

1111
## My JavaScript Demos - I Love JavaScript!
1212

13+
* [Sub-Classing NgForOf In Order To Make It A "Pure" Directive In Angular 7.2.13](https://bennadel.github.io/JavaScript-Demos/demos/ng-pure-for-angular7/)
1314
* [@Directive().inputs And @Input() Are Not Functionally Equivalent In Angular 7.2.13](https://bennadel.github.io/JavaScript-Demos/demos/input-binding-notation-angular7/)
1415
* [Quick Reference For NgModel Values And Template-Driven Forms In Angular 7.2.13](https://bennadel.github.io/JavaScript-Demos/demos/form-controls-reference-angular7/)
1516
* [Rendering A List Of Mixed Components Using NgFor And NgSwitch In Angular 7.2.13](https://bennadel.github.io/JavaScript-Demos/demos/ng-for-mixed-types-angular7/)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
# Now that we're using Webpack, we can install modules locally and just ignore
3+
# them since the assets are baked into the compiled modules.
4+
node_modules/
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
:host {
3+
display: block ;
4+
font-size: 18px ;
5+
}
6+
7+
a {
8+
color: red ;
9+
cursor: pointer ;
10+
text-decoration: underline ;
11+
user-select: none ;
12+
-moz-user-select: none ;
13+
-webkit-user-select: none ;
14+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
2+
// Import the core angular services.
3+
import { Component } from "@angular/core";
4+
5+
// ----------------------------------------------------------------------------------- //
6+
// ----------------------------------------------------------------------------------- //
7+
8+
interface User {
9+
id: number;
10+
name: string;
11+
}
12+
13+
@Component({
14+
selector: "my-app",
15+
styleUrls: [ "./app.component.less" ],
16+
template:
17+
`
18+
<p>
19+
<a (click)="swapItems()">Swap Items</a>
20+
&mdash;
21+
<a (click)="swapCollection()">Swap Collection</a>
22+
</p>
23+
24+
<ul>
25+
<li *ngPureFor="let user of users">
26+
{{ user.id }} - {{ user.name }}
27+
</li>
28+
</ul>
29+
`
30+
})
31+
export class AppComponent {
32+
33+
public users: User[];
34+
35+
// I initialize the app component.
36+
constructor() {
37+
38+
this.users = this.generateUsers();
39+
40+
}
41+
42+
// ---
43+
// PUBLIC METHODS.
44+
// ---
45+
46+
// I keep the same data, but swap the top-level collection reference.
47+
public swapCollection() : void {
48+
49+
this.users = this.users.slice();
50+
51+
}
52+
53+
54+
// I keep the same top-level collection reference, but swap the low-level item
55+
// references. As the items are swapped, the IDs are incremented so that we can see
56+
// if the DOM nodes are being updated in the View.
57+
public swapItems() : void {
58+
59+
for ( var i = 0, length = this.users.length ; i < length ; i++ ) {
60+
61+
var user = this.users[ i ];
62+
63+
// Mutate the users collection, swapping in a new item at the current index.
64+
this.users[ i ] = {
65+
id: ( user.id + 1 ),
66+
name: user.name
67+
};
68+
69+
}
70+
71+
}
72+
73+
// ---
74+
// PRIVATE METHODS.
75+
// ---
76+
77+
// I generate the collection of users using the given size.
78+
private generateUsers( count: number = 10 ) : User[] {
79+
80+
var users: User[] = [];
81+
82+
for ( var i = 0 ; i < count ; i++ ) {
83+
84+
users.push({
85+
id: i,
86+
name: `User ${ i }`
87+
});
88+
89+
}
90+
91+
return( users );
92+
93+
}
94+
95+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
// Import the core angular services.
3+
import { BrowserModule } from "@angular/platform-browser";
4+
import { NgModule } from "@angular/core";
5+
6+
// Import the application components and services.
7+
import { AppComponent } from "./app.component";
8+
import { NgPureForDirective } from "./ng-pure-for.directive";
9+
10+
// ----------------------------------------------------------------------------------- //
11+
// ----------------------------------------------------------------------------------- //
12+
13+
@NgModule({
14+
imports: [
15+
BrowserModule
16+
],
17+
declarations: [
18+
AppComponent,
19+
NgPureForDirective
20+
],
21+
bootstrap: [
22+
AppComponent
23+
]
24+
})
25+
export class AppModule {
26+
// ...
27+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
6+
<title>
7+
Sub-Classing NgForOf In Order To Make It A "Pure" Directive In Angular 7.2.13
8+
</title>
9+
</head>
10+
<body>
11+
12+
<h1>
13+
Sub-Classing NgForOf In Order To Make It A "Pure" Directive In Angular 7.2.13
14+
</h1>
15+
16+
<my-app>
17+
<p>
18+
<em>Loading files...</em>
19+
</p>
20+
21+
<p>
22+
npm Run Scripts:
23+
</p>
24+
25+
<ul>
26+
<li>
27+
<strong>npm run build</strong> &mdash; Compiles the .ts file into bundles.
28+
</li>
29+
<li>
30+
<strong>npm run watch</strong> &mdash; Compiles the .ts file into bundles and then watches files for changes.
31+
</li>
32+
</ul>
33+
</my-app>
34+
35+
</body>
36+
</html>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
// Import these libraries for their side-effects.
3+
import "core-js/es";
4+
import "zone.js/dist/zone.js";
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
// Import the core angular services.
3+
import { enableProdMode } from "@angular/core";
4+
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
5+
6+
// Import the root module for bootstrapping.
7+
import { AppModule } from "./app.module";
8+
9+
// ----------------------------------------------------------------------------------- //
10+
// ----------------------------------------------------------------------------------- //
11+
12+
// NOTE: This ENV value is being provided by Webpack's DefinePlugin. It is populated
13+
// on the mode in which the webpack-cli was invoked.
14+
if ( process.env.NODE_ENV === "production" ) {
15+
16+
enableProdMode();
17+
18+
}
19+
20+
platformBrowserDynamic().bootstrapModule( AppModule );
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
2+
// Import the core angular services.
3+
import { Directive } from "@angular/core";
4+
import { DoCheck } from "@angular/core";
5+
import { Input } from "@angular/core";
6+
import { NgForOf } from "@angular/common";
7+
import { NgForOfContext } from "@angular/common";
8+
import { NgIterable } from "@angular/core";
9+
import { OnChanges } from "@angular/core";
10+
import { TemplateRef } from "@angular/core";
11+
import { TrackByFunction } from "@angular/core";
12+
13+
// ----------------------------------------------------------------------------------- //
14+
// ----------------------------------------------------------------------------------- //
15+
16+
@Directive({
17+
selector: "[ngPureFor][ngPureForOf]"
18+
})
19+
export class NgPureForDirective<T> extends NgForOf<T> implements OnChanges, DoCheck {
20+
21+
// This directive is really a very very very thin wrapper around the existing NgForOf
22+
// directive. It's using all the inherited functionality; only, it needs to alias
23+
// the input bindings since we're using a slightly different selector "ngPureFor".
24+
// And, since the native NgForOf directive uses the @Input() decorator, we need to
25+
// use it as well in order to setup the aliases.
26+
27+
@Input( "ngPureForOf" )
28+
public ngForOf!: NgIterable<T>;
29+
30+
@Input( "ngPureForTrackBy" )
31+
public ngForTrackBy!: TrackByFunction<T>;
32+
33+
@Input( "ngPureForTemplate" )
34+
public ngForTemplate!: TemplateRef<NgForOfContext<T>>;
35+
36+
// ---
37+
// PUBLIC METHODS.
38+
// ---
39+
40+
// I get called whenever change-detection is triggered.
41+
public ngDoCheck() : void {
42+
43+
// In the native NgForOf directive, the Differ for the NgForOf collection is
44+
// checked on every change-detection digest. However, since we're turning this
45+
// into a "pure" directive (so to speak), we want to override the ngDoCheck()
46+
// method such that IT DOES NO WORK during arbitrary change-detection digests.
47+
48+
}
49+
50+
51+
// I get called whenever one of the input bindings is changed.
52+
public ngOnChanges() : void {
53+
54+
// The ngOnChanges() method will be called if the ngForOf, ngForTrackBy, or
55+
// ngForTemplate input bindings get changed. Since this is when we want to
56+
// actually perform our internal change-detection test, we can now turn around
57+
// and call the inherited ngDoCheck() method (on SUPER). This will inspect the
58+
// Differ and update the template rendering.
59+
super.ngDoCheck();
60+
61+
}
62+
63+
}

demos/ng-pure-for-angular7/build/main.31946230d12dc89f2c34.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)