PromucFlow_constructor/app/client/src/utils/DynamicBindingUtils.ts

144 lines
4.3 KiB
TypeScript
Raw Normal View History

2019-11-06 06:35:15 +00:00
import _ from "lodash";
2019-11-25 05:07:27 +00:00
import { WidgetProps } from "widgets/BaseWidget";
2019-11-28 03:56:44 +00:00
import { DATA_BIND_JS_REGEX } from "constants/BindingsConstants";
2019-11-19 12:44:58 +00:00
import ValidationFactory from "./ValidationFactory";
2019-11-28 03:56:44 +00:00
import JSExecutionManagerSingleton from "jsExecution/JSExecutionManagerSingleton";
2019-11-06 06:35:15 +00:00
2019-11-28 03:56:44 +00:00
export type NameBindingsWithData = Record<string, object>;
2019-11-14 09:28:51 +00:00
export const isDynamicValue = (value: string): boolean =>
2019-11-28 03:56:44 +00:00
DATA_BIND_JS_REGEX.test(value);
//{{}}{{}}}
function parseDynamicString(dynamicString: string): string[] {
let parsedDynamicValues = [];
const indexOfDoubleParanStart = dynamicString.indexOf("{{");
if (indexOfDoubleParanStart === -1) {
return [dynamicString];
}
//{{}}{{}}}
const firstString = dynamicString.substring(0, indexOfDoubleParanStart);
firstString && parsedDynamicValues.push(firstString);
let rest = dynamicString.substring(
indexOfDoubleParanStart,
dynamicString.length,
);
//{{}}{{}}}
let sum = 0;
for (let i = 0; i <= rest.length - 1; i++) {
const char = rest[i];
const prevChar = rest[i - 1];
if (char === "{") {
sum++;
} else if (char === "}") {
sum--;
if (prevChar === "}" && sum === 0) {
parsedDynamicValues.push(rest.substring(0, i + 1));
rest = rest.substring(i + 1, rest.length);
if (rest) {
parsedDynamicValues = parsedDynamicValues.concat(
parseDynamicString(rest),
);
break;
}
}
}
}
if (sum !== 0 && dynamicString !== "") {
return [dynamicString];
}
return parsedDynamicValues;
}
2019-11-14 09:28:51 +00:00
export const getDynamicBindings = (
dynamicString: string,
): { bindings: string[]; paths: string[] } => {
// Get the {{binding}} bound values
2019-11-28 03:56:44 +00:00
const bindings = parseDynamicString(dynamicString);
2019-11-14 09:28:51 +00:00
// Get the "binding" path values
2019-11-28 03:56:44 +00:00
const paths = bindings.map(binding => {
const length = binding.length;
const matches = binding.match(DATA_BIND_JS_REGEX);
if (matches) {
return binding.substring(2, length - 2);
}
2019-11-14 09:28:51 +00:00
return "";
});
return { bindings, paths };
};
2019-11-06 06:35:15 +00:00
// Paths are expected to have "{name}.{path}" signature
2019-11-14 09:28:51 +00:00
export const extractDynamicBoundValue = (
2019-11-28 03:56:44 +00:00
data: NameBindingsWithData,
2019-11-06 06:35:15 +00:00
path: string,
2019-11-14 09:28:51 +00:00
): any => {
2019-11-28 03:56:44 +00:00
return JSExecutionManagerSingleton.evaluateSync(path, data);
2019-11-14 09:28:51 +00:00
};
// For creating a final value where bindings could be in a template format
export const createDynamicValueString = (
binding: string,
subBindings: string[],
subValues: string[],
): string => {
// Replace the string with the data tree values
let finalValue = binding;
subBindings.forEach((b, i) => {
let value = subValues[i];
if (Array.isArray(value) || _.isObject(value)) {
value = JSON.stringify(value);
}
finalValue = finalValue.replace(b, value);
});
return finalValue;
};
export const getDynamicValue = (
dynamicBinding: string,
2019-11-28 03:56:44 +00:00
data: NameBindingsWithData,
2019-11-14 09:28:51 +00:00
): any => {
// Get the {{binding}} bound values
const { bindings, paths } = getDynamicBindings(dynamicBinding);
if (bindings.length) {
// Get the Data Tree value of those "binding "paths
2019-11-28 03:56:44 +00:00
const values = paths.map((p, i) => {
return p ? extractDynamicBoundValue(data, p) : bindings[i];
});
2019-11-14 09:28:51 +00:00
// if it is just one binding, no need to create template string
if (bindings.length === 1) return values[0];
// else return a string template with bindings
return createDynamicValueString(dynamicBinding, bindings, values);
}
return undefined;
2019-11-06 06:35:15 +00:00
};
2019-11-19 12:44:58 +00:00
export const enhanceWithDynamicValuesAndValidations = (
widget: WidgetProps,
2019-11-28 03:56:44 +00:00
nameBindingsWithData: NameBindingsWithData,
2019-11-22 13:12:39 +00:00
replaceWithParsed: boolean,
2019-11-19 12:44:58 +00:00
): WidgetProps => {
if (!widget) return widget;
const properties = { ...widget };
const invalidProps: Record<string, boolean> = {};
2019-11-28 03:56:44 +00:00
2019-11-19 12:44:58 +00:00
Object.keys(widget).forEach((property: string) => {
let value = widget[property];
2019-11-06 06:35:15 +00:00
// Check for dynamic bindings
2019-11-22 13:12:39 +00:00
if (widget.dynamicBindings && property in widget.dynamicBindings) {
2019-11-28 03:56:44 +00:00
value = getDynamicValue(value, nameBindingsWithData);
2019-11-06 06:35:15 +00:00
}
2019-11-22 13:12:39 +00:00
// Pass it through validation and parse
const { isValid, parsed } = ValidationFactory.validateWidgetProperty(
2019-11-19 12:44:58 +00:00
widget.type,
property,
value,
);
2019-11-22 13:12:39 +00:00
// Store all invalid props
if (!isValid) invalidProps[property] = true;
// Replace if flag is turned on
if (replaceWithParsed) properties[property] = parsed;
2019-11-19 12:44:58 +00:00
});
return { ...properties, invalidProps };
2019-11-06 06:35:15 +00:00
};