blob: 35c73a03c446a1ee92794030c4934f8dbcb250c3 [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 Scandoloe2643b92017-01-31 14:40:33 -080019 static $inject = ['$rootScope', '$compile', '$transitions', '$log'];
20
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,
27 private $log: ng.ILogService
Matteo Scandolo4222a432017-01-23 12:18:40 -080028 ) {
Matteo Scandoloe2643b92017-01-31 14:40:33 -080029 $transitions.onFinish({ to: '**' }, (transtion) => {
30 // wait for route transition to complete
31 transtion.promise.then(t => {
32 _.forEach(this.injectedComponents, (component: IXosInjectedComponent) => {
33 const container = $(component.targetEl);
34 // if we have the container, re-attach the component
35 if (container.length > 0) {
36 this.injectComponent(
37 container,
38 component.componentName,
39 component.attributes,
40 component.transclude,
41 component.clean
42 );
43 }
44 });
45 });
46 });
Matteo Scandolo4222a432017-01-23 12:18:40 -080047 }
48
49 public injectComponent(target: string | JQuery, componentName: string, attributes?: any, transclude?: string, clean?: boolean) {
50 let targetEl;
51 if (angular.isString(target)) {
Matteo Scandoloe2643b92017-01-31 14:40:33 -080052
53 if (target.indexOf('#') === -1) {
54 this.$log.warn(`[XosComponentInjector] Target element should be identified by an ID, you passed: ${target}`);
55 }
56
Matteo Scandolo4222a432017-01-23 12:18:40 -080057 targetEl = $(target);
58 }
59 else {
60 targetEl = target;
61 }
62
63 const componentTagName = this.camelToSnakeCase(componentName);
64 let scope = this.$rootScope.$new();
65 let attr: string = '';
66
67 if (clean) {
68 this.removeInjectedComponents(target);
69 }
70
71 if (angular.isDefined(attributes) && angular.isObject(attributes)) {
72 attr = this.stringifyAttributes(attributes);
73 scope = angular.merge(scope, attributes);
74 }
75
76 const componentTag = `<${componentTagName} ${attr}>${transclude || ''}</${componentTagName}>`;
77 const element = this.$compile(componentTag)(scope);
78
79 targetEl.append(element);
Matteo Scandoloe2643b92017-01-31 14:40:33 -080080
81 // store a reference for the element
82 this.storeInjectedComponent({
83 targetEl: angular.isString(target) ? target : '#' + targetEl.attr('id'),
84 componentName: componentName,
85 attributes: attributes,
86 transclude: transclude,
87 clean: clean,
88 });
Matteo Scandolo4222a432017-01-23 12:18:40 -080089 }
90
91 public removeInjectedComponents(target: string | JQuery) {
92 const targetEl = $(target);
93 targetEl.html('');
94 }
95
Matteo Scandoloe2643b92017-01-31 14:40:33 -080096 private isComponendStored(component: IXosInjectedComponent) {
97 return _.find(this.injectedComponents, (c: IXosInjectedComponent) => {
98 return c.targetEl === component.targetEl
99 && c.componentName === component.componentName
100 && _.isEqual(c.attributes, component.attributes)
101 && c.transclude === component.transclude
102 && c.clean === component.clean;
103 });
104 }
105
106 private storeInjectedComponent(component: IXosInjectedComponent) {
107 const isAlreadyStored = this.isComponendStored(component);
108 if (!isAlreadyStored) {
109 this.injectedComponents.push(component);
110 }
111 }
112
Matteo Scandolo4222a432017-01-23 12:18:40 -0800113 private stringifyAttributes(attributes: any): string {
114 return _.reduce(Object.keys(attributes), (string: string, a: string) => {
Matteo Scandolo837e0cc2017-01-25 11:37:34 -0800115 string += `${this.camelToSnakeCase(a)}="${a}"`;
Matteo Scandolo4222a432017-01-23 12:18:40 -0800116 return string;
117 }, '');
118 }
119
120 private camelToSnakeCase(name: string): string {
121 return name.split(/(?=[A-Z])/).map(w => w.toLowerCase()).join('-');
122 };
123}