MOBILE-2338 compile: Monitor jsData changes

main
Dani Palou 2018-05-22 13:31:52 +02:00 committed by Pau Ferrer Ocaña
parent c626bee407
commit d53a7acda5
3 changed files with 70 additions and 34 deletions

View File

@ -18,6 +18,7 @@ import {
} from '@angular/core'; } from '@angular/core';
import { NavController } from 'ionic-angular'; import { NavController } from 'ionic-angular';
import { CoreLoggerProvider } from '@providers/logger'; import { CoreLoggerProvider } from '@providers/logger';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
/** /**
* Component to create another component dynamically. * Component to create another component dynamically.
@ -68,7 +69,9 @@ export class CoreDynamicComponent implements OnInit, OnChanges, DoCheck {
protected differ: any; // To detect changes in the data input. protected differ: any; // To detect changes in the data input.
constructor(logger: CoreLoggerProvider, protected factoryResolver: ComponentFactoryResolver, differs: KeyValueDiffers, constructor(logger: CoreLoggerProvider, protected factoryResolver: ComponentFactoryResolver, differs: KeyValueDiffers,
@Optional() protected navCtrl: NavController, protected cdr: ChangeDetectorRef, protected element: ElementRef) { @Optional() protected navCtrl: NavController, protected cdr: ChangeDetectorRef, protected element: ElementRef,
protected domUtils: CoreDomUtilsProvider) {
this.logger = logger.getInstance('CoreDynamicComponent'); this.logger = logger.getInstance('CoreDynamicComponent');
this.differ = differs.find([]).create(); this.differ = differs.find([]).create();
} }
@ -99,7 +102,7 @@ export class CoreDynamicComponent implements OnInit, OnChanges, DoCheck {
if (changes) { if (changes) {
this.setInputData(); this.setInputData();
if (this.instance.ngOnChanges) { if (this.instance.ngOnChanges) {
this.instance.ngOnChanges(this.createChangesForComponent(changes)); this.instance.ngOnChanges(this.domUtils.createChangesFromKeyValueDiff(changes));
} }
} }
} }
@ -170,29 +173,4 @@ export class CoreDynamicComponent implements OnInit, OnChanges, DoCheck {
this.instance[name] = this.data[name]; this.instance[name] = this.data[name];
} }
} }
/**
* Given the changes on the data input, create the changes object for the component.
*
* @param {any} changes Changes in the data input (detected by KeyValueDiffer).
* @return {{[name: string]: SimpleChange}} List of changes for the component.
*/
protected createChangesForComponent(changes: any): { [name: string]: SimpleChange } {
const newChanges: { [name: string]: SimpleChange } = {};
// Added items are considered first change.
changes.forEachAddedItem((item) => {
newChanges[item.key] = new SimpleChange(item.previousValue, item.currentValue, true);
});
// Changed or removed items aren't first change.
changes.forEachChangedItem((item) => {
newChanges[item.key] = new SimpleChange(item.previousValue, item.currentValue, false);
});
changes.forEachRemovedItem((item) => {
newChanges[item.key] = new SimpleChange(item.previousValue, item.currentValue, true);
});
return newChanges;
}
} }

View File

@ -14,10 +14,11 @@
import { import {
Component, Input, OnInit, OnChanges, OnDestroy, ViewContainerRef, ViewChild, ComponentRef, SimpleChange, ChangeDetectorRef, Component, Input, OnInit, OnChanges, OnDestroy, ViewContainerRef, ViewChild, ComponentRef, SimpleChange, ChangeDetectorRef,
ElementRef, Optional, Output, EventEmitter ElementRef, Optional, Output, EventEmitter, DoCheck, KeyValueDiffers
} from '@angular/core'; } from '@angular/core';
import { NavController } from 'ionic-angular'; import { NavController } from 'ionic-angular';
import { CoreCompileProvider } from '../../providers/compile'; import { CoreCompileProvider } from '../../providers/compile';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
/** /**
* This component has a behaviour similar to $compile for AngularJS. Given an HTML code, it will compile it so all its * This component has a behaviour similar to $compile for AngularJS. Given an HTML code, it will compile it so all its
@ -38,7 +39,7 @@ import { CoreCompileProvider } from '../../providers/compile';
selector: 'core-compile-html', selector: 'core-compile-html',
template: '<ng-container #dynamicComponent></ng-container>' template: '<ng-container #dynamicComponent></ng-container>'
}) })
export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
@Input() text: string; // The HTML text to display. @Input() text: string; // The HTML text to display.
@Input() javascript: string; // The Javascript to execute in the component. @Input() javascript: string; // The Javascript to execute in the component.
@Input() jsData: any; // Data to pass to the fake component. @Input() jsData: any; // Data to pass to the fake component.
@ -50,11 +51,30 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy {
@ViewChild('dynamicComponent', { read: ViewContainerRef }) container: ViewContainerRef; @ViewChild('dynamicComponent', { read: ViewContainerRef }) container: ViewContainerRef;
protected componentRef: ComponentRef<any>; protected componentRef: ComponentRef<any>;
protected componentInstance: any;
protected element; protected element;
protected differ: any; // To detect changes in the jsData input.
constructor(protected compileProvider: CoreCompileProvider, protected cdr: ChangeDetectorRef, element: ElementRef, constructor(protected compileProvider: CoreCompileProvider, protected cdr: ChangeDetectorRef, element: ElementRef,
@Optional() protected navCtrl: NavController) { @Optional() protected navCtrl: NavController, differs: KeyValueDiffers, protected domUtils: CoreDomUtilsProvider) {
this.element = element.nativeElement; this.element = element.nativeElement;
this.differ = differs.find([]).create();
}
/**
* Detect and act upon changes that Angular cant or wont detect on its own (objects and arrays).
*/
ngDoCheck(): void {
if (this.componentInstance) {
// Check if there's any change in the jsData object.
const changes = this.differ.diff(this.jsData);
if (changes) {
this.setInputData();
if (this.componentInstance.ngOnChanges) {
this.componentInstance.ngOnChanges(this.domUtils.createChangesFromKeyValueDiff(changes));
}
}
}
} }
/** /**
@ -96,6 +116,9 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy {
// Create the component, using the text as the template. // Create the component, using the text as the template.
return class CoreCompileHtmlFakeComponent implements OnInit { return class CoreCompileHtmlFakeComponent implements OnInit {
constructor() { constructor() {
// Store this instance so it can be accessed by the outer component.
compileInstance.componentInstance = this;
// If there is some javascript to run, prepare the instance. // If there is some javascript to run, prepare the instance.
if (compileInstance.javascript) { if (compileInstance.javascript) {
compileInstance.compileProvider.injectLibraries(this, compileInstance.extraProviders); compileInstance.compileProvider.injectLibraries(this, compileInstance.extraProviders);
@ -107,9 +130,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy {
this['componentContainer'] = compileInstance.element; this['componentContainer'] = compileInstance.element;
// Add the data passed to the component. // Add the data passed to the component.
for (const name in compileInstance.jsData) { compileInstance.setInputData();
this[name] = compileInstance.jsData[name];
}
} }
ngOnInit(): void { ngOnInit(): void {
@ -120,4 +141,15 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy {
} }
}; };
} }
/**
* Set the JS data as input data of the component instance.
*/
protected setInputData(): void {
if (this.componentInstance) {
for (const name in this.jsData) {
this.componentInstance[name] = this.jsData[name];
}
}
}
} }

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Injectable } from '@angular/core'; import { Injectable, SimpleChange } from '@angular/core';
import { import {
LoadingController, Loading, ToastController, Toast, AlertController, Alert, Platform, Content, LoadingController, Loading, ToastController, Toast, AlertController, Alert, Platform, Content,
ModalController ModalController
@ -148,6 +148,32 @@ export class CoreDomUtilsProvider {
return {coreCanceled: true}; return {coreCanceled: true};
} }
/**
* Given a list of changes for a component input detected by a KeyValueDiffers, create an object similar to the one
* passed to the ngOnChanges functions.
*
* @param {any} changes Changes detected by KeyValueDiffer.
* @return {{[name: string]: SimpleChange}} Changes in a format like ngOnChanges.
*/
createChangesFromKeyValueDiff(changes: any): { [name: string]: SimpleChange } {
const newChanges: { [name: string]: SimpleChange } = {};
// Added items are considered first change.
changes.forEachAddedItem((item) => {
newChanges[item.key] = new SimpleChange(item.previousValue, item.currentValue, true);
});
// Changed or removed items aren't first change.
changes.forEachChangedItem((item) => {
newChanges[item.key] = new SimpleChange(item.previousValue, item.currentValue, false);
});
changes.forEachRemovedItem((item) => {
newChanges[item.key] = new SimpleChange(item.previousValue, item.currentValue, true);
});
return newChanges;
}
/** /**
* Extract the downloadable URLs from an HTML code. * Extract the downloadable URLs from an HTML code.
* *