[Angular] Overlay CDK
<p>Some basic exmaple on Doc: https://material.angular.io/cdk/overlay/overview</p><p> </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> This button triggers the overlay and is it's origin </span><span style="color: rgba(0, 128, 0, 1)">--></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">button </span><span style="color: rgba(255, 0, 0, 1)">(click)</span><span style="color: rgba(0, 0, 255, 1)">="isOpen = !isOpen"</span><span style="color: rgba(255, 0, 0, 1)"> type</span><span style="color: rgba(0, 0, 255, 1)">="button"</span><span style="color: rgba(255, 0, 0, 1)"> cdkOverlayOrigin #trigger</span><span style="color: rgba(0, 0, 255, 1)">="cdkOverlayOrigin"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)">
{{isOpen ? "Close" : "Open"}}
</span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">button</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> This template displays the overlay content and is connected to the button </span><span style="color: rgba(0, 128, 0, 1)">--></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">ng-template
</span><span style="color: rgba(255, 0, 0, 1)">cdkConnectedOverlay
</span><span style="color: rgba(0, 0, 255, 1)">="trigger"</span><span style="color: rgba(255, 0, 0, 1)">
</span><span style="color: rgba(0, 0, 255, 1)">="isOpen"</span>
<span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">ul </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="example-list"</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">li</span><span style="color: rgba(0, 0, 255, 1)">></span>Item 1<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">li</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">li</span><span style="color: rgba(0, 0, 255, 1)">></span>Item 2<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">li</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">li</span><span style="color: rgba(0, 0, 255, 1)">></span>Item 3<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">li</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">ul</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">ng-template</span><span style="color: rgba(0, 0, 255, 1)">></span></pre>
</div>
<p> </p>
<h2>Positioning</h2>
<div class="cnblogs_code">
<pre>="positions"</pre>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">import {
Component,
OnInit,
ChangeDetectionStrategy,
ViewChild,
ElementRef,
} </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/core</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
import { FocusMonitor } </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/cdk/a11y</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
import {
CdkConnectedOverlay,
ConnectedPosition,
ScrollStrategyOptions,
ScrollStrategy,
} </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/cdk/overlay</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
import { OverlayReference } </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/cdk/overlay/overlay-reference</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
...
@Component({
...
})
export </span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> DropDownSearchComponent implements OnInit {
positions: ConnectedPosition[] </span>=<span style="color: rgba(0, 0, 0, 1)"> [
{
originX: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">center</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
originY: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">bottom</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
overlayX: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">center</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
overlayY: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">top</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
offsetY: </span>-<span style="color: rgba(128, 0, 128, 1)">21</span><span style="color: rgba(0, 0, 0, 1)">,
},
{
originX: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">center</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
originY: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">top</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
overlayX: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">center</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
overlayY: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">bottom</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
panelClass: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">no-enogh-space-at-bottom</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
},
];
} </span></pre>
</div>
<p>We have defined two Postions:</p>
<p>1. Center aligned. Overlay stay bottom</p>
<p><img src="https://img2020.cnblogs.com/blog/364241/202109/364241-20210930182201926-518950260.png" alt="" width="615" height="289" loading="lazy"></p>
<p>2. Center aligned. Overlay stay on top, if not enough space at bottom</p>
<p><img src="https://img2020.cnblogs.com/blog/364241/202109/364241-20210930182238278-1421414352.png" alt="" width="613" height="253" loading="lazy"></p>
<p>Basiclly it will go though each position in array, apply it if previous position has not enough space for showing.</p>
<p> </p>
<h2>Creating Overlay and define position</h2>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">import {
ConnectedPosition,
Overlay,
OverlayPositionBuilder,
} </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/cdk/overlay</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
import { ComponentPortal } </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/cdk/portal</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
@Component({
...
})
export </span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> AppComponent implements OnInit {
@ViewChild(DropDownSearchComponent, { read: ElementRef, </span><span style="color: rgba(0, 0, 255, 1)">static</span>: <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)"> })
dropdown;
createDialog() {
</span><span style="color: rgba(0, 0, 255, 1)">const</span> overlayRef = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.overly.create({
hasBackdrop: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
positionStrategy: </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.positionBuilder
.</span><span style="color: rgba(0, 0, 255, 1)">global</span><span style="color: rgba(0, 0, 0, 1)">()
.centerHorizontally()
.centerVertically(),
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> .flexibleConnectedTo(this.dropdown)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> .withPositions([
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> here, top-left of the overlay is connected to bottom-left of the origin;
</span><span style="color: rgba(0, 128, 0, 1)">//</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> of course, you can change this object or generate it dynamically;
</span><span style="color: rgba(0, 128, 0, 1)">//</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> moreover, you can specify multiple objects in this array for CDK to find the most suitable option
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> originX: 'center',
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> originY: 'bottom',
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> overlayX: 'center',
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> overlayY: 'top',
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> } as ConnectedPosition,
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ])
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> .withPush(false),</span>
<span style="color: rgba(0, 0, 0, 1)"> });
</span><span style="color: rgba(0, 0, 255, 1)">const</span> dialogPortal = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ComponentPortal(DialogComponent);
overlayRef.attach(dialogPortal);
overlayRef.backdropClick().subscribe(() </span>=><span style="color: rgba(0, 0, 0, 1)"> overlayRef.detach());
}
}</span></pre>
</div>
<p> </p>
<p>you can have</p>
<p>1. global position: for example center the overlay</p>
<p>2. flex position: attach to one origin</p>
<p> </p>
<h2>Scolling</h2>
<div class="cnblogs_code">
<pre>="scrollStrategy"</pre>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">import {
CdkConnectedOverlay,
ConnectedPosition,
ScrollStrategyOptions,
ScrollStrategy,
} </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/cdk/overlay</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
@Component({
...
})
export </span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> DropDownSearchComponent implements OnInit {
scrollStrategy: ScrollStrategy;
constructor(
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> focusMonitor: FocusMonitor,
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> scrollStrategies: ScrollStrategyOptions
) {}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> this.scrollStrategy = new ConfirmScrollStrategy(this.inputEl);<br> // this.scrollStrategy = this.scrollStrategies.none()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> this.scrollStrategy = this.scrollStrategies.block();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> this.scrollStrategy = this.scrollStrategies.close({
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> threshold: 50,
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> });</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.scrollStrategy = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.scrollStrategies.reposition();
..
}
</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ConfirmScrollStrategy implements ScrollStrategy {
_overlay: OverlayReference;
constructor(</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> inputRef: ElementRef) {}
attach(overlayRef: OverlayReference) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>._overlay =<span style="color: rgba(0, 0, 0, 1)"> overlayRef;
}
enable() {
document.addEventListener(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">scroll</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.scrollListener);
}
disable() {
document.removeEventListener(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">scroll</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.scrollListener);
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span> scrollListener = () =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (confirm(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">The overlay will be closed. Procced?</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">._overlay.detach();
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.inputRef.nativeElement.blur();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">._overlay.updatePosition();
};
}</span></pre>
</div>
<p>There are 4 built-in srolling types:</p>
<p>1. none: Overlay stay where it was, no matter how you scroll your app</p>
<p>2. block: App cannot scroll at all</p>
<p>3. close: close the overlay once over the threhold</p>
<p>4. reposition: follow the origin when srcolling</p>
<p> </p>
<p>You can also build a custom scrolling type as the one ´<strong>ConfirmScrollStrategy</strong>´.</p>
<p> </p>
<h2>Misc</h2>
<h3>Breakpoint</h3>
<div class="cnblogs_code">
<pre>import { BreakpointObserver, Breakpoints } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/cdk/layout</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
..
constructor(
..
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> breakpointObserver: BreakpointObserver
) {}
ngOnInit(): </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.breakpointObserver.isMatched(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">(max-width: 600px)</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)) {
console.info(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">The screen width is less than 600px</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.isWideScreen$ = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.breakpointObserver
.observe()
.pipe(map(({ matches }) </span>=><span style="color: rgba(0, 0, 0, 1)"> matches));
}</span></pre>
</div>
<p> </p>
<h3>focusMonitor</h3>
<div class="cnblogs_code">
<pre>import { FocusMonitor } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/cdk/a11y</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
constructor(
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> focusMonitor: FocusMonitor,
) {}
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.isPanelVisible$ = <span style="color: rgba(0, 0, 255, 1)">this</span>.focusMonitor.monitor(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.inputEl).pipe()
}</span></pre>
</div>
<p> </p>
<h3>Listen to keyboard event for Overlay</h3>
<div class="cnblogs_code">
<div>import { ESCAPE } from '@angular/cdk/keycodes';</div>
<pre><br> @ViewChild(CdkConnectedOverlay, { <span style="color: rgba(0, 0, 255, 1)">static</span>: <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)"> })
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> connectedOverlay: CdkConnectedOverlay;
</span><span style="color: rgba(0, 0, 255, 1)">const</span> onEscClick$ = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.connectedOverlay.overlayKeydown.pipe(
filter(({ keyCode }) </span>=><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> keyCode ===<span style="color: rgba(0, 0, 0, 1)"> ESCAPE;
})
);</span></pre>
</div>
<p> </p>
<h3>More directives</h3>
<div class="cnblogs_code">
<pre><ng-<span style="color: rgba(0, 0, 0, 1)">template
cdkConnectedOverlay
cdkConnectedOverlayHasBackdrop
cdkConnectedOverlayBackdropClass</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">cdk-overlay-transparent-backdrop</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">scrollStrategy</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">originOverlay</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">showPanel$ | async</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">positions</span><span style="color: rgba(128, 0, 0, 1)">"</span>
></pre>
</div>
<p> </p>
<p>Code:</p>
<p>https://github.com/DMezhenskyi/angular-cdk-lessons</p>
<p id="1632997359310">videos:</p>
<p>https://www.youtube.com/watch?v=2pS9bYtsBRo</p><br><br>
来源:https://www.cnblogs.com/Answer1215/p/15357882.html
頁:
[1]