Skip to content

Commit 64db486

Browse files
jelbournalxhub
authored andcommitted
build: add rules for generating block/element API data (angular#52480)
Adds build rules for "artificially" generating `DocEntry` collections for block and element APIs. The two rules are very similar, but _just_ different enough that it's worth having two separate implementations. PR Close angular#52480
1 parent a3abe16 commit 64db486

18 files changed

+613
-3
lines changed

.pullapprove.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1168,10 +1168,11 @@ groups:
11681168
'tools/esm-interop/**/{*,.*}',
11691169
'tools/gulp-tasks/**/{*,.*}',
11701170
'tools/legacy-saucelabs/**/{*,.*}',
1171+
'tools/manual_api_docs/**/{*,.*}',
11711172
'tools/npm-patches/**/{*,.*}',
11721173
'tools/rxjs/**/{*,.*}',
1173-
'tools/saucelabs/**/{*,.*}',
11741174
'tools/saucelabs-daemon/**/{*,.*}',
1175+
'tools/saucelabs/**/{*,.*}',
11751176
'tools/symbol-extractor/**/{*,.*}',
11761177
'tools/testing/**/{*,.*}',
11771178
'tools/tslint/**/{*,.*}',

tools/manual_api_docs/BUILD.bazel

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
load("//tools:defaults.bzl", "nodejs_binary", "ts_library")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
ts_library(
6+
name = "generate_element_api_json_lib",
7+
srcs = ["generate_element_api_json.ts"],
8+
deps = [
9+
"//packages/compiler-cli",
10+
"@npm//@types/node",
11+
],
12+
)
13+
14+
nodejs_binary(
15+
name = "generate_element_api_json",
16+
data = [
17+
":generate_element_api_json_lib",
18+
],
19+
entry_point = ":generate_element_api_json.ts",
20+
)
21+
22+
ts_library(
23+
name = "generate_block_api_json_lib",
24+
srcs = ["generate_block_api_json.ts"],
25+
deps = [
26+
"//packages/compiler-cli",
27+
"@npm//@types/node",
28+
],
29+
)
30+
31+
nodejs_binary(
32+
name = "generate_block_api_json",
33+
data = [
34+
":generate_block_api_json_lib",
35+
],
36+
entry_point = ":generate_block_api_json.ts",
37+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
load("//tools/manual_api_docs:generate_block_api_json.bzl", "generate_block_api_json")
2+
3+
generate_block_api_json(
4+
name = "blocks",
5+
srcs = glob(["*.md"]),
6+
)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
A type of [block](api/core/defer) that can be used to defer load the JavaScript for components,
2+
directives and pipes used inside a component template.
3+
4+
## Syntax
5+
6+
```html
7+
@defer ( on <trigger>; when <condition>; prefetch on <trigger>; prefetch when <condition> ) {
8+
<!-- deferred template fragment -->
9+
<calendar-cmp />
10+
} @placeholder ( minimum? <duration> ) {
11+
<!-- placeholder template fragment -->
12+
<p>Placeholder</p>
13+
} @loading ( minimum? <duration>; after? <duration> ) {
14+
<!-- loading template fragment -->
15+
<img alt="loading image" src="loading.gif" />
16+
} @error {
17+
<!-- error template fragment -->
18+
<p>An loading error occured</p>
19+
}
20+
```
21+
22+
## Description
23+
24+
### Blocks
25+
26+
Supported sections of a defer block. Note: only the @defer block template fragment is deferred
27+
loaded. The remaining optional blocks are eagerly loaded.
28+
29+
| block | Description |
30+
|----------------|----------------------------------------------------------|
31+
| `@defer` | The defer loaded block of content |
32+
| `@placeholder` | Content shown prior to defer loading (Optional) |
33+
| `@loading` | Content shown during defer loading (Optional) |
34+
| `@error` | Content shown when defer loading errors occur (Optional) |
35+
36+
<h3>Triggers</h3>
37+
38+
Triggers provide conditions for when defer loading occurs. Some allow a template reference variable
39+
as an optional parameter. Separate multiple triggers with a semicolon.
40+
41+
| trigger | Triggers... |
42+
|---------------------------------|-----------------------------------------------|
43+
| `on idle` | when the browser reports idle state (default) |
44+
| `on viewport(<elementRef>?)` | when the element enters the viewport |
45+
| `on interaction(<elementRef>?)` | when clicked, touched, or focused |
46+
| `on hover(<elementRef>?)` | when element has been hovered |
47+
| `on immediate` | when the page finishes rendering |
48+
| `on timer(<duration>)` | after a specific timeout |
49+
| `when <condition>` | on a custom condition |
50+
51+
<h2>Prefetch</h2>
52+
53+
Configures prefetching of the defer block used in the `@defer` parameters, but does not affect
54+
rendering. Rendering is handled by the standard `on` and `when` conditions. Separate multiple
55+
prefetch configurations with a semicolon.
56+
57+
```html
58+
@defer (prefetch on <trigger>; prefetch when <condition>) {
59+
<!-- deferred template fragment -->
60+
}
61+
```
62+
63+
Learn more in the [defer loading guide](guide/defer).
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
The `@for` block repeatedly renders content of a block for each item in a collection.
2+
3+
## Syntax
4+
5+
```html
6+
@for (item of items; track item.name) {
7+
<li> {{ item.name }} </li>
8+
} @empty {
9+
<li> There are no items. </li>
10+
}
11+
```
12+
13+
## Description
14+
15+
The `@for` block renders its content in response to changes in a collection. Collections can be any
16+
JavaScript [iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols),
17+
but there are performance advantages of using a regular `Array`.
18+
19+
You can optionally include an `@empty` section immediately after the `@for` block content. The
20+
content of the `@empty` block displays when there are no items.
21+
22+
<h3> track and objects identity </h3>
23+
24+
The value of the `track` expression determines a key used to associate array items with the views in
25+
the DOM. Having clear indication of the item identity allows Angular to execute a minimal set of DOM
26+
operations as items are added, removed or moved in a collection.
27+
28+
Loops over immutable data without `trackBy` as one of the most common causes for performance issues
29+
across Angular applications. Because of the potential for poor performance, the `track` expression
30+
is required for the `@for` loops. When in doubt, using `track $index` is a good default.
31+
32+
<h3> `$index` and other contextual variables </h3>
33+
34+
Inside `@for` contents, several implicit variables are always available:
35+
36+
| Variable | Meaning |
37+
| -------- | ------- |
38+
| `$count` | Number of items in a collection iterated over |
39+
| `$index` | Index of the current row |
40+
| `$first` | Whether the current row is the first row |
41+
| `$last` | Whether the current row is the last row |
42+
| `$even` | Whether the current row index is even |
43+
| `$odd` | Whether the current row index is odd |
44+
45+
These variables are always available with these names, but can be aliased via a `let` segment:
46+
47+
```html
48+
@for (item of items; track item.id; let idx = $index, e = $even) {
49+
Item #{{ idx }}: {{ item.name }}
50+
}
51+
```
52+
53+
The aliasing is especially useful in case of using nested `@for` blocks where contextual variable
54+
names could collide.

tools/manual_api_docs/blocks/if.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
The `@if` block conditionally displays its content when its condition expression is truthy.
2+
3+
## Syntax
4+
5+
```html
6+
@if (a > b) {
7+
{{a}} is greater than {{b}}
8+
} @else if (b > a) {
9+
{{a}} is less than {{b}}
10+
} @else {
11+
{{a}} is equal to {{b}}
12+
}
13+
```
14+
15+
## Description
16+
17+
Content is added and removed from the DOM based on the evaluation of conditional expressions in
18+
the `@if` and `@else` blocks.
19+
20+
The built-in `@if` supports referencing of expression results to keep a solution for common coding
21+
patterns:
22+
23+
```html
24+
@if (users$ | async; as users) {
25+
{{ users.length }}
26+
}
27+
```
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
The `@switch` block is inspired by the JavaScript `switch` statement:
2+
3+
## Syntax
4+
5+
```html
6+
@switch (condition) {
7+
@case (caseA) {
8+
Case A.
9+
}
10+
@case (caseB) {
11+
Case B.
12+
}
13+
@default {
14+
Default case.
15+
}
16+
}
17+
```
18+
19+
## Description
20+
21+
The `@switch` blocks displays content selected by one of the cases matching against the conditional
22+
expression. The value of the conditional expression is compared to the case expression using
23+
the `===` operator.
24+
25+
The `@default` block is optional and can be omitted. If no `@case` matches the expression and there
26+
is no `@default` block, nothing is shown.
27+
28+
**`@switch` does not have fallthrough**, so you do not need an equivalent to a `break` or `return`
29+
statement.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
load("//tools/manual_api_docs:generate_element_api_json.bzl", "generate_element_api_json")
2+
3+
generate_element_api_json(
4+
name = "elements",
5+
srcs = glob(["*.md"]),
6+
)
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
A special element that can hold structural directives without adding new elements to the DOM.
2+
3+
The `<ng-container>` allows us to use structural directives without any extra element, making sure
4+
that the only DOM changes being applied are those dictated by the directives themselves.
5+
6+
This not only increases performance \(even so slightly\) since the browser ends up rendering less
7+
elements but can also be a valuable asset in having cleaner DOMs and styles alike.
8+
9+
It can for example enable us to use structural directives without breaking styling dependent on a
10+
precise DOM structure \(as for example the ones we get when using flex containers, margins, the
11+
child combinator selector, etc.\).
12+
13+
## Usage notes
14+
15+
### With `*NgIf`s
16+
17+
One common use case of `<ng-container>` is alongside the `*ngIf` structural directive. By using the
18+
special element we can produce very clean templates easy to understand and work with.
19+
20+
For example, we may want to have a number of elements shown conditionally but they do not need to be
21+
all under the same root element. That can be easily done by wrapping them in such a block:
22+
23+
<code-example format="html" language="html">
24+
25+
&lt;ng-container *ngIf="condition"&gt;
26+
&hellip;
27+
&lt;/ng-container&gt;
28+
29+
</code-example>
30+
31+
This can also be augmented with an `else` statement alongside an `<ng-template>` as:
32+
33+
<code-example format="html" language="html">
34+
35+
&lt;ng-container *ngIf="condition; else templateA"&gt;
36+
&hellip;
37+
&lt;/ng-container&gt;
38+
&lt;ng-template #templateA&gt;
39+
&hellip;
40+
&lt;/ng-template&gt;
41+
42+
</code-example>
43+
44+
### Combination of multiple structural directives
45+
46+
Multiple structural directives cannot be used on the same element; if you need to take advantage of
47+
more than one structural directive, it is advised to use an `<ng-container>` per structural
48+
directive.
49+
50+
The most common scenario is with `*ngIf` and `*ngFor`. For example, let's imagine that we have a
51+
list of items but each item needs to be displayed only if a certain condition is true. We could be
52+
tempted to try something like:
53+
54+
<code-example format="html" language="html">
55+
56+
&lt;ul&gt;
57+
&lt;li *ngFor="let item of items" *ngIf="item.isValid"&gt;
58+
{{ item.name }}
59+
&lt;/li&gt;
60+
&lt;/ul&gt;
61+
62+
</code-example>
63+
64+
As we said that would not work, what we can do is to simply move one of the structural directives to
65+
an `<ng-container>` element, which would then wrap the other one, like so:
66+
67+
<code-example format="html" language="html">
68+
69+
&lt;ul&gt;
70+
&lt;ng-container *ngFor="let item of items"&gt;
71+
&lt;li *ngIf="item.isValid"&gt;
72+
{{ item.name }}
73+
&lt;/li&gt;
74+
&lt;/ng-container&gt;
75+
&lt;/ul&gt;
76+
77+
</code-example>
78+
79+
This would work as intended without introducing any new unnecessary elements in the DOM.
80+
81+
For more information see [one structural directive per element](guide/structural-directives#one-per-element).
82+
83+
### Use alongside ngTemplateOutlet
84+
85+
The `NgTemplateOutlet` directive can be applied to any element but most of the time it's applied
86+
to `<ng-container>` ones. By combining the two, we get a very clear and easy to follow HTML and DOM
87+
structure in which no extra elements are necessary and template views are instantiated where
88+
requested.
89+
90+
For example, imagine a situation in which we have a large HTML, in which a small portion needs to be
91+
repeated in different places. A simple solution is to define an `<ng-template>` containing our
92+
repeating HTML and render that where necessary by using `<ng-container>` alongside
93+
an `NgTemplateOutlet`.
94+
95+
Like so:
96+
97+
<code-example format="html" language="html">
98+
99+
&lt;!-- &hellip; --&gt;
100+
101+
&lt;ng-container *ngTemplateOutlet="tmpl; context: {&dollar;implicit: 'Hello'}"&gt;
102+
&lt;/ng-container&gt;
103+
104+
&lt;!-- &hellip; --&gt;
105+
106+
&lt;ng-container *ngTemplateOutlet="tmpl; context: {&dollar;implicit: 'World'}"&gt;
107+
&lt;/ng-container&gt;
108+
109+
&lt;!-- &hellip; --&gt;
110+
111+
&lt;ng-template #tmpl let-text&gt;
112+
&lt;h1&gt;{{ text }}&lt;/h1&gt;
113+
&lt;/ng-template&gt;
114+
115+
</code-example>
116+
117+
For more information regarding `NgTemplateOutlet`, see
118+
the [`NgTemplateOutlet`s api documentation page](api/common/NgTemplateOutlet).
119+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
The `<ng-content>` element specifies where to project content inside a component template.
2+
3+
## Attributes
4+
5+
| Attribute | Description |
6+
|---------------|-------------------------------------------------------------------------|
7+
| `select` | CSS selector. Matching elements are projected into this `<ng-content>`. |
8+
9+
Only select elements from the projected content that match the given CSS `selector`.
10+
11+
Angular supports [selectors](https://developer.mozilla.org/docs/Web/CSS/CSS_Selectors) for any
12+
combination of tag name, attribute, CSS class, and the `:not` pseudo-class.

0 commit comments

Comments
 (0)