diff --git a/src/addon/mod/assign/pages/edit/edit.ts b/src/addon/mod/assign/pages/edit/edit.ts
index a3513e908..21ca6e70a 100644
--- a/src/addon/mod/assign/pages/edit/edit.ts
+++ b/src/addon/mod/assign/pages/edit/edit.ts
@@ -329,7 +329,7 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy {
* Component being destroyed.
*/
ngOnDestroy(): void {
- this.isDestroyed = false;
+ this.isDestroyed = true;
// Unblock the assignment.
if (this.assign) {
diff --git a/src/addon/mod/wiki/pages/edit/edit.html b/src/addon/mod/wiki/pages/edit/edit.html
new file mode 100644
index 000000000..e41b3541c
--- /dev/null
+++ b/src/addon/mod/wiki/pages/edit/edit.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/addon/mod/wiki/pages/edit/edit.module.ts b/src/addon/mod/wiki/pages/edit/edit.module.ts
new file mode 100644
index 000000000..b24ab2f09
--- /dev/null
+++ b/src/addon/mod/wiki/pages/edit/edit.module.ts
@@ -0,0 +1,33 @@
+// (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 { NgModule } from '@angular/core';
+import { IonicPageModule } from 'ionic-angular';
+import { TranslateModule } from '@ngx-translate/core';
+import { CoreComponentsModule } from '@components/components.module';
+import { CoreDirectivesModule } from '@directives/directives.module';
+import { AddonModWikiEditPage } from './edit';
+
+@NgModule({
+ declarations: [
+ AddonModWikiEditPage,
+ ],
+ imports: [
+ CoreComponentsModule,
+ CoreDirectivesModule,
+ IonicPageModule.forChild(AddonModWikiEditPage),
+ TranslateModule.forChild()
+ ],
+})
+export class AddonModWikiEditPageModule {}
diff --git a/src/addon/mod/wiki/pages/edit/edit.ts b/src/addon/mod/wiki/pages/edit/edit.ts
new file mode 100644
index 000000000..fef89d36b
--- /dev/null
+++ b/src/addon/mod/wiki/pages/edit/edit.ts
@@ -0,0 +1,538 @@
+// (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, OnInit, OnDestroy } from '@angular/core';
+import { IonicPage, NavController, NavParams } from 'ionic-angular';
+import { FormControl, FormGroup, FormBuilder } from '@angular/forms';
+import { TranslateService } from '@ngx-translate/core';
+import { CoreEventsProvider } from '@providers/events';
+import { CoreSitesProvider } from '@providers/sites';
+import { CoreSyncProvider } from '@providers/sync';
+import { CoreDomUtilsProvider } from '@providers/utils/dom';
+import { CoreTextUtilsProvider } from '@providers/utils/text';
+import { CoreCourseProvider } from '@core/course/providers/course';
+import { CoreCourseHelperProvider } from '@core/course/providers/helper';
+import { AddonModWikiProvider } from '../../providers/wiki';
+import { AddonModWikiOfflineProvider } from '../../providers/wiki-offline';
+import { AddonModWikiSyncProvider } from '../../providers/wiki-sync';
+
+/**
+ * Page that allows adding or editing a wiki page.
+ */
+@IonicPage({ segment: 'addon-mod-wiki-edit' })
+@Component({
+ selector: 'page-addon-mod-wiki-edit',
+ templateUrl: 'edit.html',
+})
+export class AddonModWikiEditPage implements OnInit, OnDestroy {
+
+ title: string; // Title to display.
+ pageForm: FormGroup; // The form group.
+ contentControl: FormControl; // The FormControl for the page content.
+ canEditTitle: boolean; // Whether title can be edited.
+ loaded: boolean; // Whether the data has been loaded.
+ component = AddonModWikiProvider.COMPONENT; // Component to link the files to.
+ componentId: number; // Component ID to link the files to.
+ wrongVersionLock: boolean; // Whether the page lock doesn't match the initial one.
+
+ protected module: any; // Wiki module instance.
+ protected courseId: number; // Course the wiki belongs to.
+ protected subwikiId: number; // Subwiki ID the page belongs to.
+ protected initialSubwikiId: number; // Same as subwikiId, but it won't be updated, it'll always be the value received.
+ protected wikiId: number; // Wiki ID the page belongs to.
+ protected pageId: number; // The page ID (if editing a page).
+ protected section: string; // The section being edited.
+ protected groupId: number; // The group the subwiki belongs to.
+ protected userId: number; // The user the subwiki belongs to.
+ protected blockId: string; // ID to block the subwiki.
+ protected editing: boolean; // Whether the user is editing a page (true) or creating a new one (false).
+ protected editOffline: boolean; // Whether the user is editing an offline page.
+ protected rteEnabled: boolean; // Whether rich text editor is enabled.
+ protected subwikiFiles: any[]; // List of files of the subwiki.
+ protected originalContent: string; // The original page content.
+ protected version: number; // Page version.
+ protected renewLockInterval: any; // An interval to renew the lock every certain time.
+ protected forceLeave = false; // To allow leaving the page without checking for changes.
+ protected isDestroyed = false; // Whether the page has been destroyed.
+ protected pageParamsToLoad: any; // Params of the page to load when this page is closed.
+
+ constructor(navParams: NavParams, fb: FormBuilder, protected navCtrl: NavController, protected sitesProvider: CoreSitesProvider,
+ protected syncProvider: CoreSyncProvider, protected domUtils: CoreDomUtilsProvider,
+ protected translate: TranslateService, protected courseProvider: CoreCourseProvider,
+ protected eventsProvider: CoreEventsProvider, protected wikiProvider: AddonModWikiProvider,
+ protected wikiOffline: AddonModWikiOfflineProvider, protected wikiSync: AddonModWikiSyncProvider,
+ protected textUtils: CoreTextUtilsProvider, protected courseHelper: CoreCourseHelperProvider) {
+
+ this.module = navParams.get('module') || {};
+ this.courseId = navParams.get('courseId');
+ this.subwikiId = navParams.get('subwikiId');
+ this.wikiId = navParams.get('wikiId');
+ this.pageId = navParams.get('pageId');
+ this.section = navParams.get('section');
+ this.groupId = navParams.get('groupId');
+ this.userId = navParams.get('userId');
+
+ let pageTitle = navParams.get('pageTitle');
+ pageTitle = pageTitle ? pageTitle.replace(/\+/g, ' ') : '';
+
+ this.initialSubwikiId = this.subwikiId;
+ this.componentId = this.module.id;
+ this.canEditTitle = !pageTitle;
+ this.title = pageTitle ? this.translate.instant('addon.mod_wiki.editingpage', {$a: pageTitle}) :
+ this.translate.instant('addon.mod_wiki.newpagehdr');
+ this.blockId = this.wikiSync.getSubwikiBlockId(this.subwikiId, this.wikiId, this.userId, this.groupId);
+
+ // Create the form group and its controls.
+ this.contentControl = fb.control('');
+ this.pageForm = fb.group({
+ title: pageTitle
+ });
+ this.pageForm.addControl('text', this.contentControl);
+
+ // Block the wiki so it cannot be synced.
+ this.syncProvider.blockOperation(this.component, this.blockId);
+ }
+
+ /**
+ * Component being initialized.
+ */
+ ngOnInit(): void {
+ this.fetchWikiPageData().then((success) => {
+ if (success && this.blockId && !this.isDestroyed) {
+ // Block the subwiki now that we have blockId for sure.
+ const newBlockId = this.wikiSync.getSubwikiBlockId(this.subwikiId, this.wikiId, this.userId, this.groupId);
+ if (newBlockId != this.blockId) {
+ this.syncProvider.unblockOperation(this.component, this.blockId);
+ this.blockId = newBlockId;
+ this.syncProvider.blockOperation(this.component, this.blockId);
+ }
+ }
+ }).finally(() => {
+ this.loaded = true;
+ });
+ }
+
+ /**
+ * Convenience function to get wiki page data.
+ *
+ * @return {Promise} Promise resolved with boolean: whether it was successful.
+ */
+ protected fetchWikiPageData(): Promise {
+ let promise,
+ canEdit = false;
+
+ if (this.pageId) {
+ // Editing a page that already exists.
+ this.canEditTitle = false;
+ this.editing = true;
+ this.editOffline = false; // Cannot edit pages in offline.
+
+ // Get page contents to obtain title and editing permission
+ promise = this.wikiProvider.getPageContents(this.pageId).then((pageContents) => {
+ this.pageForm.controls.title.setValue(pageContents.title); // Set the title in the form group.
+ this.wikiId = pageContents.wikiid;
+ this.subwikiId = pageContents.subwikiid;
+ this.title = this.translate.instant('addon.mod_wiki.editingpage', {$a: pageContents.title});
+ this.groupId = pageContents.groupid;
+ this.userId = pageContents.userid;
+ canEdit = pageContents.caneditpage;
+
+ // Wait for sync to be over (if any).
+ return this.wikiSync.waitForSync(this.blockId);
+ }).then(() => {
+ // Check if rich text editor is enabled.
+ return this.domUtils.isRichTextEditorEnabled();
+ }).then((enabled) => {
+ this.rteEnabled = enabled;
+
+ if (enabled) {
+ // Get subwiki files, needed to replace URLs for rich text editor.
+ return this.wikiProvider.getSubwikiFiles(this.wikiId, this.groupId, this.userId);
+ }
+ }).then((files) => {
+ this.subwikiFiles = files;
+
+ // Get editable text of the page/section.
+ return this.wikiProvider.getPageForEditing(this.pageId, this.section);
+ }).then((editContents) => {
+ // Get the original page contents, treating file URLs if needed.
+ const content = this.rteEnabled ? this.textUtils.replacePluginfileUrls(editContents.content, this.subwikiFiles) :
+ editContents.content;
+
+ this.contentControl.setValue(content);
+ this.originalContent = content;
+ this.version = editContents.version;
+
+ if (canEdit) {
+ // Renew the lock every certain time.
+ this.renewLockInterval = setInterval(() => {
+ this.renewLock();
+ }, AddonModWikiProvider.RENEW_LOCK_TIME);
+ }
+ });
+ } else {
+ // New page. Wait for sync to be over (if any).
+ promise = this.wikiSync.waitForSync(this.blockId);
+
+ if (this.contentControl.value) {
+ // Check if there's already some offline data for this page.
+ promise = promise.then(() => {
+ return this.wikiOffline.getNewPage(this.pageForm.controls.title.value, this.subwikiId, this.wikiId,
+ this.userId, this.groupId);
+ }).then((page) => {
+ // Load offline content.
+ this.contentControl.setValue(page.cachedcontent);
+ this.originalContent = page.cachedcontent;
+ this.editOffline = true;
+ }).catch(() => {
+ // No offline data found.
+ this.editOffline = false;
+ });
+ } else {
+ this.editOffline = false;
+ }
+
+ promise.then(() => {
+ this.editing = false;
+ canEdit = !!this.blockId; // If no blockId, the user cannot edit the page.
+ });
+ }
+
+ return promise.then(() => {
+ return true;
+ }).catch((error) => {
+ this.domUtils.showErrorModalDefault(error, 'Error getting wiki data.');
+
+ // Go back.
+ this.forceLeavePage();
+
+ return false;
+ }).finally(() => {
+ if (!canEdit) {
+ // Cannot edit, show alert and go back.
+ this.domUtils.showAlert(this.translate.instant('core.notice'),
+ this.translate.instant('addon.mod_wiki.cannoteditpage'));
+ this.forceLeavePage();
+ }
+ });
+ }
+
+ /**
+ * Force leaving the page, without checking for changes.
+ */
+ protected forceLeavePage(): void {
+ this.forceLeave = true;
+ this.navCtrl.pop();
+ }
+
+ /**
+ * Navigate to a new offline page.
+ *
+ * @param {string} title Page title.
+ */
+ protected goToNewOfflinePage(title: string): void {
+ if (this.courseId && (this.module.id || this.wikiId)) {
+ // We have enough data to navigate to the page.
+ if (!this.editOffline || this.previousViewPageIsDifferentOffline(title)) {
+ this.pageParamsToLoad = {
+ module: this.module,
+ courseId: this.courseId,
+ pageId: null,
+ pageTitle: title,
+ wikiId: this.wikiId,
+ subwikiId: this.subwikiId,
+ userId: this.userId,
+ groupId: this.groupId
+ };
+ }
+ } else {
+ this.domUtils.showAlert(this.translate.instant('core.success'), this.translate.instant('core.datastoredoffline'));
+ }
+
+ this.forceLeavePage();
+ }
+
+ /**
+ * Check if we need to navigate to a new state.
+ *
+ * @param {string} title Page title.
+ * @return {Promise} Promise resolved when done.
+ */
+ protected gotoPage(title: string): Promise {
+ return this.retrieveModuleInfo(this.wikiId).then(() => {
+ let openPage = false;
+
+ // Not the firstpage.
+ if (this.initialSubwikiId) {
+ if (!this.editing && this.editOffline && this.previousViewPageIsDifferentOffline(title)) {
+ // The user submitted an offline page that isn't loaded in the back view, open it.
+ openPage = true;
+ } else if (!this.editOffline && this.previousViewIsDifferentPageOnline()) {
+ // The user submitted an offline page that isn't loaded in the back view, open it.
+ openPage = true;
+ }
+ }
+
+ if (openPage) {
+ // Setting that will do the app navigate to the page.
+ this.pageParamsToLoad = {
+ module: this.module,
+ courseId: this.courseId,
+ pageId: this.pageId,
+ pageTitle: title,
+ wikiId: this.wikiId,
+ subwikiId: this.subwikiId,
+ userId: this.userId,
+ groupId: this.groupId
+ };
+ }
+
+ this.forceLeavePage();
+ }).catch(() => {
+ // Go back if it fails.
+ this.forceLeavePage();
+ });
+ }
+
+ /**
+ * Check if data has changed.
+ *
+ * @return {boolean} Whether data has changed.
+ */
+ protected hasDataChanged(): boolean {
+ const values = this.pageForm.value;
+
+ return !(this.originalContent == values.text || (!this.editing && !values.text && !values.title));
+ }
+
+ /**
+ * Check if we can leave the page or not.
+ *
+ * @return {boolean|Promise} Resolved if we can leave it, rejected if not.
+ */
+ ionViewCanLeave(): boolean | Promise {
+ if (this.forceLeave) {
+ return true;
+ }
+
+ // Check if data has changed.
+ if (this.hasDataChanged()) {
+ return this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
+ }
+
+ return true;
+ }
+
+ /**
+ * View left.
+ */
+ ionViewDidLeave(): void {
+ if (this.pageParamsToLoad) {
+ // Go to the page we've just created/edited.
+ this.navCtrl.push('AddonModWikiIndexPage', this.pageParamsToLoad);
+ }
+ }
+
+ /**
+ * In case we are NOT editing an offline page, check if the page loaded in previous view is different than this view.
+ *
+ * @return {boolean} Whether previous view wiki page is different than current page.
+ */
+ protected previousViewIsDifferentPageOnline(): boolean {
+ // We cannot precisely detect when the state is the same but this is close to it.
+ const previousView = this.navCtrl.getPrevious();
+
+ return !this.editing || previousView.component.name != 'AddonModWikiIndexPage' ||
+ previousView.data.module.id != this.module.id || previousView.data.pageId != this.pageId;
+ }
+
+ /**
+ * In case we're editing an offline page, check if the page loaded in previous view is different than this view.
+ *
+ * @param {string} title The current page title.
+ * @return {boolean} Whether previous view wiki page is different than current page.
+ */
+ protected previousViewPageIsDifferentOffline(title: string): boolean {
+ // We cannot precisely detect when the state is the same but this is close to it.
+ const previousView = this.navCtrl.getPrevious();
+
+ if (previousView.component.name != 'AddonModWikiIndexPage' || previousView.data.module.id != this.module.id ||
+ previousView.data.wikiId != this.wikiId || previousView.data.pageTitle != title) {
+ return true;
+ }
+
+ // Check subwiki using subwiki or user and group.
+ const previousSubwikiId = parseInt(previousView.data.subwikiId, 10) || 0;
+ if (previousSubwikiId > 0 && this.subwikiId > 0) {
+ return previousSubwikiId != this.subwikiId;
+ }
+
+ const previousUserId = parseInt(previousView.data.userId, 10) || 0,
+ previousGroupId = parseInt(previousView.data.groupId, 10) || 0;
+
+ return this.userId != previousUserId || this.groupId != previousGroupId;
+ }
+
+ /**
+ * Save the data.
+ */
+ save(): void {
+ const values = this.pageForm.value,
+ title = values.title,
+ modal = this.domUtils.showModalLoading('core.sending', true);
+ let promise,
+ text = values.text;
+
+ if (this.rteEnabled) {
+ text = this.textUtils.restorePluginfileUrls(text, this.subwikiFiles);
+ } else {
+ text = this.textUtils.formatHtmlLines(text);
+ }
+
+ if (this.editing) {
+ // Edit existing page.
+ promise = this.wikiProvider.editPage(this.pageId, text, this.section).then(() => {
+ // Invalidate page since it changed.
+ return this.wikiProvider.invalidatePage(this.pageId).then(() => {
+ return this.gotoPage(title);
+ });
+ });
+ } else {
+ // Creating a new page.
+ if (!title) {
+ // Title is mandatory, stop.
+ this.domUtils.showAlert(this.translate.instant('core.notice'),
+ this.translate.instant('addon.mod_wiki.titleshouldnotbeempty'));
+ modal.dismiss();
+
+ return;
+ }
+
+ if (!this.editOffline) {
+ // Check if the user has an offline page with the same title.
+ promise = this.wikiOffline.getNewPage(title, this.subwikiId, this.wikiId, this.userId, this.groupId).then(() => {
+ // There's a page with same name, reject with error message.
+ return Promise.reject(this.translate.instant('addon.mod_wiki.pageexists'));
+ }, () => {
+ // Not found, page can be sent.
+ });
+ } else {
+ promise = Promise.resolve();
+ }
+
+ promise = promise.then(() => {
+ // Try to send the page.
+ let wikiId = this.wikiId || (this.module && this.module.instance);
+
+ return this.wikiProvider.newPage(title, text, this.subwikiId, wikiId, this.userId, this.groupId).then((id) => {
+ if (id > 0) {
+ // Page was created, get its data and go to the page.
+ this.pageId = id;
+
+ return this.wikiProvider.getPageContents(this.pageId).then((pageContents) => {
+ const promises = [];
+
+ wikiId = parseInt(pageContents.wikiid, 10);
+ if (!this.subwikiId) {
+ // Subwiki was not created, invalidate subwikis as well.
+ promises.push(this.wikiProvider.invalidateSubwikis(wikiId));
+ }
+
+ this.subwikiId = parseInt(pageContents.subwikiid, 10);
+ this.userId = parseInt(pageContents.userid, 10);
+ this.groupId = parseInt(pageContents.groupid, 10);
+
+ // Invalidate subwiki pages since there are new.
+ promises.push(this.wikiProvider.invalidateSubwikiPages(wikiId));
+
+ return Promise.all(promises).then(() => {
+ return this.gotoPage(title);
+ });
+ }).finally(() => {
+ // Notify page created.
+ this.eventsProvider.trigger(AddonModWikiProvider.PAGE_CREATED_EVENT, {
+ pageId: this.pageId,
+ subwikiId: this.subwikiId,
+ pageTitle: title,
+ siteId: this.sitesProvider.getCurrentSiteId()
+ });
+ });
+ } else {
+ // Page stored in offline. Go to see the offline page.
+ this.goToNewOfflinePage(title);
+ }
+ });
+ });
+ }
+
+ return promise.catch((error) => {
+ this.domUtils.showErrorModalDefault(error, 'Error saving wiki data.');
+ }).finally(() => {
+ modal.dismiss();
+ });
+ }
+
+ /**
+ * Renew lock and control versions.
+ */
+ protected renewLock(): void {
+ this.wikiProvider.getPageForEditing(this.pageId, this.section, true).then((response) => {
+ if (response.version && this.version != response.version) {
+ this.wrongVersionLock = true;
+ }
+ });
+ }
+
+ /**
+ * Fetch module information to redirect when needed.
+ *
+ * @param {number} wikiId Wiki ID.
+ * @return {Promise} Promise resolved when done.
+ */
+ protected retrieveModuleInfo(wikiId: number): Promise {
+ if (this.module.id && this.courseId) {
+ // We have enough data.
+ return Promise.resolve();
+ }
+
+ const promise = this.module.id ? Promise.resolve(this.module) :
+ this.courseProvider.getModuleBasicInfoByInstance(wikiId, 'wiki');
+
+ return promise.then((mod) => {
+ this.module = mod;
+ this.componentId = this.module.id;
+
+ if (!this.courseId && this.module.course) {
+ this.courseId = this.module.course;
+ } else if (!this.courseId) {
+ return this.courseHelper.getModuleCourseIdByInstance(wikiId, 'wiki').then((course) => {
+ this.courseId = course;
+ });
+ }
+ });
+ }
+
+ /**
+ * Component being destroyed.
+ */
+ ngOnDestroy(): void {
+ this.isDestroyed = true;
+ clearInterval(this.renewLockInterval);
+
+ // Unblock the subwiki.
+ if (this.blockId) {
+ this.syncProvider.unblockOperation(this.component, this.blockId);
+ }
+ }
+}
diff --git a/src/addon/mod/wiki/providers/wiki.ts b/src/addon/mod/wiki/providers/wiki.ts
index fd46d3474..511d245c3 100644
--- a/src/addon/mod/wiki/providers/wiki.ts
+++ b/src/addon/mod/wiki/providers/wiki.ts
@@ -61,6 +61,7 @@ export interface AddonModWikiSubwikiListData {
export class AddonModWikiProvider {
static COMPONENT = 'mmaModWiki';
static PAGE_CREATED_EVENT = 'addon_mod_wiki_page_created';
+ static RENEW_LOCK_TIME = 30000; // Milliseconds.
protected ROOT_CACHE_KEY = 'mmaModWiki:';
protected logger;
diff --git a/src/components/rich-text-editor/rich-text-editor.ts b/src/components/rich-text-editor/rich-text-editor.ts
index c0a9ed6c4..4eac21804 100644
--- a/src/components/rich-text-editor/rich-text-editor.ts
+++ b/src/components/rich-text-editor/rich-text-editor.ts
@@ -12,11 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { Component, Input, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core';
+import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, AfterContentInit, OnDestroy } from '@angular/core';
import { TextInput } from 'ionic-angular';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { FormControl } from '@angular/forms';
import { Keyboard } from '@ionic-native/keyboard';
+import { Subscription } from 'rxjs';
/**
* Directive to display a rich text editor if enabled.
@@ -36,7 +37,7 @@ import { Keyboard } from '@ionic-native/keyboard';
selector: 'core-rich-text-editor',
templateUrl: 'rich-text-editor.html'
})
-export class CoreRichTextEditorComponent {
+export class CoreRichTextEditorComponent implements AfterContentInit, OnDestroy {
// Based on: https://github.com/judgewest2000/Ionic3RichText/
// @todo: Resize, images, anchor button, fullscreen...
@@ -53,6 +54,8 @@ export class CoreRichTextEditorComponent {
uniqueId = `rte{Math.floor(Math.random() * 1000000)}`;
editorElement: HTMLDivElement;
+ protected valueChangeSubscription: Subscription;
+
constructor(private domUtils: CoreDomUtilsProvider, private keyboard: Keyboard) {
this.contentChanged = new EventEmitter();
}
@@ -69,13 +72,17 @@ export class CoreRichTextEditorComponent {
this.editorElement = this.editor.nativeElement as HTMLDivElement;
this.editorElement.innerHTML = this.control.value;
this.textarea.value = this.control.value;
- this.control.setValue(this.control.value);
this.editorElement.onchange = this.onChange.bind(this);
this.editorElement.onkeyup = this.onChange.bind(this);
this.editorElement.onpaste = this.onChange.bind(this);
this.editorElement.oninput = this.onChange.bind(this);
+ // Listen for changes on the control to update the editor (if it is updated from outside of this component).
+ this.valueChangeSubscription = this.control.valueChanges.subscribe((param) => {
+ this.editorElement.innerHTML = param;
+ });
+
// Setup button actions.
const buttons = (this.decorate.nativeElement as HTMLDivElement).getElementsByTagName('button');
for (let i = 0; i < buttons.length; i++) {
@@ -109,14 +116,16 @@ export class CoreRichTextEditorComponent {
if (this.isNullOrWhiteSpace(this.editorElement.innerText)) {
this.clearText();
} else {
- this.control.setValue(this.editorElement.innerHTML);
+ // Don't emit event so our valueChanges doesn't get notified by this change.
+ this.control.setValue(this.editorElement.innerHTML, {emitEvent: false});
this.textarea.value = this.editorElement.innerHTML;
}
} else {
if (this.isNullOrWhiteSpace(this.textarea.value)) {
this.clearText();
} else {
- this.control.setValue(this.textarea.value);
+ // Don't emit event so our valueChanges doesn't get notified by this change.
+ this.control.setValue(this.textarea.value, {emitEvent: false});
}
}
@@ -183,7 +192,8 @@ export class CoreRichTextEditorComponent {
clearText(): void {
this.editorElement.innerHTML = '';
this.textarea.value = '';
- this.control.setValue(null);
+ // Don't emit event so our valueChanges doesn't get notified by this change.
+ this.control.setValue(null, {emitEvent: false});
}
/**
@@ -199,4 +209,11 @@ export class CoreRichTextEditorComponent {
$event.stopPropagation();
document.execCommand(command, false, parameters);
}
+
+ /**
+ * Component being destroyed.
+ */
+ ngOnDestroy(): void {
+ this.valueChangeSubscription && this.valueChangeSubscription.unsubscribe();
+ }
}