blob: 9fc475b36701101d5309889cdb1269a764220ddc [file] [log] [blame]
Matteo Scandolo4222a432017-01-23 12:18:40 -08001import * as $ from 'jquery';
2import * as _ from 'lodash';
3
4export interface IXosComponentInjectorService {
Matteo Scandoloe2643b92017-01-31 14:40:33 -08005 injectedComponents: IXosInjectedComponent[];
Matteo Scandolo4222a432017-01-23 12:18:40 -08006 injectComponent(target: string | JQuery, componentName: string, attributes?: any, transclude?: string, clean?: boolean): void;
7 removeInjectedComponents(target: string | JQuery): void;
8}
9
Matteo Scandoloe2643b92017-01-31 14:40:33 -080010export interface IXosInjectedComponent {
11 targetEl: string;
12 componentName: string;
13 attributes?: any;
14 transclude?: string;
15 clean?: boolean;
16}
17
Matteo Scandolo4222a432017-01-23 12:18:40 -080018export class XosComponentInjector implements IXosComponentInjectorService {
Matteo Scandolo5053cbe2017-01-31 17:37:56 -080019 static $inject = ['$rootScope', '$compile', '$transitions', '$log', '$timeout'];
Matteo Scandoloe2643b92017-01-31 14:40:33 -080020
21 public injectedComponents: IXosInjectedComponent[] = [];
Matteo Scandolo4222a432017-01-23 12:18:40 -080022
23 constructor (
24 private $rootScope: ng.IRootScopeService,
Matteo Scandoloe2643b92017-01-31 14:40:33 -080025 private $compile: ng.ICompileService,
26 private $transitions: any,
Matteo Scandolo5053cbe2017-01-31 17:37:56 -080027 private $log: ng.ILogService,
28 private $timeout: ng.ITimeoutService
Matteo Scandolo4222a432017-01-23 12:18:40 -080029 ) {
Matteo Scandoloe2643b92017-01-31 14:40:33 -080030 $transitions.onFinish({ to: '**' }, (transtion) => {
31 // wait for route transition to complete
32 transtion.promise.then(t => {
33 _.forEach(this.injectedComponents, (component: IXosInjectedComponent) => {
34 const container = $(component.targetEl);
35 // if we have the container, re-attach the component
36 if (container.length > 0) {
37 this.injectComponent(
38 container,
39 component.componentName,
40 component.attributes,
41 component.transclude,
42 component.clean
43 );
44 }
45 });
46 });
47 });
Matteo Scandolo4222a432017-01-23 12:18:40 -080048 }
49
50 public injectComponent(target: string | JQuery, componentName: string, attributes?: any, transclude?: string, clean?: boolean) {
51 let targetEl;
52 if (angular.isString(target)) {
Matteo Scandoloe2643b92017-01-31 14:40:33 -080053
54 if (target.indexOf('#') === -1) {
55 this.$log.warn(`[XosComponentInjector] Target element should be identified by an ID, you passed: ${target}`);
56 }
57
Matteo Scandolo4222a432017-01-23 12:18:40 -080058 targetEl = $(target);
59 }
60 else {
61 targetEl = target;
62 }
63
64 const componentTagName = this.camelToSnakeCase(componentName);
65 let scope = this.$rootScope.$new();
66 let attr: string = '';
67
68 if (clean) {
69 this.removeInjectedComponents(target);
70 }
71
72 if (angular.isDefined(attributes) && angular.isObject(attributes)) {
73 attr = this.stringifyAttributes(attributes);
Matteo Scandolo520a8a12017-03-10 17:31:37 -080074 // we cannot use angular.merge as it cast Resource to Objects
75 _.forEach(Object.keys(attributes), (k: string) => {
76 scope[k] = attributes[k];
77 });
Matteo Scandolo4222a432017-01-23 12:18:40 -080078 }
79
80 const componentTag = `<${componentTagName} ${attr}>${transclude || ''}</${componentTagName}>`;
81 const element = this.$compile(componentTag)(scope);
82
Matteo Scandolo5053cbe2017-01-31 17:37:56 -080083 this.$timeout(function() {
84 scope.$apply();
85 });
86
Matteo Scandolo4222a432017-01-23 12:18:40 -080087 targetEl.append(element);
Matteo Scandoloe2643b92017-01-31 14:40:33 -080088
89 // store a reference for the element
90 this.storeInjectedComponent({
91 targetEl: angular.isString(target) ? target : '#' + targetEl.attr('id'),
92 componentName: componentName,
93 attributes: attributes,
94 transclude: transclude,
95 clean: clean,
96 });
Matteo Scandolo4222a432017-01-23 12:18:40 -080097 }
98
99 public removeInjectedComponents(target: string | JQuery) {
100 const targetEl = $(target);
101 targetEl.html('');
102 }
103
Matteo Scandoloe2643b92017-01-31 14:40:33 -0800104 private isComponendStored(component: IXosInjectedComponent) {
105 return _.find(this.injectedComponents, (c: IXosInjectedComponent) => {
106 return c.targetEl === component.targetEl
107 && c.componentName === component.componentName
108 && _.isEqual(c.attributes, component.attributes)
109 && c.transclude === component.transclude
110 && c.clean === component.clean;
111 });
112 }
113
114 private storeInjectedComponent(component: IXosInjectedComponent) {
115 const isAlreadyStored = this.isComponendStored(component);
116 if (!isAlreadyStored) {
117 this.injectedComponents.push(component);
118 }
119 }
120
Matteo Scandolo4222a432017-01-23 12:18:40 -0800121 private stringifyAttributes(attributes: any): string {
122 return _.reduce(Object.keys(attributes), (string: string, a: string) => {
Matteo Scandolo837e0cc2017-01-25 11:37:34 -0800123 string += `${this.camelToSnakeCase(a)}="${a}"`;
Matteo Scandolo4222a432017-01-23 12:18:40 -0800124 return string;
125 }, '');
126 }
127
128 private camelToSnakeCase(name: string): string {
129 return name.split(/(?=[A-Z])/).map(w => w.toLowerCase()).join('-');
130 };
131}