@web-atoms/core-docs
Version:
456 lines • 17.3 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "../App", "../Atom", "../core/AtomBinder", "../core/AtomDisposableList", "../core/AtomWatcher", "../core/BindableProperty", "../di/Inject", "./baseTypes"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Validate = exports.CachedWatch = exports.Watch = exports.BindableBroadcast = exports.BindableReceive = exports.Receive = exports.AtomViewModel = exports.waitForReady = void 0;
const App_1 = require("../App");
const Atom_1 = require("../Atom");
const AtomBinder_1 = require("../core/AtomBinder");
const AtomDisposableList_1 = require("../core/AtomDisposableList");
const AtomWatcher_1 = require("../core/AtomWatcher");
const BindableProperty_1 = require("../core/BindableProperty");
const Inject_1 = require("../di/Inject");
const baseTypes_1 = require("./baseTypes");
function runDecoratorInits() {
const v = this.constructor.prototype;
if (!v) {
return;
}
const ris = v._$_inits;
if (ris) {
for (const ri of ris) {
ri.call(this, this);
}
}
}
function privateInit() {
return __awaiter(this, void 0, void 0, function* () {
try {
yield Atom_1.Atom.postAsync(this.app, () => __awaiter(this, void 0, void 0, function* () {
runDecoratorInits.apply(this);
// this.registerWatchers();
}));
yield Atom_1.Atom.postAsync(this.app, () => __awaiter(this, void 0, void 0, function* () {
yield this.init();
this.onReady();
}));
if (this.postInit) {
for (const i of this.postInit) {
i();
}
this.postInit = null;
}
}
finally {
const pi = this.pendingInits;
this.pendingInits = null;
for (const iterator of pi) {
iterator();
}
}
});
}
/**
* Useful only for Unit testing, this function will await till initialization is
* complete and all pending functions are executed
*/
function waitForReady(vm) {
return __awaiter(this, void 0, void 0, function* () {
while (vm.pendingInits) {
yield Atom_1.Atom.delay(100);
}
});
}
exports.waitForReady = waitForReady;
/**
* ViewModel class supports initialization and supports {@link IDisposable} dispose pattern.
* @export
* @class AtomViewModel
*/
let AtomViewModel = class AtomViewModel {
constructor(app) {
this.app = app;
this.disposables = null;
this.validations = [];
this.pendingInits = [];
this.mShouldValidate = false;
this.app.runAsync(() => privateInit.apply(this));
}
/**
* If it returns true, it means all pending initializations have finished
*/
get isReady() {
return this.pendingInits === null;
}
get errors() {
const e = [];
if (!this.mShouldValidate) {
return e;
}
for (const v of this.validations) {
if (!v.initialized) {
return e;
}
const error = this[v.name];
if (error) {
e.push({ name: v.name, error });
}
}
return e;
}
/**
* Returns parent AtomViewModel if it was initialized with one. This property is also
* useful when you open an popup or window. Whenever a popup/window is opened, ViewModel
* associated with the UI element that opened this popup/window becomes parent of ViewModel
* of popup/window.
*/
get parent() {
return this.mParent;
}
set parent(v) {
if (this.mParent && this.mParent.mChildren) {
this.mParent.mChildren.remove(this);
}
this.mParent = v;
if (v) {
const c = v.mChildren || (v.mChildren = []);
c.add(this);
this.registerDisposable({
dispose: () => {
c.remove(this);
}
});
}
}
/**
* Returns true if all validations didn't return any error. All validations
* are decorated with @{@link Validate} decorator.
*/
get isValid() {
let valid = true;
const validateWasFalse = this.mShouldValidate === false;
this.mShouldValidate = true;
for (const v of this.validations) {
if (!v.initialized) {
v.watcher.init(true);
v.initialized = true;
}
if (this[v.name]) {
if (validateWasFalse) {
AtomBinder_1.AtomBinder.refreshValue(this, v.name);
}
valid = false;
}
}
if (this.mChildren) {
for (const child of this.mChildren) {
if (!child.isValid) {
valid = false;
}
}
}
AtomBinder_1.AtomBinder.refreshValue(this, "errors");
return valid;
}
/**
* Resets validations and all errors are removed.
* @param resetChildren reset child view models as well. Default is true.
*/
resetValidations(resetChildren = true) {
this.mShouldValidate = false;
for (const v of this.validations) {
this.refresh(v.name);
}
if (resetChildren && this.mChildren) {
for (const iterator of this.mChildren) {
iterator.resetValidations(resetChildren);
}
}
}
/**
* Runs function after initialization is complete.
* @param f function to execute
*/
runAfterInit(f) {
if (this.pendingInits) {
this.pendingInits.push(f);
return;
}
f();
}
// /**
// * Binds source property to target property with optional two ways
// * @param target target whose property will be set
// * @param propertyName name of target property
// * @param source source to read property from
// * @param path property path of source
// * @param twoWays optional, two ways {@link IValueConverter}
// */
// public bind(
// target: any,
// propertyName: string,
// source: any,
// path: string[][],
// twoWays?: IValueConverter | ((v: any) => any) ): IDisposable {
// const pb = new PropertyBinding(
// target,
// null,
// propertyName,
// path,
// (twoWays && typeof twoWays !== "function") ? true : false , twoWays, source);
// return this.registerDisposable(pb);
// }
/**
* Refreshes bindings associated with given property name
* @param name name of property
*/
refresh(name) {
AtomBinder_1.AtomBinder.refreshValue(this, name);
}
/**
* Put your asynchronous initialization here
*
* @returns {Promise<any>}
* @memberof AtomViewModel
*/
// tslint:disable-next-line:no-empty
init() {
return __awaiter(this, void 0, void 0, function* () {
});
}
/**
* dispose method will be called when attached view will be disposed or
* when a new view model will be assigned to view, old view model will be disposed.
*
* @memberof AtomViewModel
*/
dispose() {
if (this.disposables) {
this.disposables.dispose();
}
}
// /**
// * Internal method, do not use, instead use errors.hasErrors()
// *
// * @memberof AtomViewModel
// */
// public runValidation(): void {
// for (const v of this.validations) {
// v.watcher.evaluate(true);
// }
// }
/**
* Register a disposable to be disposed when view model will be disposed.
*
* @protected
* @param {IDisposable} d
* @memberof AtomViewModel
*/
registerDisposable(d) {
this.disposables = this.disposables || new AtomDisposableList_1.AtomDisposableList();
return this.disposables.add(d);
}
// tslint:disable-next-line:no-empty
onReady() { }
/**
* Execute given expression whenever any bindable expression changes
* in the expression.
*
* For correct generic type resolution, target must always be `this`.
*
* this.setupWatch(() => {
* if(!this.data.fullName){
* this.data.fullName = `${this.data.firstName} ${this.data.lastName}`;
* }
* });
*
* @protected
* @template T
* @param {() => any} ft
* @returns {IDisposable}
* @memberof AtomViewModel
*/
setupWatch(ft, proxy, forValidation, name) {
const d = new AtomWatcher_1.AtomWatcher(this, ft, proxy, this);
if (forValidation) {
this.validations = this.validations || [];
this.validations.push({ name, watcher: d, initialized: false });
}
else {
d.init();
}
return this.registerDisposable(d);
}
// tslint:disable-next-line:no-empty
onPropertyChanged(name) { }
};
AtomViewModel = __decorate([
__param(0, Inject_1.Inject),
__metadata("design:paramtypes", [App_1.App])
], AtomViewModel);
exports.AtomViewModel = AtomViewModel;
/**
* Receive messages for given channel
* @param {(string | RegExp)} channel
* @returns {Function}
*/
function Receive(...channel) {
return (target, key) => {
baseTypes_1.registerInit(target, (vm) => {
// tslint:disable-next-line:ban-types
const fx = vm[key];
const a = (ch, d) => {
const p = fx.call(vm, ch, d);
if (p && p.then && p.catch) {
p.catch((e) => {
// tslint:disable-next-line: no-console
console.warn(e);
});
}
};
const ivm = vm;
for (const c of channel) {
ivm.registerDisposable(ivm.app.subscribe(c, a));
}
});
};
}
exports.Receive = Receive;
function BindableReceive(...channel) {
return (target, key) => {
const bp = BindableProperty_1.BindableProperty(target, key);
baseTypes_1.registerInit(target, (vm) => {
const fx = (cx, m) => {
vm[key] = m;
};
const ivm = vm;
for (const c of channel) {
ivm.registerDisposable(ivm.app.subscribe(c, fx));
}
});
return bp;
};
}
exports.BindableReceive = BindableReceive;
function BindableBroadcast(...channel) {
return (target, key) => {
const bp = BindableProperty_1.BindableProperty(target, key);
baseTypes_1.registerInit(target, (vm) => {
const fx = (t) => {
const v = vm[key];
for (const c of channel) {
vm.app.broadcast(c, v);
}
};
const d = new AtomWatcher_1.AtomWatcher(vm, [key.split(".")], fx);
d.init();
vm.registerDisposable(d);
});
return bp;
};
}
exports.BindableBroadcast = BindableBroadcast;
function Watch(target, key, descriptor) {
baseTypes_1.registerInit(target, (vm) => {
const ivm = vm;
if (descriptor && descriptor.get) {
ivm.setupWatch(descriptor.get, () => {
vm.refresh(key.toString());
});
return;
}
ivm.setupWatch(vm[key]);
});
}
exports.Watch = Watch;
/**
* Cached watch must be used with async getters to avoid reloading of
* resources unless the properties referenced are changed
* @param target ViewModel
* @param key name of property
* @param descriptor descriptor of property
*/
function CachedWatch(target, key, descriptor) {
const getMethod = descriptor.get;
descriptor.get = (() => null);
baseTypes_1.registerInit(target, (vm) => {
const ivm = vm;
const fieldName = `_${key}`;
Object.defineProperty(ivm, key, {
enumerable: true,
configurable: true,
get() {
const c = ivm[fieldName] || (ivm[fieldName] = {
value: getMethod.apply(ivm)
});
return c.value;
}
});
ivm.setupWatch(getMethod, () => {
ivm[fieldName] = null;
AtomBinder_1.AtomBinder.refreshValue(ivm, key);
});
});
}
exports.CachedWatch = CachedWatch;
function Validate(target, key, descriptor) {
// tslint:disable-next-line:ban-types
const getMethod = descriptor.get;
// // trick is to change property descriptor...
// delete target[key];
descriptor.get = () => null;
// // replace it with dummy descriptor...
// Object.defineProperty(target, key, descriptor);
baseTypes_1.registerInit(target, (vm) => {
const initialized = { i: false };
const ivm = vm;
Object.defineProperty(ivm, key, {
enumerable: true,
configurable: true,
get() {
if (vm.mShouldValidate && initialized.i) {
return getMethod.apply(this);
}
return null;
}
});
ivm.setupWatch(getMethod, () => {
// descriptor.get = getMethod;
// Object.defineProperty(target, key, descriptor);
initialized.i = true;
vm.refresh(key.toString());
}, true, key.toString());
return;
});
}
exports.Validate = Validate;
});
//# sourceMappingURL=AtomViewModel.js.map