Skip to content

Commit

Permalink
Merge pull request #6 from rico-projects/fixMultipleControllerUsage
Browse files Browse the repository at this point in the history
Fix multiple controller usage
  • Loading branch information
giftkugel authored Jan 29, 2019
2 parents 3a3c3d6 + f3c8f80 commit a93de70
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 136 deletions.
134 changes: 2 additions & 132 deletions src/lib/controller-proxy.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
import { ApplicationRef } from '@angular/core';
import { LoggerFactory, LogLevel } from 'rico-js';
import { RicoService } from './rico-angular.service';

export class ControllerProxy {

static LOGGER: any = LoggerFactory.getLogger('RicoAngularAdapter::ControllerProxy');

private clientContext: any;
private internalModel: any;
private vanillaControllerProxy: any;
private appRef: ApplicationRef;
private clientContext: any;
private modelContainer: Map<any, Map<any, any>>;

constructor(appRef: ApplicationRef, clientContext: any) {
constructor(clientContext: any) {
this.clientContext = clientContext;
this.appRef = appRef;
this.modelContainer = new Map();

this.onBeanAddedHandler = this.onBeanAddedHandler.bind(this);
this.onBeanRemovedHandler = this.onBeanRemovedHandler.bind(this);
this.onBeanUpdateHandler = this.onBeanUpdateHandler.bind(this);
this.onArrayUpdateHandler = this.onArrayUpdateHandler.bind(this);

this.bindBeanManagerListenersForController(clientContext.beanManager);
}

public get model(): any {
Expand Down Expand Up @@ -62,122 +50,4 @@ export class ControllerProxy {
return this.vanillaControllerProxy.destroy();
}

private bindBeanManagerListenersForController(beanManager: any) {
const onBeanAddedHandlerResult = beanManager.onAdded(this.onBeanAddedHandler);
const onBeanRemovedHandlerResult = beanManager.onRemoved(this.onBeanRemovedHandler);
const onBeanUpdateHandlerResult = beanManager.onBeanUpdate(this.onBeanUpdateHandler);
const onArrayUpdateHandlerResult = beanManager.onArrayUpdate(this.onArrayUpdateHandler);
// TODO The results should be used to clean up at the end

ControllerProxy.LOGGER.debug('Rico remoting model binding listeners for Angular registered');
}

private onBeanAddedHandler(bean: any) {
this.modelContainer.set(bean, new Map());
ControllerProxy.LOGGER.debug('onBeanAddedHandler', bean);

for (const propertyName of Object.keys(bean)) {
this.watchProperty(bean, propertyName);
}
}

private onBeanRemovedHandler(bean: any) {
this.modelContainer.delete(bean);
ControllerProxy.LOGGER.debug('onBeanRemovedHandler', bean);
}

private onBeanUpdateHandler(bean: any, propertyName: string, newValue: any, oldValue: any) {
ControllerProxy.LOGGER.debug('onBeanUpdateHandler', bean, propertyName, newValue, oldValue);
let newProperty = true;
for (const currentPropertyName in bean) {
if (currentPropertyName === propertyName) {
newProperty = false;
}
}

if (oldValue === newValue) {
return;
}

if (newProperty) {
this.watchProperty(bean, propertyName);
}
bean[propertyName] = newValue;

this.appRef.tick();
}

private onArrayUpdateHandler(bean: any, propertyName: string, index: number, count: number, newElements: Array<any>) {
ControllerProxy.LOGGER.debug('onArrayUpdateHandler', bean, propertyName, index, count, newElements);

const array = bean[propertyName];

const oldElements = array.slice(index, index + count);
if (this.deepEqual(newElements, oldElements)) {
return;
}

if (typeof newElements === 'undefined' || (newElements && newElements.length === 0)) {
array.splice(index, count);
} else {
this.injectArray(array, index, newElements);

newElements.forEach( (element) => {
for (const currentPropertyName of Object.keys(element)) {
this.watchProperty(element, currentPropertyName);
}
});
}

this.appRef.tick();
}

private watchProperty(bean: any, propertyName: string) {
ControllerProxy.LOGGER.debug('Watching', JSON.stringify(bean), 'property', propertyName);
const proxy = this;
const valueMap = proxy.modelContainer.get(bean);
(function () {
Object.defineProperty(bean, propertyName, {
// Create a new getter for the property
get: function () {
ControllerProxy.LOGGER.trace('Get for ' + propertyName);
return valueMap.get(propertyName);
},
// Create a new setter for the property
set: function (value) {
ControllerProxy.LOGGER.trace('Set for', propertyName, 'with value', value);
proxy.clientContext.beanManager.classRepository.notifyBeanChange(bean, propertyName, value);
valueMap.set(propertyName, value);
},
enumerable: true
});
})();
}

private injectArray(baseArray: Array<any>, startIndex: number, insertArray: Array<any>) {
baseArray.splice.apply(baseArray, [startIndex, 0].concat(insertArray));
}

private exists(object: any) {
return typeof object !== 'undefined' && object !== null;
}

private deepEqual(array1: Array<any>, array2: Array<any>) {
if (array1 === array2 || (!this.exists(array1) && !this.exists(array2))) {
return true;
}
if (this.exists(array1) !== this.exists(array2)) {
return false;
}
const n = array1.length;
if (array2.length !== n) {
return false;
}
for (let i = 0; i < n; i++) {
if (array1[i] !== array2[i]) {
return false;
}
}
return true;
}
}
144 changes: 144 additions & 0 deletions src/lib/model-maintainer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { ApplicationRef, Injectable } from '@angular/core';
import { LoggerFactory, LogLevel } from 'rico-js';

export class ModelMaintainer {

static LOGGER: any = LoggerFactory.getLogger('RicoAngularAdapter::ModelMaintainer');

private appRef: ApplicationRef;
private clientContext: any;
private modelContainer: Map<any, Map<any, any>>;

constructor(appRef: ApplicationRef, clientContext: any) {
this.clientContext = clientContext;
this.appRef = appRef;
this.modelContainer = new Map();

this.onBeanAddedHandler = this.onBeanAddedHandler.bind(this);
this.onBeanRemovedHandler = this.onBeanRemovedHandler.bind(this);
this.onBeanUpdateHandler = this.onBeanUpdateHandler.bind(this);
this.onArrayUpdateHandler = this.onArrayUpdateHandler.bind(this);

this.bindBeanManagerListenersForController(clientContext.beanManager);
}

private bindBeanManagerListenersForController(beanManager: any) {
const onBeanAddedHandlerResult = beanManager.onAdded(this.onBeanAddedHandler);
const onBeanRemovedHandlerResult = beanManager.onRemoved(this.onBeanRemovedHandler);
const onBeanUpdateHandlerResult = beanManager.onBeanUpdate(this.onBeanUpdateHandler);
const onArrayUpdateHandlerResult = beanManager.onArrayUpdate(this.onArrayUpdateHandler);
// TODO The results should be used to clean up at the end

ModelMaintainer.LOGGER.debug('Rico remoting model binding listeners for Angular registered');
}

private onBeanAddedHandler(bean: any) {
this.modelContainer.set(bean, new Map());
ModelMaintainer.LOGGER.debug('onBeanAddedHandler', bean);

for (const propertyName of Object.keys(bean)) {
this.watchProperty(bean, propertyName);
}
}

private onBeanRemovedHandler(bean: any) {
this.modelContainer.delete(bean);
ModelMaintainer.LOGGER.debug('onBeanRemovedHandler', bean);
}

private onBeanUpdateHandler(bean: any, propertyName: string, newValue: any, oldValue: any) {
ModelMaintainer.LOGGER.debug('onBeanUpdateHandler', bean, propertyName, newValue, oldValue);
let newProperty = true;
for (const currentPropertyName in bean) {
if (currentPropertyName === propertyName) {
newProperty = false;
}
}

if (oldValue === newValue) {
return;
}

if (newProperty) {
this.watchProperty(bean, propertyName);
}
bean[propertyName] = newValue;

this.appRef.tick();
}

private onArrayUpdateHandler(bean: any, propertyName: string, index: number, count: number, newElements: Array<any>) {
ModelMaintainer.LOGGER.debug('onArrayUpdateHandler', bean, propertyName, index, count, newElements);

const array = bean[propertyName];

const oldElements = array.slice(index, index + count);
if (this.deepEqual(newElements, oldElements)) {
return;
}

if (typeof newElements === 'undefined' || (newElements && newElements.length === 0)) {
array.splice(index, count);
} else {
this.injectArray(array, index, newElements);

newElements.forEach( (element) => {
for (const currentPropertyName of Object.keys(element)) {
this.watchProperty(element, currentPropertyName);
}
});
}

this.appRef.tick();
}

private watchProperty(bean: any, propertyName: string) {
ModelMaintainer.LOGGER.debug('Watching', JSON.stringify(bean), 'property', propertyName);
const proxy = this;
const valueMap = proxy.modelContainer.get(bean);
(function () {
Object.defineProperty(bean, propertyName, {
// Create a new getter for the property
get: function () {
ModelMaintainer.LOGGER.trace('Get for ' + propertyName);
return valueMap.get(propertyName);
},
// Create a new setter for the property
set: function (value) {
ModelMaintainer.LOGGER.trace('Set for', propertyName, 'with value', value);
proxy.clientContext.beanManager.classRepository.notifyBeanChange(bean, propertyName, value);
valueMap.set(propertyName, value);
},
enumerable: true
});
})();
}

private injectArray(baseArray: Array<any>, startIndex: number, insertArray: Array<any>) {
baseArray.splice.apply(baseArray, [startIndex, 0].concat(insertArray));
}

private exists(object: any) {
return typeof object !== 'undefined' && object !== null;
}

private deepEqual(array1: Array<any>, array2: Array<any>) {
if (array1 === array2 || (!this.exists(array1) && !this.exists(array2))) {
return true;
}
if (this.exists(array1) !== this.exists(array2)) {
return false;
}
const n = array1.length;
if (array2.length !== n) {
return false;
}
for (let i = 0; i < n; i++) {
if (array1[i] !== array2[i]) {
return false;
}
}
return true;
}
}

9 changes: 5 additions & 4 deletions src/lib/rico-angular.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable, ApplicationRef } from '@angular/core';
import { getService, LoggerFactory } from 'rico-js';
import { ControllerProxy } from './controller-proxy';
import { ModelMaintainer } from './model-maintainer';

@Injectable({
providedIn: 'root'
Expand All @@ -10,20 +11,20 @@ export class RicoService {

private contextFactory: any;
private clientContext: any;
private appRef: ApplicationRef;
private modelMaintainer: ModelMaintainer;

constructor() {
RicoService.LOGGER.debug('RicoService created');
}

connect(remotingEndpoint: string, appRef: ApplicationRef): Promise<any> {
this.appRef = appRef;
if (!this.contextFactory) {
this.contextFactory = this.getClientContextFactory();
}

if (!this.clientContext) {
this.clientContext = this.contextFactory.create(remotingEndpoint);
this.modelMaintainer = new ModelMaintainer(appRef, this.clientContext);
}

return this.clientContext.connect();
Expand All @@ -43,7 +44,7 @@ export class RicoService {
}

getHttpClient() {
return getService('HttpClilent');
return getService('HttpClient');
}

getLogger(name: string) {
Expand All @@ -52,7 +53,7 @@ export class RicoService {

createController(name: string): Promise<ControllerProxy> {
if (this.clientContext && this.clientContext.isConnected) {
const controllerProxy = new ControllerProxy(this.appRef, this.clientContext);
const controllerProxy = new ControllerProxy(this.clientContext);
return controllerProxy.create(name);
} else {
return Promise.reject('Cannot create controller. ClientContext not conntected');
Expand Down

0 comments on commit a93de70

Please sign in to comment.