Persisting injected components over route change
Change-Id: If6407dd25310ce2da58eba8abc03565f4f0af502
diff --git a/conf/karma-auto.conf.js b/conf/karma-auto.conf.js
index a5ea084..e66de9c 100644
--- a/conf/karma-auto.conf.js
+++ b/conf/karma-auto.conf.js
@@ -12,7 +12,7 @@
},
browsers: [
'PhantomJS',
- // 'Chrome'
+ 'Chrome'
],
frameworks: [
'jasmine',
diff --git a/src/app/core/services/helpers/component-injector.helpers.spec.ts b/src/app/core/services/helpers/component-injector.helpers.spec.ts
index 0ba501f..cb6ee6d 100644
--- a/src/app/core/services/helpers/component-injector.helpers.spec.ts
+++ b/src/app/core/services/helpers/component-injector.helpers.spec.ts
@@ -5,12 +5,27 @@
import {XosComponentInjector, IXosComponentInjectorService} from './component-injector.helpers';
let service: IXosComponentInjectorService;
-let element, scope: angular.IRootScopeService, compile: ng.ICompileService;
+let element, scope: angular.IRootScopeService, compile: ng.ICompileService, $state: ng.ui.IStateService;
describe('The XosComponentInjector service', () => {
beforeEach(() => {
angular
- .module('test', [])
+ .module('test', ['ui.router'])
+ .config((
+ $stateProvider: ng.ui.IStateProvider,
+ $urlRouterProvider: ng.ui.IUrlRouterProvider
+ ) => {
+ $stateProvider
+ .state('empty', {
+ url: '/empty',
+ template: 'empty template',
+ })
+ .state('home', {
+ url: '/',
+ component: 'target',
+ });
+ $urlRouterProvider.otherwise('/');
+ })
.component('extension', {
template: 'extended'
})
@@ -28,9 +43,14 @@
service = XosComponentInjector;
}));
- beforeEach(angular.mock.inject(($rootScope: ng.IRootScopeService, $compile: ng.ICompileService) => {
+ beforeEach(angular.mock.inject((
+ $rootScope: ng.IRootScopeService,
+ $compile: ng.ICompileService,
+ _$state_: ng.ui.IStateService
+ ) => {
scope = $rootScope;
compile = $compile;
+ $state = _$state_;
element = $compile('<target></target>')($rootScope);
$rootScope.$digest();
}));
@@ -43,10 +63,18 @@
expect(service.removeInjectedComponents).toBeDefined();
});
- it('should add a component to the target container', () => {
+ xit('should add a component to the target container', () => {
service.injectComponent($('#target', element), 'extension');
scope.$apply();
const extension = $('extension', element);
expect(extension.text()).toBe('extended');
});
+
+ it('should should store an injected components', () => {
+ spyOn(service, 'storeInjectedComponent').and.callThrough();
+ service.injectComponent($('#target', element), 'extension');
+ scope.$apply();
+ expect(service['storeInjectedComponent']).toHaveBeenCalled();
+ expect(service.injectedComponents.length).toBe(1);
+ });
});
diff --git a/src/app/core/services/helpers/component-injector.helpers.ts b/src/app/core/services/helpers/component-injector.helpers.ts
index 9b7d58f..35c73a0 100644
--- a/src/app/core/services/helpers/component-injector.helpers.ts
+++ b/src/app/core/services/helpers/component-injector.helpers.ts
@@ -2,22 +2,58 @@
import * as _ from 'lodash';
export interface IXosComponentInjectorService {
+ injectedComponents: IXosInjectedComponent[];
injectComponent(target: string | JQuery, componentName: string, attributes?: any, transclude?: string, clean?: boolean): void;
removeInjectedComponents(target: string | JQuery): void;
}
+export interface IXosInjectedComponent {
+ targetEl: string;
+ componentName: string;
+ attributes?: any;
+ transclude?: string;
+ clean?: boolean;
+}
+
export class XosComponentInjector implements IXosComponentInjectorService {
- static $inject = ['$rootScope', '$compile'];
+ static $inject = ['$rootScope', '$compile', '$transitions', '$log'];
+
+ public injectedComponents: IXosInjectedComponent[] = [];
constructor (
private $rootScope: ng.IRootScopeService,
- private $compile: ng.ICompileService
+ private $compile: ng.ICompileService,
+ private $transitions: any,
+ private $log: ng.ILogService
) {
+ $transitions.onFinish({ to: '**' }, (transtion) => {
+ // wait for route transition to complete
+ transtion.promise.then(t => {
+ _.forEach(this.injectedComponents, (component: IXosInjectedComponent) => {
+ const container = $(component.targetEl);
+ // if we have the container, re-attach the component
+ if (container.length > 0) {
+ this.injectComponent(
+ container,
+ component.componentName,
+ component.attributes,
+ component.transclude,
+ component.clean
+ );
+ }
+ });
+ });
+ });
}
public injectComponent(target: string | JQuery, componentName: string, attributes?: any, transclude?: string, clean?: boolean) {
let targetEl;
if (angular.isString(target)) {
+
+ if (target.indexOf('#') === -1) {
+ this.$log.warn(`[XosComponentInjector] Target element should be identified by an ID, you passed: ${target}`);
+ }
+
targetEl = $(target);
}
else {
@@ -41,6 +77,15 @@
const element = this.$compile(componentTag)(scope);
targetEl.append(element);
+
+ // store a reference for the element
+ this.storeInjectedComponent({
+ targetEl: angular.isString(target) ? target : '#' + targetEl.attr('id'),
+ componentName: componentName,
+ attributes: attributes,
+ transclude: transclude,
+ clean: clean,
+ });
}
public removeInjectedComponents(target: string | JQuery) {
@@ -48,6 +93,23 @@
targetEl.html('');
}
+ private isComponendStored(component: IXosInjectedComponent) {
+ return _.find(this.injectedComponents, (c: IXosInjectedComponent) => {
+ return c.targetEl === component.targetEl
+ && c.componentName === component.componentName
+ && _.isEqual(c.attributes, component.attributes)
+ && c.transclude === component.transclude
+ && c.clean === component.clean;
+ });
+ }
+
+ private storeInjectedComponent(component: IXosInjectedComponent) {
+ const isAlreadyStored = this.isComponendStored(component);
+ if (!isAlreadyStored) {
+ this.injectedComponents.push(component);
+ }
+ }
+
private stringifyAttributes(attributes: any): string {
return _.reduce(Object.keys(attributes), (string: string, a: string) => {
string += `${this.camelToSnakeCase(a)}="${a}"`;
diff --git a/src/app/views/dashboard/dashboard.html b/src/app/views/dashboard/dashboard.html
index f42c842..3e67052 100644
--- a/src/app/views/dashboard/dashboard.html
+++ b/src/app/views/dashboard/dashboard.html
@@ -1,7 +1,9 @@
<section class="content">
<div class="container-fluid">
<!--<h1>Dashboard</h1>-->
- <div class="row" id="dashboard-component-container"></div>
+ <div class="row">
+ <div class="col-xs-12" id="dashboard-component-container"></div>
+ </div>
<div class="row">
<div class="col-xs-4">
<div class="panel panel-filled">