src/component.js
import angular from 'angular';
import {Annotation} from './annotation';
import {Annotations} from './annotations';
import {addStaticGetterObjectMember, addStaticGetter} from './utils';
export class ComponentEvent {
expression = null;
fire(locals) {
this.expression(locals);
}
}
export class ComponentAnnotation extends Annotation {
get controllerCls() {
const annotation = this;
const TargetCls = this.targetCls;
const flags = this.flags;
class ControllerCls extends TargetCls {
constructor($scope, $log) {
const injected = Array.from(arguments).slice(2);
super(...injected);
annotation.applyInjectionBindings(this, injected);
annotation.applyDecorators(this);
if (flags) {
Object.keys(flags).forEach(flag => {
const property = `_${flag}Flag`;
Reflect.defineProperty(this, flag, {
get: () => angular.isDefined(this[property]) ? this[property] !== 'false' : false
});
});
}
if (this.onDestroy instanceof Function) {
$scope.$on('$destroy', this.onDestroy.bind(this));
}
if (this.activate instanceof Function) {
this.activate();
}
this.fireComponentEvent = (event, locals) => {
$log.warn(`
Component.fireComponentEvent() has been deprecated in Anglue 1.x.
Please use @Event() myEvent; in combination with this.myEvent.fire().
`);
if (this._eventHandlers && this._eventHandlers[event]) {
Reflect.apply(this._eventHandlers[event], this, [locals]);
}
};
}
}
return ControllerCls;
}
getInjectionTokens() {
return ['$scope', '$log'].concat(super.getInjectionTokens());
}
get dependencies() {
const targetCls = this.targetCls;
return [].concat(
targetCls.dependencies || [],
Annotation.getModuleNames(targetCls.components)
);
}
get template() {
return this.targetCls.template || null;
}
get bindings() {
return this.targetCls.bindings || null;
}
get events() {
return this.targetCls.events || null;
}
get flags() {
return this.targetCls.flags || null;
}
//noinspection InfiniteRecursionJS
get getDirective() {
return this.targetCls.getDirective || function(config) {
return function() {
return config;
};
};
}
get module() {
if (!this._module) {
const name = this.name;
const template = this.template;
const bindings = this.bindings;
const events = this.events;
this._module = angular.module(
`components.${name}`,
this.dependencies
);
const directiveConfig = {
restrict: 'EA',
controllerAs: name,
bindToController: true,
scope: true,
controller: this.getInjectionTokens().concat([this.controllerCls])
};
if (template) {
if (template.url) {
directiveConfig.templateUrl = template.url;
} else if (template.inline) {
directiveConfig.template = template.inline;
}
if (template.replace) {
directiveConfig.replace = true;
}
}
if (bindings) {
const scope = directiveConfig.scope = {};
for (const binding of Object.keys(bindings)) {
let attr = bindings[binding];
if (!attr[0].match(/(&|=|@)/)) {
attr = `=${attr}`;
}
scope[binding] = attr;
}
}
if (events) {
directiveConfig.link = (scope, el, attr, ctrl) => {
if (events) {
const eventHandlers = ctrl._eventHandlers = {};
Object.keys(events).forEach(event => {
if (attr[event]) {
eventHandlers[events[event]] = locals => {
scope.$parent.$eval(attr[event], locals);
};
const componentEventName = events[event];
if (ctrl[componentEventName] instanceof ComponentEvent) {
ctrl[componentEventName].expression = ctrl[`_${componentEventName}Expression`];
}
}
});
}
};
}
this._module.directive(name, this.getDirective(directiveConfig));
this.configure(this._module);
}
return this._module;
}
}
export default ComponentAnnotation;
export function Component(config) {
return cls => {
let componentName;
const isConfigObject = angular.isObject(config);
if (isConfigObject && config.name) {
componentName = config.name;
} else if (angular.isString(config)) {
componentName = config;
} else {
const clsName = cls.name.replace(/component$/i, '');
componentName = `${clsName[0].toLowerCase()}${clsName.slice(1)}`;
}
if (isConfigObject && config.dependencies) {
addStaticGetter(cls, 'dependencies', () => config.dependencies);
}
addStaticGetter(cls, 'annotation', () => Annotations.getComponent(componentName, cls));
};
}
export function View(config) {
return cls => {
if (config.template) {
addStaticGetter(cls, 'template', () => ({
inline: config.template,
replace: config.replace || false
}));
} else if (config.templateUrl) {
addStaticGetter(cls, 'template', () => ({
url: config.templateUrl,
replace: config.replace || false
}));
}
if (config.components) {
addStaticGetter(cls, 'components', () => config.components);
}
};
}
export function Binding(config) {
return (cls, propertyName, descriptor) => {
const isConfigObject = angular.isObject(config);
let attribute = propertyName;
if (isConfigObject && config.attribute) {
attribute = config.attribute;
} else if (angular.isString(config)) {
attribute = config;
}
if (isConfigObject && config.expression === true) {
attribute = `&${attribute}`;
} else if (isConfigObject && config.string === true) {
attribute = `@${attribute}`;
} else {
attribute = `=${attribute}`;
}
// For some reason these property initializers are called after
// the bindings have already been set by angular. This causes
// them to be set back to undefined again. Thus this override
// to make sure we just return whatever the value was already.
if (descriptor.initializer !== undefined) {
descriptor.initializer = function() {
return this[propertyName];
};
}
addStaticGetterObjectMember(cls.constructor, 'bindings', propertyName, attribute);
};
}
export function Flag(config) {
return (cls, propertyName) => {
const attribute = config || propertyName;
addStaticGetterObjectMember(cls.constructor, 'flags', propertyName, attribute);
const propertyBinding = `_${propertyName}Flag`;
const attributeBinding = `@${attribute}`;
addStaticGetterObjectMember(cls.constructor, 'bindings', propertyBinding, attributeBinding);
};
}
export function Event() {
return (cls, propertyName, descriptor) => {
const attribute = `on${propertyName[0].toUpperCase()}${propertyName.slice(1)}`;
if (!descriptor.initializer) {
descriptor.initializer = () => {
return new ComponentEvent();
};
}
addStaticGetterObjectMember(cls.constructor, 'events', attribute, propertyName);
addStaticGetterObjectMember(cls.constructor, 'bindings',
`_${propertyName}Expression`, `&${attribute}`);
};
}