web-atoms-mvvm
Version:
MVVM, REST Json Service, Message Subscriptions for Web Atoms
247 lines (211 loc) • 7.1 kB
text/typescript
namespace WebAtoms {
/**
* DisposableAction holds an action that
* will be executed when dispose will be called.
*
* subscribe(m,f):AtomDisposable{
* //...
* //subscribe to something...
* //...
* return new AtomDisposable(()=>{
*
* //...
* //unsubscribe from something
* //
*
* });
* }
*
* User can simply call dispose to make sure subscription was unsubscribed.
*
* @export
* @class DisposableAction
* @implements {AtomDisposable}
*/
export class DisposableAction implements AtomDisposable {
f:()=>void;
constructor(f:()=>void) {
this.f = f;
}
dispose():void {
this.f();
}
}
// tslint:disable-next-line
var AtomUI = window["AtomUI"];
// tslint:disable-next-line
var oldCreateControl = AtomUI.createControl;
// tslint:disable-next-line
AtomUI.createControl = function(element,type,data,newScope){
if(type) {
if(type.constructor === String || (typeof type) === "string") {
var t:any = WebAtoms[type] || Atom.get(window,type);
if(t) {
type = t;
}
}
}
return oldCreateControl.call(Atom,element,type,data,newScope);
};
Atom.post = function(f:()=>void):void {
(WebAtoms as any).dispatcher.callLater(f);
};
Atom.postAsync = function(f:()=>any):Promise<any> {
return new Promise((resolve,reject)=> {
(WebAtoms as any).dispatcher.callLater(async ()=> {
try {
var p:any = f();
if(p && p.then && p.catch) {
await p;
}
resolve();
} catch(e) {
reject(e);
}
});
});
};
Atom.using = function(d:AtomDisposable, f:()=>void):void {
try {
f();
} finally {
d.dispose();
}
};
Atom.usingAsync = async function(d:AtomDisposable, f:()=>Promise<any>):Promise<any> {
try {
await f();
} finally {
d.dispose();
}
};
Atom.watch = function(item:any, property:string, f: ()=>void):AtomDisposable {
AtomBinder.add_WatchHandler(item,property,f);
return new DisposableAction(()=> {
AtomBinder.remove_WatchHandler(item,property,f);
});
};
Atom.delay = function(n:number, ct?:CancelToken):Promise<any> {
return new Promise((resolve,reject)=> {
var t:any = setTimeout(function():void {
resolve();
}, (n));
if(ct) {
ct.registerForCancel(()=> {
clearTimeout(t);
reject("cancelled");
});
}
});
};
/**
*
*
* @export
* @interface AtomDisposable
*/
// tslint:disable-next-line:interface-name
export interface AtomDisposable {
dispose():void ;
}
export type AtomAction = (msg: string, data: any) => void;
class AtomHandler {
constructor(message: string) {
this.message = message;
this.list = new Array<AtomAction>();
}
public message: string;
public list: Array<AtomAction>;
}
export class AtomMessageAction {
public message: string;
public action: AtomAction;
constructor(msg: string, a: AtomAction) {
this.message = msg;
this.action = a;
}
}
/**
* Device (usually browser), instance of which supports
* singleton instance to provide broadcast/messaging
*
* @export
* @class AtomDevice
*/
export class AtomDevice {
/**
*
*
* @static
* @type {AtomDevice}
* @memberof AtomDevice
*/
static instance: AtomDevice = new AtomDevice();
constructor() {
this.bag = {};
}
/**
* This method will run any asynchronous method
* and it will display an error if it will fail
* asynchronously
*
* @template T
* @param {() => Promise<T>} tf
* @memberof AtomDevice
*/
public runAsync<T>(tf: () => Promise<T>):void {
var task:any = tf();
task.catch(error=> {
console.error(error);
Atom.showError(error);
});
// tslint:disable-next-line
task.then(():void => {});
}
private bag: any;
/**
* Broadcast given data to channel, only within the current window.
*
* @param {string} channel
* @param {*} data
* @returns
* @memberof AtomDevice
*/
public broadcast(channel: string, data: any):void {
var ary:AtomHandler = this.bag[channel] as AtomHandler;
if (!ary) {
return;
}
for (let entry of ary.list) {
entry.call(this, channel, data);
}
}
/**
* Subscribe for given channel with action that will be
* executed when anyone will broadcast (this only works within the
* current browser window)
*
* This method returns a disposable, when you call `.dispose()` it will
* unsubscribe for current subscription
*
* @param {string} channel
* @param {AtomAction} action
* @returns {AtomDisposable} Disposable that supports removal of subscription
* @memberof AtomDevice
*/
public subscribe(channel: string, action: AtomAction): AtomDisposable {
var ary:AtomHandler = this.bag[channel] as AtomHandler;
if (!ary) {
ary = new AtomHandler(channel);
this.bag[channel] = ary;
}
ary.list.push(action);
return new DisposableAction(()=> {
ary.list = ary.list.filter(a=> a !== action);
if(!ary.list.length) {
this.bag[channel] = null;
}
});
}
}
}