Angular Component composition via ReactJS style API
This is my recomendation how to write angular components with leveraging react style component API naming.
And Remember, Respekt is everything! :)
Notes
ny
- is not new york :D, it's our meetup group prefix ngParty, come to our meetup yo ngParty
@TODO
- Tweak examples via ES7/TS decorators
- some other todo
// UI State model
class NyComponentState{
// if the tooltip should be shown
// component decorators can modify this property.
shouldShow: boolean;
// if the tooltip is currently being shown.
// Component decorators can use this property to test if the tooltip is being shown
isShowing: boolean;
// the message the tooltip contains. It is an angular expression
message: string;
// the x coordinate of the tooltip
x: number;
// the y coordinate of the tooltip
y: number;
constructor({shouldShow=true,isShowing=false,message='',x=0,y=0}=<NyComponentState>{}){
Object.assign(this,{shouldShow,isShowing,message,x,y})
}
}
interface IComponent<T> extends IDirective<T>{}
interface IDirective<T>{
state: T
setState(T): void,
componentDidMount(...args): void
componentWillUnmount(...args): void;
}
// Component definition
class NyComponent implements IComponent<NyComponentState>{
// #state
private state: NyComponentState;
private ngModel: ng.INgModelController;
// #getInitialState
constructor(private ComponentState: NyComponentState){
this.setState(ComponentState())
}
// #setState
private setState(statePropertiesToSet){
Object.assign(this.state, statePropertiesToSet)
}
// #componentDidMount
componentDidMount(ngModel){
// assign provided controllers
this.ngModel = ngModel;
}
// #componentWillUnmount
componentWillUnmount(){
// paste here logic that you want handle on $scope#$destroy
}
// some custom controller method for changing position
setPosition(x: number, y: number) {
this.setState({x,y})
}
}
function nyComponent(){
return {
controller: NyComponent,
controllerAs:'ctrl',
scope: {},
bindToController:{},
require:['nyComponent','ngModel'],
link: (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes, controller: [NyComponent, ng.INgModelController])=>{
// set controllers
const [ctrl, ngModel] = controller;
// handle mount
// we can pass aditional controllers if any are required and process the logic inside controller
ctrl.componentDidMount(ngModel);
// handle destroy
scope.$on('$destroy',ctrl.componentWillUnmount);
// handle host events
//
// @Todo move this to decorator
// - this will be handled via
// @Component({
// host:{
// '(focus)': 'onFocus()',
// '(mouseenter)': 'logCoordinates()'
// }
// })
const hostListeners = {
'focus': ctrl.onFocus,
'mouseenter': ctrl.logCoordinates
};
angular.forEeach(hostListeners,(listener, eventName)=>{ element.on(eventName, listener.bind(ctrl)); });
element.on('$destroy',(e)=>element.off(...Object.keys(hostListeners));
}
};
}
// register to angular
const ngModule = angular.module('nyReactStyleApp',[]);
ngModule
.directive(nyComponent.name, nyComponent)
.value('NyComponentState', NyComponentState);
angular.bootstrap(document,[ngModule.name]);