Skip to content

Commit

Permalink
fix(core): fixes Ivy
Browse files Browse the repository at this point in the history
BREAKING CHANGE:
Components with getter and setter without both are not supported

ISSUES CLOSED: #28, #29
  • Loading branch information
thekiba committed Feb 18, 2020
1 parent 21e8393 commit 7ef62c9
Show file tree
Hide file tree
Showing 15 changed files with 220 additions and 65 deletions.
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"@angular/language-service": "9.0.1",
"@types/jasmine": "~2.8.9",
"@types/jasminewd2": "~2.0.5",
"@types/node": "10.12.0",
"@types/node": "12.12.27",
"codelyzer": "4.5.0",
"commitizen": "^3.0.7",
"cz-customizable": "^5.2.0",
Expand Down
2 changes: 1 addition & 1 deletion projects/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ngxd/core",
"version": "9.0.0",
"version": "9.0.1",
"description": "NGXD is a dynamic pattern + library for Angular",
"keywords": [
"angular",
Expand Down
50 changes: 39 additions & 11 deletions projects/core/src/lib/adapter/adapter-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
ChangeDetectorRef,
ComponentFactory,
ComponentRef,
isDevMode,
SimpleChange,
Type,
} from '@angular/core';
Expand All @@ -16,6 +17,7 @@ import {
toPropertyDef,
} from '../utils';
import { HostAdapter } from './host.adapter';
import { isLifeCycleComponent } from './lifecycle.components';
import { LifecycleComponent } from './lifecycle.strategies';

export interface NgxComponentOutletAdapterRefConfig<TComponent> {
Expand Down Expand Up @@ -199,7 +201,27 @@ export class NgxComponentOutletAdapterRef<TComponent> {
defaultDescriptor.set.call(context, value);
}

instance[insidePropName] = value;
try {
instance[insidePropName] = value;
} catch (e) {
// Todo: add more debug
if (isDevMode()) {
const constructorName = (instance as any).constructor.name;
console.log(`You should use get and set descriptors both with dynamic components:
ERROR: not found '${insidePropName}' input, it has getter only, please add setter!
class ${constructorName} {
get ${insidePropName}() { ... }
// Please add that 👇
set ${insidePropName}() { ... }
}`);
console.error(e);
}
}
this.changeDetectorRef.markForCheck();
},
});
}
Expand All @@ -208,17 +230,23 @@ export class NgxComponentOutletAdapterRef<TComponent> {
const instance: TComponent & LifecycleComponent = this.componentRef.instance as any;

if (hasProperty(this.componentRef.componentType.prototype, 'ngOnChanges')) {
const markForCheckWrapped = markForCheckWrapper(
instance.ngDoCheck,
this.changeDetectorRef
).bind(instance);
if (isLifeCycleComponent(this.onInitComponentRef.instance)) {
this.onInitComponentRef.instance.attach(instance, this.changeDetectorRef);
} else {
// this.onInitComponentRef.instance.ngOnInit = onChangesWrapper(instance.ngOnInit).bind(
// instance
// );
console.warn('todo: add for OnInit on dynamic component');
}

this.onInitComponentRef.instance.ngOnInit = onChangesWrapper(instance.ngOnInit).bind(
instance
);
this.doCheckComponentRef.instance.ngDoCheck = onChangesWrapper(markForCheckWrapped).bind(
instance
);
if (isLifeCycleComponent(this.doCheckComponentRef.instance)) {
this.doCheckComponentRef.instance.attach(instance, this.changeDetectorRef);
} else {
// this.doCheckComponentRef.instance.ngDoCheck = onChangesWrapper(markForCheckWrapped).bind(
// instance
// );
console.warn('todo: add for DoCheck on dynamic component');
}
} else {
this.doCheckComponentRef.instance.ngDoCheck = markForCheckWrapper(
instance.ngDoCheck,
Expand Down
16 changes: 12 additions & 4 deletions projects/core/src/lib/adapter/adapter.builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ export class NgxComponentOutletAdapterBuilder {
TComponent
> = componentFactoryResolver.resolveComponentFactory(componentType);

const componentRef: ComponentRef<TComponent> = viewContainerRef.createComponent(
componentFactory,
viewContainerRef.length,
// const componentRef: ComponentRef<TComponent> = viewContainerRef.createComponent(
// componentFactory,
// viewContainerRef.length,
// injector,
// projectableNodes
// );
const componentRef: ComponentRef<TComponent> = componentFactory.create(
injector,
projectableNodes
);
Expand All @@ -40,12 +44,16 @@ export class NgxComponentOutletAdapterBuilder {
componentFactoryResolver
);

return new NgxComponentOutletAdapterRef({
const adapterRef = new NgxComponentOutletAdapterRef({
componentFactory,
componentRef,
host,
onInitComponentRef,
doCheckComponentRef,
});

viewContainerRef.insert(componentRef.hostView, viewContainerRef.length);

return adapterRef;
}
}
18 changes: 18 additions & 0 deletions projects/core/src/lib/adapter/host-input.adapter.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isDevMode } from '@angular/core';
import { Subject } from 'rxjs';
import { getPropertyDescriptor, PRIVATE_CONTEXT_PREFIX } from '../utils';

Expand All @@ -19,6 +20,23 @@ export class HostInputAdapter<TComponent> {

this.changes = new Subject<any>();
this.defaultDescriptor = getPropertyDescriptor(host, name);

if (this.defaultDescriptor && this.defaultDescriptor.get && !this.defaultDescriptor.set) {
if (isDevMode()) {
const constructorName = (host as any).constructor.name;
console.log(`You should use get and set descriptors both with dynamic components:
ERROR: not found '${name}' input, it has setter only, please add getter!
class ${constructorName} {
// Please add that 👇
get ${name}() { ... }
set ${name}() { ... }
}`);
}
}
this.refCount = 0;

const defaultValue = host[name];
Expand Down
89 changes: 81 additions & 8 deletions projects/core/src/lib/adapter/lifecycle.components.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,103 @@
import { ChangeDetectionStrategy, Component, DoCheck, OnInit } from '@angular/core';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
DoCheck,
OnInit,
} from '@angular/core';
import { onChangesWrapper } from '../utils';

const lifeCycleComponentSymbol = Symbol('Life Cycle Component');

export interface LifeCycleComponent {
[lifeCycleComponentSymbol]: boolean;

attach(context: unknown, changeDetectorRef: ChangeDetectorRef): void;
}

export function isLifeCycleComponent(component: unknown): component is LifeCycleComponent {
return component && component[lifeCycleComponentSymbol];
}

@Component({
selector: 'ngx-component-outlet-on-init-only',
template: '',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class OnInitOnlyComponent implements OnInit {
ngOnInit() {}
export class OnInitOnlyComponent implements LifeCycleComponent, OnInit {
[lifeCycleComponentSymbol] = true;

private context: unknown;
private changeDetectorRef: ChangeDetectorRef;

ngOnInit() {
if (this.context) {
onChangesWrapper(() => {}).call(this.context);
}
}

attach(context: unknown, changeDetectorRef: ChangeDetectorRef): void {
this.context = context;
this.changeDetectorRef = changeDetectorRef;
}
}

@Component({
selector: 'ngx-component-outlet-do-check-only',
template: '',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DoCheckOnlyComponent implements DoCheck {
ngDoCheck() {}
export class DoCheckOnlyComponent implements LifeCycleComponent, DoCheck {
[lifeCycleComponentSymbol] = true;

private context: unknown;
private changeDetectorRef: ChangeDetectorRef;

ngDoCheck() {
if (this.context) {
onChangesWrapper(() => {}).call(this.context);
}

if (this.changeDetectorRef) {
this.changeDetectorRef.markForCheck();
}
}

attach(context: unknown, changeDetectorRef: ChangeDetectorRef): void {
this.context = context;
this.changeDetectorRef = changeDetectorRef;
}
}

@Component({
selector: 'ngx-component-outlet-on-init-do-check',
template: '',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class OnInitAndDoCheckComponent implements OnInit, DoCheck {
ngOnInit() {}
export class OnInitAndDoCheckComponent implements LifeCycleComponent, OnInit, DoCheck {
[lifeCycleComponentSymbol] = true;

private context: unknown;
private changeDetectorRef: ChangeDetectorRef;

ngOnInit() {
if (this.context) {
onChangesWrapper(() => {}).call(this.context);
}
}

ngDoCheck() {
if (this.context) {
onChangesWrapper(() => {}).call(this.context);
}

if (this.changeDetectorRef) {
this.changeDetectorRef.markForCheck();
}
}

ngDoCheck() {}
attach(context: unknown, changeDetectorRef: ChangeDetectorRef): void {
this.context = context;
this.changeDetectorRef = changeDetectorRef;
}
}
4 changes: 3 additions & 1 deletion projects/core/src/lib/adapter/lifecycle.strategies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ const FEATURE_COMPONENTS_DISABLE = {

export function resolveStrategy(component: Type<any>) {
const type = resolveStrategyType(component);
return STRATEGY_CONFIG[type];

const USE_ONLY_DEFAULT_STRATEGY_FOR_IVY = LifecycleStrategyType.OnInitAndDoCheck;
return STRATEGY_CONFIG[USE_ONLY_DEFAULT_STRATEGY_FOR_IVY];
}

function resolveStrategyType<T = any>(component: Type<T>): LifecycleStrategyType {
Expand Down
18 changes: 2 additions & 16 deletions projects/core/src/lib/lib.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ModuleWithProviders, NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import { NgxComponentOutletAdapterBuilder } from './adapter/adapter.builder';

import {
Expand All @@ -21,18 +21,4 @@ import { NgxComponentOutletResolvePipe } from './helpers/resolve.pipe';
exports: [NgxComponentOutletDirective, NgxComponentOutletResolvePipe],
providers: [NgxComponentOutletAdapterBuilder],
})
export class NgxdModule {
/**
* @deprecated
*/
static forRoot(): ModuleWithProviders {
throw new Error('Deprecation import through NgxdModule.forRoot(), use import as NgxdModule.');
}

/**
* @deprecated
*/
static forChild(): ModuleWithProviders {
throw new Error('Deprecation import through NgxdModule.forChild(), use import as NgxdModule.');
}
}
export class NgxdModule {}
2 changes: 2 additions & 0 deletions projects/core/src/public_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export * from './lib/lib.module';
export * from './lib/helpers/resolver';
export * from './lib/helpers/provider';
export * from './lib/adapter/adapter.experimental';
export * from './lib/directive/component.outlet';
export * from './lib/helpers/resolve.pipe';
Loading

0 comments on commit 7ef62c9

Please sign in to comment.