mframejs
Version:
simple framework
202 lines (159 loc) • 7.2 kB
text/typescript
import { RepeatAttribute } from './repeatAttribute';
import { BindingEngine } from '../binding/exported';
import { IListener } from '../interface/exported';
import { ArrayObserverHandler } from '../binding/array/arrayObserverHandler';
import { PropertyObserverHandler } from '../binding/property/propertyObserverHandler';
/***************************************************************
* Helpers for repeat for attribute when array changes
* ArrayMethodCallHandler & ArrayPropertyChange & PropertyChangeSimple
***************************************************************/
/**
* Handles events/methods on array, like push/splice etc
*
*/
export class ArrayMethodCallHandler implements IListener {
public name: string;
public caller: ArrayObserverHandler | PropertyObserverHandler;
constructor(
private repeat: RepeatAttribute
) {
this.name = 'repeat' + this.repeat.value;
}
public call(events: any) {
let sort = true;
events.forEach((event: any) => {
switch (event.event) {
case 'push':
let i = 0;
while (i < event.args.length) {
this.repeat.push(event.args[i]);
i++;
}
this.repeat.updateInternals();
break;
case 'reverse':
case 'sort':
if (sort) {
sort = false;
const array = this.repeat.$array; // BindingEngine.evaluateExpression(this.repeat.arrayExpression, this.repeat.$bindingContext);
if (array) {
array.forEach((ctx: any, i: number) => {
this.repeat.templateArray[i].ctx.$context[this.repeat.rowInstanceName] = ctx;
});
}
}
break;
case 'shift':
this.repeat.shift();
break;
case 'pop':
this.repeat.pop();
break;
case 'splice':
this.repeat.splice(event.args);
break;
default:
}
});
}
}
/**
* Handles changes to array when its replaced
*
*/
export class ArrayPropertyChange implements IListener {
public name: string;
public caller: ArrayObserverHandler | PropertyObserverHandler;
constructor(
private repeat: RepeatAttribute,
public expression?: boolean
) {
this.name = 'repeat' + this.repeat.value;
}
public call() {
if (this.repeat.isAttached) {
const array = BindingEngine.evaluateExpression(this.repeat.arrayExpression, this.repeat.$bindingContext);
if (Array.isArray(array) && this.repeat.templateArray.length !== array.length) {
this.repeat.$array = array;
if (this.repeat.templateArray.length !== 0 && array.length !== 0) {
if (this.repeat.templateArray.length > array.length) {
this.repeat.loopBinded(true, this.repeat.templateArray.length - array.length, 0);
} else {
this.repeat.loopBinded(true, 0, array.length - this.repeat.templateArray.length);
}
BindingEngine.unSubscribeClassArray(this.repeat.$bindingContext, this.repeat.arrayMethodCallHandler);
this.repeat.subscribeArray();
} else {
this.repeat.clearTemplateArray();
let array = BindingEngine.evaluateExpression(this.repeat.arrayExpression, this.repeat.$bindingContext);
if (Array.isArray(array)) {
array.forEach((ctx: any) => {
this.repeat.push(ctx);
});
BindingEngine.unSubscribeClassArray(this.repeat.$bindingContext, this.repeat.arrayMethodCallHandler);
this.repeat.subscribeArray();
array = null;
}
}
} else {
if (Array.isArray(array)) {
// array is same size, we just need to replace data
this.repeat.$array = array;
this.repeat.loopBinded();
BindingEngine.unSubscribeClassArray(this.repeat.$bindingContext, this.repeat.arrayMethodCallHandler);
if (array) {
this.repeat.subscribeArray();
}
} else {
// array is null/undefined, we need to clear array
this.repeat.$array = Array.isArray(array) ? array : [];
this.repeat.clearTemplateArray();
BindingEngine.unSubscribeClassArray(this.repeat.$bindingContext, this.repeat.arrayMethodCallHandler);
if (array) {
this.repeat.subscribeArray();
}
}
}
}
}
}
/**
* Handles changes to property(not array when this is used) when its replaced (simple array, not object)
*
*/
export class PropertyChangeSimple implements IListener {
public name: string;
public caller: ArrayObserverHandler | PropertyObserverHandler;
constructor(private repeat: RepeatAttribute) {
this.name = 'repeat' + this.repeat.value;
}
public call(newValue: any) {
if (this.repeat.isAttached) {
if (this.repeat.arrayType === 'string') {
// TODO: this is slow if sub router and many
// I think this will ever be used..much
// need to think about implementing this better like numbers
const stringLength = typeof newValue === 'string' ? newValue.length : 0;
if (this.repeat.templateArray.length !== stringLength) {
this.repeat.clearTemplateArray();
for (let i = 0; i < stringLength; i++) {
this.repeat.push(newValue[i]);
}
}
}
if (this.repeat.arrayType === 'number') {
if (typeof newValue !== 'number') {
console.warn('repeat not number:', newValue);
newValue = 0;
}
if (this.repeat.templateArray.length !== newValue) {
if (this.repeat.templateArray.length < newValue) {
this.repeat.loopArrayNumber(true, 0, newValue - this.repeat.templateArray.length);
} else {
this.repeat.loopArrayNumber(true, this.repeat.templateArray.length - newValue, 0);
}
}
}
}
}
}