fix(ivy): don't create TNodes for native projectable nodes (#28275)

Before this commit we were creating a "fake" TNode for each and every
projectable node passed during dynamic component creation. This approach
had several problems:
- the existing TView structure had to be mutated to accomodate new TNodes and
it was very easy to "corrupt" TView / TNode data structures;
- TNodes are not really needed to fully support projectable nodes so we were
creating objects and updating existing data structures for nothing.

This commit changes the approach so we don't create "fake" TNodes for projectable
nodes but instead we process projectable nodes directly in the projection instruction.
As a result we've got less code, less object allocation and - as a bonus - we fix few
bugs where TView / TNode data structures were corrupted when using projectable nodes.

PR Close #28275
This commit is contained in:
Pawel Kozlowski
2019-01-21 14:55:37 +01:00
committed by Alex Rickabaugh
parent d8f2318811
commit cf8770f3cc
11 changed files with 369 additions and 210 deletions

View File

@ -10,7 +10,6 @@ import {ChangeDetectorRef, Component, EventEmitter, Input, NO_ERRORS_SCHEMA, NgM
import {async, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {fixmeIvy} from '@angular/private/testing';
import * as angular from '@angular/upgrade/src/common/angular1';
import {$EXCEPTION_HANDLER} from '@angular/upgrade/src/common/constants';
import {UpgradeAdapter, UpgradeAdapterRef} from '@angular/upgrade/src/dynamic/upgrade_adapter';
@ -3098,33 +3097,31 @@ withEachNg1Version(() => {
});
}));
fixmeIvy('FW-873: projected component injector hierarchy not wired up correctly')
.it('should respect hierarchical dependency injection for ng2', async(() => {
const ng1Module = angular.module('ng1', []);
it('should respect hierarchical dependency injection for ng2', async(() => {
const ng1Module = angular.module('ng1', []);
@Component(
{selector: 'ng2-parent', template: `ng2-parent(<ng-content></ng-content>)`})
class Ng2Parent {
}
@Component({selector: 'ng2-child', template: `ng2-child`})
class Ng2Child {
constructor(parent: Ng2Parent) {}
}
@Component({selector: 'ng2-parent', template: `ng2-parent(<ng-content></ng-content>)`})
class Ng2Parent {
}
@Component({selector: 'ng2-child', template: `ng2-child`})
class Ng2Child {
constructor(parent: Ng2Parent) {}
}
@NgModule({declarations: [Ng2Parent, Ng2Child], imports: [BrowserModule]})
class Ng2Module {
}
@NgModule({declarations: [Ng2Parent, Ng2Child], imports: [BrowserModule]})
class Ng2Module {
}
const element = html('<ng2-parent><ng2-child></ng2-child></ng2-parent>');
const element = html('<ng2-parent><ng2-child></ng2-child></ng2-parent>');
const adapter: UpgradeAdapter = new UpgradeAdapter(Ng2Module);
ng1Module.directive('ng2Parent', adapter.downgradeNg2Component(Ng2Parent))
.directive('ng2Child', adapter.downgradeNg2Component(Ng2Child));
adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(document.body.textContent).toEqual('ng2-parent(ng2-child)');
ref.dispose();
});
}));
const adapter: UpgradeAdapter = new UpgradeAdapter(Ng2Module);
ng1Module.directive('ng2Parent', adapter.downgradeNg2Component(Ng2Parent))
.directive('ng2Child', adapter.downgradeNg2Component(Ng2Child));
adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(document.body.textContent).toEqual('ng2-parent(ng2-child)');
ref.dispose();
});
}));
});
describe('testability', () => {