From a0312278d8c11460b95db714059ec317fd672d29 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 30 Jan 2018 10:18:53 +0100 Subject: [PATCH] MOBILE-2324 core: Create and apply core-dynamic-component --- src/components/components.module.ts | 7 +- .../dynamic-component/dynamic-component.html | 5 + .../dynamic-component/dynamic-component.ts | 168 ++++++++++++++++++ src/core/course/components/format/format.html | 49 +++-- src/core/course/components/format/format.ts | 145 +++++---------- .../singleactivity/components/format.ts | 117 ------------ .../components/singleactivity.html | 1 + .../components/singleactivity.ts | 55 ++++++ .../singleactivity/providers/handler.ts | 2 +- .../singleactivity/singleactivity.module.ts | 4 +- src/core/user/components/components.module.ts | 2 + .../user-profile-field.html | 3 +- .../user-profile-field/user-profile-field.ts | 74 ++------ 13 files changed, 322 insertions(+), 310 deletions(-) create mode 100644 src/components/dynamic-component/dynamic-component.html create mode 100644 src/components/dynamic-component/dynamic-component.ts delete mode 100644 src/core/course/formats/singleactivity/components/format.ts create mode 100644 src/core/course/formats/singleactivity/components/singleactivity.html create mode 100644 src/core/course/formats/singleactivity/components/singleactivity.ts diff --git a/src/components/components.module.ts b/src/components/components.module.ts index 1cf8543a4..d1dddb4fb 100644 --- a/src/components/components.module.ts +++ b/src/components/components.module.ts @@ -38,6 +38,7 @@ import { CoreTabsComponent } from './tabs/tabs'; import { CoreTabComponent } from './tabs/tab'; import { CoreRichTextEditorComponent } from './rich-text-editor/rich-text-editor'; import { CoreNavBarButtonsComponent } from './navbar-buttons/navbar-buttons'; +import { CoreDynamicComponent } from './dynamic-component/dynamic-component'; @NgModule({ declarations: [ @@ -61,7 +62,8 @@ import { CoreNavBarButtonsComponent } from './navbar-buttons/navbar-buttons'; CoreTabsComponent, CoreTabComponent, CoreRichTextEditorComponent, - CoreNavBarButtonsComponent + CoreNavBarButtonsComponent, + CoreDynamicComponent ], entryComponents: [ CoreContextMenuPopoverComponent, @@ -92,7 +94,8 @@ import { CoreNavBarButtonsComponent } from './navbar-buttons/navbar-buttons'; CoreTabsComponent, CoreTabComponent, CoreRichTextEditorComponent, - CoreNavBarButtonsComponent + CoreNavBarButtonsComponent, + CoreDynamicComponent ] }) export class CoreComponentsModule {} diff --git a/src/components/dynamic-component/dynamic-component.html b/src/components/dynamic-component/dynamic-component.html new file mode 100644 index 000000000..99c89fec9 --- /dev/null +++ b/src/components/dynamic-component/dynamic-component.html @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/dynamic-component/dynamic-component.ts b/src/components/dynamic-component/dynamic-component.ts new file mode 100644 index 000000000..01fa9532d --- /dev/null +++ b/src/components/dynamic-component/dynamic-component.ts @@ -0,0 +1,168 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { + Component, Input, ViewChild, OnInit, OnChanges, DoCheck, ViewContainerRef, ComponentFactoryResolver, + KeyValueDiffers, SimpleChange +} from '@angular/core'; +import { CoreLoggerProvider } from '../../providers/logger'; + +/** + * Component to create another component dynamically. + * + * You need to pass the class of the component to this component (the class, not the name), along with the input data. + * + * So you should do something like: + * + * import { MyComponent } from './component'; + * + * ... + * + * this.component = MyComponent; + * + * And in the template: + * + * + *

Cannot render the data.

+ *
+ * + * Please notice that the component that you pass needs to be declared in entryComponents of the module to be created dynamically. + * + * The contents of this component will be displayed if no component is supplied or it cannot be created. In the example above, + * if no component is supplied then the template will show the message "Cannot render the data.". + */ +@Component({ + selector: 'core-dynamic-component', + templateUrl: 'dynamic-component.html' +}) +export class CoreDynamicComponent implements OnInit, OnChanges, DoCheck { + + @Input() component: any; + @Input() data: any; + + // Get the container where to put the dynamic component. + @ViewChild('dynamicComponent', { read: ViewContainerRef }) set dynamicComponent(el: ViewContainerRef) { + this.container = el; + this.createComponent(); + } + + instance: any; + container: ViewContainerRef; + protected logger: any; + protected differ: any; // To detect changes in the data input. + + constructor(logger: CoreLoggerProvider, private factoryResolver: ComponentFactoryResolver, differs: KeyValueDiffers) { + this.logger = logger.getInstance('CoreDynamicComponent'); + this.differ = differs.find([]).create(); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.createComponent(); + } + + /** + * Detect changes on input properties. + */ + ngOnChanges(changes: { [name: string]: SimpleChange }): void { + if (!this.instance && changes.component) { + this.createComponent(); + } + } + + /** + * Detect and act upon changes that Angular can’t or won’t detect on its own (objects and arrays). + */ + ngDoCheck(): void { + if (this.instance) { + // Check if there's any change in the data object. + const changes = this.differ.diff(this.data); + if (changes) { + this.setInputData(); + if (this.instance.ngOnChanges) { + this.instance.ngOnChanges(this.createChangesForComponent(changes)); + } + } + } + } + + /** + * Create a component, add it to a container and set the input data. + * + * @return {boolean} Whether the component was successfully created. + */ + protected createComponent(): boolean { + if (!this.component || !this.container) { + // No component to instantiate or container doesn't exist right now. + return false; + } + + if (this.instance) { + // Component already instantiated. + return true; + } + + try { + // Create the component and add it to the container. + const factory = this.factoryResolver.resolveComponentFactory(this.component), + componentRef = this.container.createComponent(factory); + + this.instance = componentRef.instance; + + this.setInputData(); + + return true; + } catch (ex) { + this.logger.error('Error creating component', ex); + + return false; + } + } + + /** + * Set the input data for the component. + */ + protected setInputData(): void { + for (const name in this.data) { + 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/course/components/format/format.html b/src/core/course/components/format/format.html index a0fbe654f..0b1e8dcc8 100644 --- a/src/core/course/components/format/format.html +++ b/src/core/course/components/format/format.html @@ -1,45 +1,45 @@ -
+ - - - - - - + + + + + + + -
- - - {{section.formattedName || section.name}} - - - -
- + +
+ + + {{section.formattedName || section.name}} + + + +
+
- + - - +
- + - - +
-
+ @@ -78,6 +78,3 @@ {{section.count}} / {{section.total}} - - - \ No newline at end of file diff --git a/src/core/course/components/format/format.ts b/src/core/course/components/format/format.ts index 626218146..578f9fe0f 100644 --- a/src/core/course/components/format/format.ts +++ b/src/core/course/components/format/format.ts @@ -12,13 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { - Component, Input, OnInit, OnChanges, OnDestroy, ViewContainerRef, ComponentFactoryResolver, ViewChild, ChangeDetectorRef, - SimpleChange, Output, EventEmitter -} from '@angular/core'; +import { Component, Input, OnInit, OnChanges, OnDestroy, SimpleChange, Output, EventEmitter } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreEventsProvider } from '../../../../providers/events'; -import { CoreLoggerProvider } from '../../../../providers/logger'; import { CoreSitesProvider } from '../../../../providers/sites'; import { CoreDomUtilsProvider } from '../../../../providers/utils/dom'; import { CoreCourseProvider } from '../../../course/providers/course'; @@ -48,31 +44,15 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { @Input() initialSectionNumber?: number; // The section to load first (by number). @Output() completionChanged?: EventEmitter; // Will emit an event when any module completion changes. - // Get the containers where to inject dynamic components. We use a setter because they might be inside a *ngIf. - @ViewChild('courseFormat', { read: ViewContainerRef }) set courseFormat(el: ViewContainerRef) { - if (this.course) { - this.createComponent('courseFormat', this.cfDelegate.getCourseFormatComponent(this.course), el); - } else { - // The component hasn't been initialized yet. Store the container. - this.componentContainers['courseFormat'] = el; - } - } - @ViewChild('courseSummary', { read: ViewContainerRef }) set courseSummary(el: ViewContainerRef) { - this.createComponent('courseSummary', this.cfDelegate.getCourseSummaryComponent(this.course), el); - } - @ViewChild('sectionSelector', { read: ViewContainerRef }) set sectionSelector(el: ViewContainerRef) { - this.createComponent('sectionSelector', this.cfDelegate.getSectionSelectorComponent(this.course), el); - } - @ViewChild('singleSection', { read: ViewContainerRef }) set singleSection(el: ViewContainerRef) { - this.createComponent('singleSection', this.cfDelegate.getSingleSectionComponent(this.course), el); - } - @ViewChild('allSections', { read: ViewContainerRef }) set allSections(el: ViewContainerRef) { - this.createComponent('allSections', this.cfDelegate.getAllSectionsComponent(this.course), el); - } + // All the possible component classes. + courseFormatComponent: any; + courseSummaryComponent: any; + sectionSelectorComponent: any; + singleSectionComponent: any; + allSectionsComponent: any; - // Instances and containers of all the components that the handler could define. - protected componentContainers: { [type: string]: ViewContainerRef } = {}; - componentInstances: { [type: string]: any } = {}; + // Data to pass to the components. + data: any = {}; displaySectionSelector: boolean; selectedSection: any; @@ -80,23 +60,20 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { selectOptions: any = {}; loaded: boolean; - protected logger; protected sectionStatusObserver; - constructor(logger: CoreLoggerProvider, private cfDelegate: CoreCourseFormatDelegate, translate: TranslateService, - private factoryResolver: ComponentFactoryResolver, private cdr: ChangeDetectorRef, + constructor(private cfDelegate: CoreCourseFormatDelegate, translate: TranslateService, private courseHelper: CoreCourseHelperProvider, private domUtils: CoreDomUtilsProvider, eventsProvider: CoreEventsProvider, private sitesProvider: CoreSitesProvider, prefetchDelegate: CoreCourseModulePrefetchDelegate) { - this.logger = logger.getInstance('CoreCourseFormatComponent'); this.selectOptions.title = translate.instant('core.course.sections'); this.completionChanged = new EventEmitter(); // Listen for section status changes. this.sectionStatusObserver = eventsProvider.on(CoreEventsProvider.SECTION_STATUS_CHANGED, (data) => { if (this.downloadEnabled && this.sections && this.sections.length && this.course && data.sectionId && - data.courseId == this.course.id) { + data.courseId == this.course.id) { // Check if the affected section is being downloaded. // If so, we don't update section status because it'll already be updated when the download finishes. const downloadId = this.courseHelper.getSectionDownloadId({ id: data.sectionId }); @@ -135,15 +112,19 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { */ ngOnInit(): void { this.displaySectionSelector = this.cfDelegate.displaySectionSelector(this.course); - - this.createComponent( - 'courseFormat', this.cfDelegate.getCourseFormatComponent(this.course), this.componentContainers['courseFormat']); } /** * Detect changes on input properties. */ ngOnChanges(changes: { [name: string]: SimpleChange }): void { + this.setInputData(); + + if (changes.course) { + // Course has changed, try to get the components. + this.getComponents(); + } + if (changes.sections && this.sections) { if (!this.selectedSection) { // There is no selected section yet, calculate which one to load. @@ -186,62 +167,39 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { if (changes.downloadEnabled && this.downloadEnabled) { this.calculateSectionsStatus(false); } - - // Apply the changes to the components and call ngOnChanges if it exists. - for (const type in this.componentInstances) { - const instance = this.componentInstances[type]; - - for (const name in changes) { - instance[name] = changes[name].currentValue; - } - - if (instance.ngOnChanges) { - instance.ngOnChanges(changes); - } - } } /** - * Create a component, add it to a container and set the input data. - * - * @param {string} type The "type" of the component. - * @param {any} componentClass The class of the component to create. - * @param {ViewContainerRef} container The container to add the component to. - * @return {boolean} Whether the component was successfully created. + * Set the input data for components. */ - protected createComponent(type: string, componentClass: any, container: ViewContainerRef): boolean { - if (!componentClass || !container) { - // No component to instantiate or container doesn't exist right now. - return false; - } + protected setInputData(): void { + this.data.course = this.course; + this.data.sections = this.sections; + this.data.initialSectionId = this.initialSectionId; + this.data.initialSectionNumber = this.initialSectionNumber; + this.data.downloadEnabled = this.downloadEnabled; + } - if (this.componentInstances[type] && container === this.componentContainers[type]) { - // Component already instantiated and the component hasn't been destroyed, nothing to do. - return true; - } - - try { - // Create the component and add it to the container. - const factory = this.factoryResolver.resolveComponentFactory(componentClass), - componentRef = container.createComponent(factory); - - this.componentContainers[type] = container; - this.componentInstances[type] = componentRef.instance; - - // Set the Input data. - this.componentInstances[type].course = this.course; - this.componentInstances[type].sections = this.sections; - this.componentInstances[type].initialSectionId = this.initialSectionId; - this.componentInstances[type].initialSectionNumber = this.initialSectionNumber; - this.componentInstances[type].downloadEnabled = this.downloadEnabled; - - this.cdr.detectChanges(); // The instances are used in ngIf, tell Angular that something has changed. - - return true; - } catch (ex) { - this.logger.error('Error creating component', type, ex); - - return false; + /** + * Get the components classes. + */ + protected getComponents(): void { + if (this.course) { + if (!this.courseFormatComponent) { + this.courseFormatComponent = this.cfDelegate.getCourseFormatComponent(this.course); + } + if (!this.courseSummaryComponent) { + this.courseSummaryComponent = this.cfDelegate.getCourseSummaryComponent(this.course); + } + if (!this.sectionSelectorComponent) { + this.sectionSelectorComponent = this.cfDelegate.getSectionSelectorComponent(this.course); + } + if (!this.singleSectionComponent) { + this.singleSectionComponent = this.cfDelegate.getSingleSectionComponent(this.course); + } + if (!this.allSectionsComponent) { + this.allSectionsComponent = this.cfDelegate.getAllSectionsComponent(this.course); + } } } @@ -253,16 +211,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { sectionChanged(newSection: any): void { const previousValue = this.selectedSection; this.selectedSection = newSection; - - // If there is a component to render the current section, update its section. - if (this.componentInstances.singleSection) { - this.componentInstances.singleSection.section = this.selectedSection; - if (this.componentInstances.singleSection.ngOnChanges) { - this.componentInstances.singleSection.ngOnChanges({ - section: new SimpleChange(previousValue, newSection, typeof previousValue != 'undefined') - }); - } - } + this.data.section = this.selectedSection; } /** diff --git a/src/core/course/formats/singleactivity/components/format.ts b/src/core/course/formats/singleactivity/components/format.ts deleted file mode 100644 index 001e0e692..000000000 --- a/src/core/course/formats/singleactivity/components/format.ts +++ /dev/null @@ -1,117 +0,0 @@ -// (C) Copyright 2015 Martin Dougiamas -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnChanges, ViewContainerRef, ComponentFactoryResolver, SimpleChange } from '@angular/core'; -import { CoreLoggerProvider } from '../../../../../providers/logger'; -import { CoreCourseModuleDelegate } from '../../../providers/module-delegate'; -import { CoreCourseUnsupportedModuleComponent } from '../../../components/unsupported-module/unsupported-module'; - -/** - * Component to display single activity format. It will determine the right component to use and instantiate it. - * - * The instantiated component will receive the course and the module as inputs. - */ -@Component({ - selector: 'core-course-format-single-activity', - template: '' -}) -export class CoreCourseFormatSingleActivityComponent implements OnChanges { - @Input() course: any; // The course to render. - @Input() sections: any[]; // List of course sections. - @Input() downloadEnabled?: boolean; // Whether the download of sections and modules is enabled. - - protected logger: any; - protected module: any; - protected componentInstance: any; - - constructor(logger: CoreLoggerProvider, private viewRef: ViewContainerRef, private factoryResolver: ComponentFactoryResolver, - private moduleDelegate: CoreCourseModuleDelegate) { - this.logger = logger.getInstance('CoreCourseFormatSingleActivityComponent'); - } - - /** - * Detect changes on input properties. - */ - ngOnChanges(changes: { [name: string]: SimpleChange }): void { - if (this.course && this.sections && this.sections.length) { - // In single activity the module should only have 1 section and 1 module. Get the module. - const module = this.sections[0] && this.sections[0].modules && this.sections[0].modules[0]; - if (module && !this.componentInstance) { - // We haven't created the component yet. Create it now. - this.createComponent(module); - } - - if (this.componentInstance && this.componentInstance.ngOnChanges) { - // Call ngOnChanges of the component. - const newChanges: { [name: string]: SimpleChange } = {}; - - // Check if course has changed. - if (changes.course) { - newChanges.course = changes.course; - this.componentInstance.course = this.course; - } - - // Check if module has changed. - if (changes.sections && module != this.module) { - newChanges.module = { - currentValue: module, - firstChange: changes.sections.firstChange, - previousValue: this.module, - isFirstChange: (): boolean => { - return newChanges.module.firstChange; - } - }; - this.componentInstance.module = module; - this.module = module; - } - - if (Object.keys(newChanges).length) { - this.componentInstance.ngOnChanges(newChanges); - } - } - } - } - - /** - * Create the component, add it to the container and set the input data. - * - * @param {any} module The module. - * @return {boolean} Whether the component was successfully created. - */ - protected createComponent(module: any): boolean { - const componentClass = this.moduleDelegate.getMainComponent(this.course, module) || CoreCourseUnsupportedModuleComponent; - if (!componentClass) { - // No component to instantiate. - return false; - } - - try { - // Create the component and add it to the container. - const factory = this.factoryResolver.resolveComponentFactory(componentClass), - componentRef = this.viewRef.createComponent(factory); - - this.componentInstance = componentRef.instance; - - // Set the Input data. - this.componentInstance.courseId = this.course.id; - this.componentInstance.module = module; - - return true; - } catch (ex) { - this.logger.error('Error creating component', ex); - - return false; - } - } -} diff --git a/src/core/course/formats/singleactivity/components/singleactivity.html b/src/core/course/formats/singleactivity/components/singleactivity.html new file mode 100644 index 000000000..1f3a37007 --- /dev/null +++ b/src/core/course/formats/singleactivity/components/singleactivity.html @@ -0,0 +1 @@ + diff --git a/src/core/course/formats/singleactivity/components/singleactivity.ts b/src/core/course/formats/singleactivity/components/singleactivity.ts new file mode 100644 index 000000000..cd5cae32c --- /dev/null +++ b/src/core/course/formats/singleactivity/components/singleactivity.ts @@ -0,0 +1,55 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Component, Input, OnChanges, SimpleChange } from '@angular/core'; +import { CoreCourseModuleDelegate } from '../../../providers/module-delegate'; +import { CoreCourseUnsupportedModuleComponent } from '../../../components/unsupported-module/unsupported-module'; + +/** + * Component to display single activity format. It will determine the right component to use and instantiate it. + * + * The instantiated component will receive the course and the module as inputs. + */ +@Component({ + selector: 'core-course-format-single-activity', + templateUrl: 'singleactivity.html' +}) +export class CoreCourseFormatSingleActivityComponent implements OnChanges { + @Input() course: any; // The course to render. + @Input() sections: any[]; // List of course sections. + @Input() downloadEnabled?: boolean; // Whether the download of sections and modules is enabled. + + componentClass: any; // The class of the component to render. + data: any = {}; // Data to pass to the component. + + constructor(private moduleDelegate: CoreCourseModuleDelegate) { } + + /** + * Detect changes on input properties. + */ + ngOnChanges(changes: { [name: string]: SimpleChange }): void { + if (this.course && this.sections && this.sections.length) { + // In single activity the module should only have 1 section and 1 module. Get the module. + const module = this.sections[0] && this.sections[0].modules && this.sections[0].modules[0]; + if (module && !this.componentClass) { + // We haven't obtained the class yet. Get it now. + this.componentClass = this.moduleDelegate.getMainComponent(this.course, module) || + CoreCourseUnsupportedModuleComponent; + } + + this.data.courseId = this.course.id; + this.data.module = module; + } + } +} diff --git a/src/core/course/formats/singleactivity/providers/handler.ts b/src/core/course/formats/singleactivity/providers/handler.ts index 1f232fe78..17f1a5734 100644 --- a/src/core/course/formats/singleactivity/providers/handler.ts +++ b/src/core/course/formats/singleactivity/providers/handler.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; import { CoreCourseFormatHandler } from '../../../providers/format-delegate'; -import { CoreCourseFormatSingleActivityComponent } from '../components/format'; +import { CoreCourseFormatSingleActivityComponent } from '../components/singleactivity'; /** * Handler to support singleactivity course format. diff --git a/src/core/course/formats/singleactivity/singleactivity.module.ts b/src/core/course/formats/singleactivity/singleactivity.module.ts index 0ccb98388..8899f01db 100644 --- a/src/core/course/formats/singleactivity/singleactivity.module.ts +++ b/src/core/course/formats/singleactivity/singleactivity.module.ts @@ -13,15 +13,17 @@ // limitations under the License. import { NgModule } from '@angular/core'; -import { CoreCourseFormatSingleActivityComponent } from './components/format'; +import { CoreCourseFormatSingleActivityComponent } from './components/singleactivity'; import { CoreCourseFormatSingleActivityHandler } from './providers/handler'; import { CoreCourseFormatDelegate } from '../../providers/format-delegate'; +import { CoreComponentsModule } from '../../../../components/components.module'; @NgModule({ declarations: [ CoreCourseFormatSingleActivityComponent ], imports: [ + CoreComponentsModule ], providers: [ CoreCourseFormatSingleActivityHandler diff --git a/src/core/user/components/components.module.ts b/src/core/user/components/components.module.ts index 13682bbe7..3fd47b41e 100644 --- a/src/core/user/components/components.module.ts +++ b/src/core/user/components/components.module.ts @@ -17,6 +17,7 @@ import { CommonModule } from '@angular/common'; import { IonicModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreUserProfileFieldComponent } from './user-profile-field/user-profile-field'; +import { CoreComponentsModule } from '../../../components/components.module'; @NgModule({ declarations: [ @@ -26,6 +27,7 @@ import { CoreUserProfileFieldComponent } from './user-profile-field/user-profile CommonModule, IonicModule, TranslateModule.forChild(), + CoreComponentsModule ], providers: [ ], diff --git a/src/core/user/components/user-profile-field/user-profile-field.html b/src/core/user/components/user-profile-field/user-profile-field.html index 825590342..1f3a37007 100644 --- a/src/core/user/components/user-profile-field/user-profile-field.html +++ b/src/core/user/components/user-profile-field/user-profile-field.html @@ -1,2 +1 @@ - - \ No newline at end of file + diff --git a/src/core/user/components/user-profile-field/user-profile-field.ts b/src/core/user/components/user-profile-field/user-profile-field.ts index 0058e9a9a..7cbd32d4f 100644 --- a/src/core/user/components/user-profile-field/user-profile-field.ts +++ b/src/core/user/components/user-profile-field/user-profile-field.ts @@ -12,8 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, ViewChild, ViewContainerRef, ComponentFactoryResolver, OnInit } from '@angular/core'; -import { CoreLoggerProvider } from '../../../../providers/logger'; +import { Component, Input, OnInit } from '@angular/core'; import { CoreUserProfileFieldDelegate } from '../../providers/user-profile-field-delegate'; import { CoreUtilsProvider } from '../../../../providers/utils/utils'; @@ -31,74 +30,23 @@ export class CoreUserProfileFieldComponent implements OnInit { @Input() form?: any; // Form where to add the form control. Required if edit=true or signup=true. @Input() registerAuth?: string; // Register auth method. E.g. 'email'. - // Get the containers where to inject dynamic components. We use a setter because they might be inside a *ngIf. - @ViewChild('userProfileField', { read: ViewContainerRef }) set userProfileField(el: ViewContainerRef) { - if (this.field) { - this.createComponent(this.ufDelegate.getComponent(this.field, this.signup), el); - } else { - // The component hasn't been initialized yet. Store the container. - this.fieldContainer = el; - } - } + componentClass: any; // The class of the component to render. + data: any = {}; // Data to pass to the component. - protected logger; - - // Instances and containers of all the components that the handler could define. - protected fieldContainer: ViewContainerRef; - protected fieldInstance: any; - - constructor(logger: CoreLoggerProvider, private factoryResolver: ComponentFactoryResolver, - private ufDelegate: CoreUserProfileFieldDelegate, private utilsProvider: CoreUtilsProvider) { - this.logger = logger.getInstance('CoreUserProfileFieldComponent'); - } + constructor(private ufDelegate: CoreUserProfileFieldDelegate, private utilsProvider: CoreUtilsProvider) { } /** * Component being initialized. */ ngOnInit(): void { - this.createComponent(this.ufDelegate.getComponent(this.field, this.signup), this.fieldContainer); - } + this.componentClass = this.ufDelegate.getComponent(this.field, this.signup); - /** - * Create a component, add it to a container and set the input data. - * - * @param {any} componentClass The class of the component to create. - * @param {ViewContainerRef} container The container to add the component to. - * @return {boolean} Whether the component was successfully created. - */ - protected createComponent(componentClass: any, container: ViewContainerRef): boolean { - if (!componentClass || !container) { - // No component to instantiate or container doesn't exist right now. - return false; - } - - if (this.fieldInstance && container === this.fieldContainer) { - // Component already instantiated and the component hasn't been destroyed, nothing to do. - return true; - } - - try { - // Create the component and add it to the container. - const factory = this.factoryResolver.resolveComponentFactory(componentClass), - componentRef = container.createComponent(factory); - - this.fieldContainer = container; - this.fieldInstance = componentRef.instance; - - // Set the Input data. - this.fieldInstance.field = this.field; - this.fieldInstance.edit = this.utilsProvider.isTrueOrOne(this.edit); - if (this.edit) { - this.fieldInstance.signup = this.utilsProvider.isTrueOrOne(this.signup); - this.fieldInstance.disabled = this.utilsProvider.isTrueOrOne(this.field.locked); - this.fieldInstance.form = this.form; - } - - return true; - } catch (ex) { - this.logger.error('Error creating user field component', ex, componentClass); - - return false; + this.data.field = this.field; + this.data.edit = this.utilsProvider.isTrueOrOne(this.edit); + if (this.edit) { + this.data.signup = this.utilsProvider.isTrueOrOne(this.signup); + this.data.disabled = this.utilsProvider.isTrueOrOne(this.field.locked); + this.data.form = this.form; } } }