fix(elements): update the view of an OnPush component when inputs change (#39452)
As with regular Angular components, Angular elements are expected to have their views update when inputs change. Previously, Angular Elements views were not updated if the underlying component used the `OnPush` change detection strategy. This commit fixes this by calling `markForCheck()` on the component view's `ChangeDetectorRef`. NOTE: This is similar to how `@angular/upgrade` does it: https://github.com/angular/angular/blob/3236ae0ee118d0734c90fa9f3767435396213470/packages/upgrade/src/common/src/downgrade_component_adapter.ts#L146. Fixes #38948 PR Close #39452
This commit is contained in:
committed by
Misko Hevery
parent
c1907809a8
commit
bdce7698fc
@@ -5,7 +5,7 @@ describe('Element E2E Tests', function () {
|
||||
describe('Hello World Elements', () => {
|
||||
beforeEach(() => browser.get('hello-world.html'));
|
||||
|
||||
describe('(with default view encapsulation)', () => {
|
||||
describe('(with default CD strategy and view encapsulation)', () => {
|
||||
const helloWorldEl = element(by.css('hello-world-el'));
|
||||
|
||||
it('should display "Hello World!"', function () {
|
||||
@@ -21,6 +21,22 @@ describe('Element E2E Tests', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('(with `OnPush` CD strategy)', () => {
|
||||
const helloWorldOnpushEl = element(by.css('hello-world-onpush-el'));
|
||||
|
||||
it('should display "Hello World!"', function () {
|
||||
expect(helloWorldOnpushEl.getText()).toBe('Hello World!');
|
||||
});
|
||||
|
||||
it('should display "Hello Foo!" via name attribute', function () {
|
||||
const input = element(by.css('input[type=text]'));
|
||||
input.sendKeys('Foo');
|
||||
|
||||
// Make tests less flaky on CI by waiting up to 5s for the element text to be updated.
|
||||
browser.wait(EC.textToBePresentInElement(helloWorldOnpushEl, 'Hello Foo!'), 5000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('(with `ShadowDom` view encapsulation)', () => {
|
||||
const helloWorldShadowEl = element(by.css('hello-world-shadow-el'));
|
||||
const getShadowDomText = (el: ElementFinder) =>
|
||||
|
||||
@@ -2,17 +2,29 @@ import {Injector, NgModule} from '@angular/core';
|
||||
import {createCustomElement} from '@angular/elements';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
|
||||
import {HelloWorldComponent, HelloWorldShadowComponent, TestCardComponent} from './elements';
|
||||
import {HelloWorldComponent, HelloWorldOnpushComponent, HelloWorldShadowComponent, TestCardComponent} from './elements';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [HelloWorldComponent, HelloWorldShadowComponent, TestCardComponent],
|
||||
entryComponents: [HelloWorldComponent, HelloWorldShadowComponent, TestCardComponent],
|
||||
declarations: [
|
||||
HelloWorldComponent,
|
||||
HelloWorldOnpushComponent,
|
||||
HelloWorldShadowComponent,
|
||||
TestCardComponent,
|
||||
],
|
||||
entryComponents: [
|
||||
HelloWorldComponent,
|
||||
HelloWorldOnpushComponent,
|
||||
HelloWorldShadowComponent,
|
||||
TestCardComponent,
|
||||
],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
export class AppModule {
|
||||
constructor(injector: Injector) {
|
||||
customElements.define('hello-world-el', createCustomElement(HelloWorldComponent, {injector}));
|
||||
customElements.define(
|
||||
'hello-world-onpush-el', createCustomElement(HelloWorldOnpushComponent, {injector}));
|
||||
customElements.define(
|
||||
'hello-world-shadow-el', createCustomElement(HelloWorldShadowComponent, {injector}));
|
||||
customElements.define('test-card', createCustomElement(TestCardComponent, {injector}));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Component, Input, ViewEncapsulation} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, Component, Input, ViewEncapsulation} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'hello-world-el',
|
||||
@@ -8,6 +8,15 @@ export class HelloWorldComponent {
|
||||
@Input() name: string = 'World';
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'hello-world-onpush-el',
|
||||
template: 'Hello {{name}}!',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class HelloWorldOnpushComponent {
|
||||
@Input() name: string = 'World';
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'hello-world-shadow-el',
|
||||
template: 'Hello {{name}}!',
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<body>
|
||||
<input type="text">
|
||||
<hello-world-el></hello-world-el>
|
||||
<hello-world-onpush-el></hello-world-onpush-el>
|
||||
<hello-world-shadow-el></hello-world-shadow-el>
|
||||
<script src="dist/bundle.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -5,9 +5,11 @@ platformBrowser().bootstrapModuleFactory(AppModuleNgFactory, {ngZone: 'noop'});
|
||||
|
||||
const input = document.querySelector('input')!;
|
||||
const helloWorld = document.querySelector('hello-world-el')!;
|
||||
const helloWorldOnpush = document.querySelector('hello-world-onpush-el')!;
|
||||
const helloWorldShadow = document.querySelector('hello-world-shadow-el')!;
|
||||
|
||||
input.addEventListener('input', () => {
|
||||
helloWorld.setAttribute('name', input.value);
|
||||
helloWorldOnpush.setAttribute('name', input.value);
|
||||
helloWorldShadow.setAttribute('name', input.value);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user