diff --git a/src/components/dynamic-component/dynamic-component.ts b/src/components/dynamic-component/dynamic-component.ts index 1692b7663..5e232ce04 100644 --- a/src/components/dynamic-component/dynamic-component.ts +++ b/src/components/dynamic-component/dynamic-component.ts @@ -18,6 +18,7 @@ import { } from '@angular/core'; import { NavController } from 'ionic-angular'; import { CoreLoggerProvider } from '@providers/logger'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; /** * 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. 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.differ = differs.find([]).create(); } @@ -99,7 +102,7 @@ export class CoreDynamicComponent implements OnInit, OnChanges, DoCheck { if (changes) { this.setInputData(); 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]; } } - - /** - * 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; - } } diff --git a/src/core/compile/components/compile-html/compile-html.ts b/src/core/compile/components/compile-html/compile-html.ts index 8b7a7a218..3151f4d29 100644 --- a/src/core/compile/components/compile-html/compile-html.ts +++ b/src/core/compile/components/compile-html/compile-html.ts @@ -14,10 +14,11 @@ import { Component, Input, OnInit, OnChanges, OnDestroy, ViewContainerRef, ViewChild, ComponentRef, SimpleChange, ChangeDetectorRef, - ElementRef, Optional, Output, EventEmitter + ElementRef, Optional, Output, EventEmitter, DoCheck, KeyValueDiffers } from '@angular/core'; import { NavController } from 'ionic-angular'; 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 @@ -38,7 +39,7 @@ import { CoreCompileProvider } from '../../providers/compile'; selector: 'core-compile-html', template: '' }) -export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { +export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck { @Input() text: string; // The HTML text to display. @Input() javascript: string; // The Javascript to execute in the 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; protected componentRef: ComponentRef; + protected componentInstance: any; protected element; + protected differ: any; // To detect changes in the jsData input. 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.differ = differs.find([]).create(); + } + + /** + * Detect and act upon changes that Angular can’t or won’t 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. return class CoreCompileHtmlFakeComponent implements OnInit { 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 (compileInstance.javascript) { compileInstance.compileProvider.injectLibraries(this, compileInstance.extraProviders); @@ -107,9 +130,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { this['componentContainer'] = compileInstance.element; // Add the data passed to the component. - for (const name in compileInstance.jsData) { - this[name] = compileInstance.jsData[name]; - } + compileInstance.setInputData(); } 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]; + } + } + } } diff --git a/src/providers/utils/dom.ts b/src/providers/utils/dom.ts index c66f47855..510c7876d 100644 --- a/src/providers/utils/dom.ts +++ b/src/providers/utils/dom.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, SimpleChange } from '@angular/core'; import { LoadingController, Loading, ToastController, Toast, AlertController, Alert, Platform, Content, ModalController @@ -148,6 +148,32 @@ export class CoreDomUtilsProvider { 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. *