blob: e70e257dd88b4468db483645e73515bffef2e604 [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
Matteo Scandolo4e870232017-01-30 13:43:05 -080049 // FIXME
50 // component are correctly injected but not persisted,
51 // if I change route they go away
Matteo Scandolo4222a432017-01-23 12:18:40 -080052 public injectComponent(target: string | JQuery, componentName: string, attributes?: any, transclude?: string, clean?: boolean) {
53 let targetEl;
54 if (angular.isString(target)) {
Matteo Scandoloe2643b92017-01-31 14:40:33 -080055
56 if (target.indexOf('#') === -1) {
57 this.$log.warn(`[XosComponentInjector] Target element should be identified by an ID, you passed: ${target}`);
58 }
59
Matteo Scandolo4222a432017-01-23 12:18:40 -080060 targetEl = $(target);
61 }
62 else {
63 targetEl = target;
64 }
65
66 const componentTagName = this.camelToSnakeCase(componentName);
67 let scope = this.$rootScope.$new();
68 let attr: string = '';
69
70 if (clean) {
71 this.removeInjectedComponents(target);
72 }
73
74 if (angular.isDefined(attributes) && angular.isObject(attributes)) {
75 attr = this.stringifyAttributes(attributes);
76 scope = angular.merge(scope, attributes);
77 }
78
79 const componentTag = `<${componentTagName} ${attr}>${transclude || ''}</${componentTagName}>`;
80 const element = this.$compile(componentTag)(scope);
81
82 targetEl.append(element);
Matteo Scandoloe2643b92017-01-31 14:40:33 -080083
84 // store a reference for the element
85 this.storeInjectedComponent({
86 targetEl: angular.isString(target) ? target : '#' + targetEl.attr('id'),
87 componentName: componentName,
88 attributes: attributes,
89 transclude: transclude,
90 clean: clean,
91 });
Matteo Scandolo4222a432017-01-23 12:18:40 -080092 }
93
94 public removeInjectedComponents(target: string | JQuery) {
95 const targetEl = $(target);
96 targetEl.html('');
97 }
98
Matteo Scandoloe2643b92017-01-31 14:40:33 -080099 private isComponendStored(component: IXosInjectedComponent) {
100 return _.find(this.injectedComponents, (c: IXosInjectedComponent) => {
101 return c.targetEl === component.targetEl
102 && c.componentName === component.componentName
103 && _.isEqual(c.attributes, component.attributes)
104 && c.transclude === component.transclude
105 && c.clean === component.clean;
106 });
107 }
108
109 private storeInjectedComponent(component: IXosInjectedComponent) {
110 const isAlreadyStored = this.isComponendStored(component);
111 if (!isAlreadyStored) {
112 this.injectedComponents.push(component);
113 }
114 }
115
Matteo Scandolo4222a432017-01-23 12:18:40 -0800116 private stringifyAttributes(attributes: any): string {
117 return _.reduce(Object.keys(attributes), (string: string, a: string) => {
Matteo Scandolo837e0cc2017-01-25 11:37:34 -0800118 string += `${this.camelToSnakeCase(a)}="${a}"`;
Matteo Scandolo4222a432017-01-23 12:18:40 -0800119 return string;
120 }, '');
121 }
122
123 private camelToSnakeCase(name: string): string {
124 return name.split(/(?=[A-Z])/).map(w => w.toLowerCase()).join('-');
125 };
126}