Canh Ngo pushed to branch feature/cmng-psp1-CHANNELMGR-467 at cms-community / hippo-addon-channel-manager
Commits: 10e6e8a7 by Canh Ngo at 2016-03-17T14:18:29+01:00 CHANNELMGR-467: Moved all render calls to RenderingService Moved Hst calls to remove, add components to the HstService - - - - - 8 changed files: - frontend-ng/src/angularjs/api/hst.service.js - frontend-ng/src/angularjs/channel/page/componentRendering.service.js - frontend-ng/src/angularjs/channel/page/componentRendering.service.spec.js - frontend-ng/src/angularjs/channel/page/element/containerElement.js - frontend-ng/src/angularjs/channel/page/page.js - frontend-ng/src/angularjs/channel/page/pageStructure.service.js - + frontend-ng/src/angularjs/channel/page/rendering.service.js - + frontend-ng/src/angularjs/channel/page/rendering.service.spec.js Changes: ===================================== frontend-ng/src/angularjs/api/hst.service.js ===================================== --- a/frontend-ng/src/angularjs/api/hst.service.js +++ b/frontend-ng/src/angularjs/api/hst.service.js @@ -83,4 +83,29 @@ export class HstService { return apiUrl; } + /** + * Add a component to the specified container. + * + * @param catalogComponent + * @param containerId + * @returns {*} the request promise + */ + addHstComponent(catalogComponent, containerId) { + const requestPayload = `data: { + parentId: ${containerId}, + id: ${catalogComponent.id}, + name: ${catalogComponent.name}, + label: ${catalogComponent.label}, + type: ${catalogComponent.type}, + template: ${catalogComponent.template}, + componentClassName: ${catalogComponent.componentClassName}, + xtype: ${catalogComponent.xtype}, + children: [] + }`; + return this.doPost(requestPayload, containerId, 'create', catalogComponent.id); + } + + removeHstComponent(containerId, componentId) { + return this.doGet(containerId, 'delete', componentId); + } } ===================================== frontend-ng/src/angularjs/channel/page/componentRendering.service.js ===================================== --- a/frontend-ng/src/angularjs/channel/page/componentRendering.service.js +++ b/frontend-ng/src/angularjs/channel/page/componentRendering.service.js @@ -16,13 +16,14 @@ export class ComponentRenderingService { - constructor($http, $log, CmsService, PageStructureService) { + constructor($http, $log, CmsService, PageStructureService, RenderingService) { 'ngInject'; this.$http = $http; this.$log = $log; this.CmsService = CmsService; this.PageStructureService = PageStructureService; + this.RenderingService = RenderingService; } initialize() { @@ -32,37 +33,11 @@ export class ComponentRenderingService { _renderComponent(componentId, propertiesMap) { const component = this.PageStructureService.getComponentById(componentId); if (component) { - this._fetchHtml(component, propertiesMap).then((response) => { - this.PageStructureService.replaceComponent(component, response.data); + this.RenderingService.fetchComponentMarkup(component, propertiesMap).then((response) => { + this.PageStructureService.updateComponent(component, response.data); }); } else { this.$log.warn(`Cannot render unknown component '${componentId}'`); } } - - _fetchHtml(component, propertiesMap) { - function toUrlEncodedFormData(json) { - const keyValuePairs = []; - for (const property in json) { - if (json.hasOwnProperty(property)) { - const key = encodeURIComponent(property); - const value = encodeURIComponent(json[property]); - keyValuePairs.push(`${key}=${value}`); - } - } - return keyValuePairs.join('&'); - } - - return this.$http({ - method: 'POST', - url: component.getRenderUrl(), - headers: { - Accept: 'text/html, */*', - 'Content-Type': 'application/x-www-form-urlencoded', - }, - data: propertiesMap, - transformRequest: toUrlEncodedFormData, - }); - } - } ===================================== frontend-ng/src/angularjs/channel/page/componentRendering.service.spec.js ===================================== --- a/frontend-ng/src/angularjs/channel/page/componentRendering.service.spec.js +++ b/frontend-ng/src/angularjs/channel/page/componentRendering.service.spec.js @@ -18,16 +18,18 @@ describe('ComponentRenderingService', () => { 'use strict'; let PageStructureService; - let $httpBackend; + let RenderingService; let $log; + let $q; beforeEach(() => { module('hippo-cm.channel.page'); - inject((_$httpBackend_, _$log_, _PageStructureService_) => { - $httpBackend = _$httpBackend_; + inject((_$httpBackend_, _$q_, _$log_, _PageStructureService_, _RenderingService_) => { $log = _$log_; + $q = _$q_; PageStructureService = _PageStructureService_; + RenderingService = _RenderingService_; }); }); @@ -35,29 +37,18 @@ describe('ComponentRenderingService', () => { jasmine.getFixtures().load('channel/page/componentRendering.service.fixture.html'); }); - afterEach(() => { - $httpBackend.verifyNoOutstandingExpectation(); - $httpBackend.verifyNoOutstandingRequest(); - }); - it('renders a component', () => { - const component = jasmine.createSpyObj('component', ['getRenderUrl', 'getJQueryElement']); + const component = jasmine.createSpyObj('component', ['getJQueryElement']); const iframeElement = $j('#component'); - component.getRenderUrl.and.returnValue('/test-render-url'); component.getJQueryElement.and.returnValue(iframeElement); spyOn(PageStructureService, 'getComponent').and.returnValue(component); - spyOn(PageStructureService, 'replaceComponent'); - $httpBackend.whenPOST('/test-render-url').respond('<div>component markup</div>'); + spyOn(PageStructureService, 'updateComponent'); + spyOn(RenderingService, 'fetchComponentMarkup').and.returnValue($q.when('{ data: <div>component markup</div> }')); window.CMS_TO_APP.publish('render-component', '1234', { foo: 1, bar: 'a:b' }); - $httpBackend.expectPOST('/test-render-url', 'foo=1&bar=a%3Ab', { - Accept: 'text/html, */*', - 'Content-Type': 'application/x-www-form-urlencoded', - }); - $httpBackend.flush(); - + expect(RenderingService.fetchComponentMarkup).toHaveBeenCalledWith(component, { foo: 1, bar: 'a:b' }); expect(PageStructureService.replaceComponent).toHaveBeenCalledWith(component, '<div>component markup</div>'); }); ===================================== frontend-ng/src/angularjs/channel/page/element/containerElement.js ===================================== --- a/frontend-ng/src/angularjs/channel/page/element/containerElement.js +++ b/frontend-ng/src/angularjs/channel/page/element/containerElement.js @@ -80,6 +80,17 @@ export class ContainerElement extends PageStructureElement { return this.items.find((item) => item.getJQueryElement('iframeBoxElement').is(iframeElement)); } + replaceComponent(oldComponent, newComponent) { + const index = this.items.indexOf(oldComponent); + if (index !== -1) { + this.items[index] = newComponent; + } + } + + hasComponent(component) { + return this.items.indexOf(component) !== -1; + } + getHstRepresentation() { return { data: { ===================================== frontend-ng/src/angularjs/channel/page/page.js ===================================== --- a/frontend-ng/src/angularjs/channel/page/page.js +++ b/frontend-ng/src/angularjs/channel/page/page.js @@ -14,6 +14,7 @@ * limitations under the License. */ +import { RenderingService } from './rendering.service'; import { ComponentRenderingService } from './componentRendering.service'; import { PageMetaDataService } from './pageMetaData.service'; import { PageStructureService } from './pageStructure.service'; @@ -21,6 +22,7 @@ import { run } from './page.run'; export const channelPageModule = angular .module('hippo-cm.channel.page', []) + .service('RenderingService', RenderingService) .service('ComponentRenderingService', ComponentRenderingService) .service('PageMetaDataService', PageMetaDataService) .service('PageStructureService', PageStructureService) ===================================== frontend-ng/src/angularjs/channel/page/pageStructure.service.js ===================================== --- a/frontend-ng/src/angularjs/channel/page/pageStructure.service.js +++ b/frontend-ng/src/angularjs/channel/page/pageStructure.service.js @@ -21,18 +21,18 @@ import { ComponentElement } from './element/componentElement'; export class PageStructureService { - constructor($log, $q, $http, HstConstants, hstCommentsProcessorService, OverlaySyncService, ChannelService, CmsService, PageMetaDataService, HstService) { + constructor($log, $q, HstConstants, hstCommentsProcessorService, RenderingService, OverlaySyncService, ChannelService, CmsService, PageMetaDataService, HstService) { 'ngInject'; // Injected this.$log = $log; - this.$http = $http; this.$q = $q; this.HST = HstConstants; this.HstService = HstService; this.ChannelService = ChannelService; this.CmsService = CmsService; this.hstCommentsProcessorService = hstCommentsProcessorService; + this.RenderingService = RenderingService; this.OverlaySyncService = OverlaySyncService; this.pageMetaData = PageMetaDataService; @@ -94,6 +94,10 @@ export class PageStructureService { return component; } + hasContainer(container) { + return this.containers.indexOf(container) !== -1; + } + /** * Remove the component identified by given Id * @param componentId @@ -104,18 +108,14 @@ export class PageStructureService { if (component) { const container = component.getContainer(); - return this._removeHstComponent(container.getId(), componentId) - .then(() => this.reloadContainer(container)); + this.HstService.removeHstComponent(container.getId(), componentId) + .then(() => this._renderContainer(container)); // TODO handle error } else { this.$log.debug(`Was asked to remove component with ID '${componentId}', but couldn't find it in the page structure.`); } } - _removeHstComponent(containerId, componentId) { - return this.HstService.doGet(containerId, 'delete', componentId); - } - getContainerByIframeElement(containerIFrameElement) { return this.containers.find((container) => container.getJQueryElement('iframeBoxElement').is(containerIFrameElement)); } @@ -144,67 +144,48 @@ export class PageStructureService { }); } - replaceComponent(component, newMarkup) { + /** + * Update the component with the new markup + */ + updateComponent(component, newMarkup) { const jQueryNodeCollection = component.replaceDOM(newMarkup); this._replaceComponent(component, this._createComponent(jQueryNodeCollection, component.getContainer())); this.OverlaySyncService.syncIframe(); } - _createComponent(jQueryNodeCollection, container) { - let component = null; - - this.hstCommentsProcessorService.processFragment(jQueryNodeCollection, (commentDomElement, metaData) => { - switch (metaData[this.HST.TYPE]) { - case this.HST.TYPE_COMPONENT: - try { - component = new ComponentElement(commentDomElement, metaData, container, this.hstCommentsProcessorService); - } catch (exception) { - this.$log.debug(exception, metaData); - } - break; - - default: - break; - } + _renderContainer(container) { + this.RenderingService.fetchContainerMarkup(container).then((response) => { + this._updateContainer(container, response.data); }); + } - if (!component) { - this.$log.error('Failed to create a new component'); - } - - return component; + _updateContainer(container, newMarkup) { + const jQueryContainerElement = container.replaceDOM(newMarkup); + this._replaceContainer(container, this._createContainer(jQueryContainerElement)); + this.OverlaySyncService.syncIframe(); // necessary? mutation observer should trigger this... } _replaceComponent(oldComponent, newComponent) { const container = oldComponent.getContainer(); - const index = container.items.indexOf(oldComponent); - if (index === -1) { + if (this.hasContainer(container) && container.hasComponent(oldComponent)) { + container.replaceComponent(oldComponent, newComponent); + } else { this.$log.warn('Cannot find component', oldComponent); - return; } - container.items[index] = newComponent; } addComponentToContainer(catalogComponent, overlayDomElementOfContainer) { - const oldContainer = this.containers.find((c) => c.getJQueryElement('overlay')[0] === overlayDomElementOfContainer); + const container = this.containers.find((c) => c.getJQueryElement('overlay')[0] === overlayDomElementOfContainer); - if (oldContainer) { - this._addHstComponent(catalogComponent, oldContainer.getId()) - .then(() => this.reloadContainer(oldContainer)); + if (container) { + this.HstService.addHstComponent(catalogComponent, container.getId()) + .then(() => this._renderContainer(container)); // TODO: handle error } else { - console.log('oldContainer not found'); + console.log('container not found'); } } - reloadContainer(container) { - this._fetchContainerMarkup(container).then((response) => { - const jQueryContainerElement = container.replaceDOM(response.data); - this._replaceContainer(container, this._createContainer(jQueryContainerElement)); - this.OverlaySyncService.syncIframe(); // necessary? mutation observer should trigger this... - }); - } - _replaceContainer(oldContainer, newContainer) { const index = this.containers.indexOf(oldContainer); if (index === -1) { @@ -214,27 +195,16 @@ export class PageStructureService { this.containers[index] = newContainer; } - _fetchContainerMarkup(container) { - return this.$http({ - method: 'GET', - url: container.getRenderUrl(), - header: { - Accept: 'text/html, */* ', - 'Content-Type': 'application/x-www-form-urlencoded', - }, - }); - } - /** * Create a new container with meta-data from the given markup value * @param markup * @returns {*} * @private */ - _createContainer(jQueryContainerElement) { + _createContainer(jQueryNodeCollection) { let container = null; - this.hstCommentsProcessorService.processFragment(jQueryContainerElement, function (commentDomElement, metaData) { + this.hstCommentsProcessorService.processFragment(jQueryNodeCollection, (commentDomElement, metaData) => { switch (metaData[this.HST.TYPE]) { case this.HST.TYPE_CONTAINER: if (!container) { @@ -262,7 +232,7 @@ export class PageStructureService { default: break; } - }.bind(this)); + }); if (!container) { this.$log.error('Failed to create a new container'); @@ -272,23 +242,30 @@ export class PageStructureService { } /** - * Add the component to the container at back-end - * @param componentId - * @param containerId - * @returns {*} - * @private + * Create a new component with meta-data from the given markup value */ - _addHstComponent(catalogComponent, containerId) { - const requestPayload = `data: { - parentId: ${containerId}, - id: ${catalogComponent.id}, - name: ${catalogComponent.name}, - label: ${catalogComponent.label}, - type: ${catalogComponent.type}, - template: ${catalogComponent.template}, - componentClassName: ${catalogComponent.componentClassName}, - xtype: ${catalogComponent.xtype}, - }`; - return this.HstService.doPost(requestPayload, containerId, 'create', catalogComponent.id); + _createComponent(jQueryNodeCollection, container) { + let component = null; + + this.hstCommentsProcessorService.processFragment(jQueryNodeCollection, (commentDomElement, metaData) => { + switch (metaData[this.HST.TYPE]) { + case this.HST.TYPE_COMPONENT: + try { + component = new ComponentElement(commentDomElement, metaData, container, this.hstCommentsProcessorService); + } catch (exception) { + this.$log.debug(exception, metaData); + } + break; + + default: + break; + } + }); + + if (!component) { + this.$log.error('Failed to create a new component'); + } + + return component; } } ===================================== frontend-ng/src/angularjs/channel/page/rendering.service.js ===================================== --- /dev/null +++ b/frontend-ng/src/angularjs/channel/page/rendering.service.js @@ -0,0 +1,64 @@ +/* + * + * * Copyright 2016 Hippo B.V. (http://www.onehippo.com) + * * + * * 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. + * + */ + +export class RenderingService { + + constructor($http, $log) { + 'ngInject'; + + this.$http = $http; + this.$log = $log; + } + + fetchContainerMarkup(container) { + return this.$http({ + method: 'GET', + url: container.getRenderUrl(), + header: { + Accept: 'text/html, */* ', + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }); + } + + fetchComponentMarkup(component, propertiesMap) { + function toUrlEncodedFormData(json) { + const keyValuePairs = []; + for (const property in json) { + if (json.hasOwnProperty(property)) { + const key = encodeURIComponent(property); + const value = encodeURIComponent(json[property]); + keyValuePairs.push(`${key}=${value}`); + } + } + return keyValuePairs.join('&'); + } + + return this.$http({ + method: 'POST', + url: component.getRenderUrl(), + headers: { + Accept: 'text/html, */*', + 'Content-Type': 'application/x-www-form-urlencoded', + }, + data: propertiesMap, + transformRequest: toUrlEncodedFormData, + }); + } + +} ===================================== frontend-ng/src/angularjs/channel/page/rendering.service.spec.js ===================================== --- /dev/null +++ b/frontend-ng/src/angularjs/channel/page/rendering.service.spec.js @@ -0,0 +1,69 @@ +/* + * + * * Copyright 2016 Hippo B.V. (http://www.onehippo.com) + * * + * * 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. + * + */ + +describe('RenderingService', () => { + 'use strict'; + + let RenderingService; + let $httpBackend; + + beforeEach(() => { + module('hippo-cm.channel.page'); + + inject((_RenderingService_, _$httpBackend_) => { + $httpBackend = _$httpBackend_; + RenderingService = _RenderingService_; + }); + }); + + + afterEach(() => { + $httpBackend.verifyNoOutstandingExpectation(); + $httpBackend.verifyNoOutstandingRequest(); + }); + + it('fetch a component markup', () => { + const component = jasmine.createSpyObj('component', ['getRenderUrl']); + + component.getRenderUrl.and.returnValue('/test-render-url'); + $httpBackend.whenPOST('/test-render-url').respond('<div>component markup</div>'); + + RenderingService.fetchComponentMarkup(component, { foo: 1, bar: 'a:b' }); + + $httpBackend.expectPOST('/test-render-url', 'foo=1&bar=a%3Ab', { + Accept: 'text/html, */*', + 'Content-Type': 'application/x-www-form-urlencoded', + }); + $httpBackend.flush(); + }); + + it('fetch a container markup', () => { + const container = jasmine.createSpyObj('container', ['getRenderUrl']); + + container.getRenderUrl.and.returnValue('/test-render-url'); + $httpBackend.whenGET('/test-render-url').respond('<div>container markup</div>'); + + RenderingService.fetchContainerMarkup(container); + + $httpBackend.expectGET('/test-render-url', { + Accept: 'text/html, */*', + 'Content-Type': 'application/x-www-form-urlencoded', + }); + $httpBackend.flush(); + }); +}); View it on GitLab: https://code.onehippo.org/cms-community/hippo-addon-channel-manager/commit/10e6e8a7c47c49335b7903004e20a7838458be35
_______________________________________________ Hippocms-svn mailing list Hippocms-svn@lists.onehippo.org https://lists.onehippo.org/mailman/listinfo/hippocms-svn