@pnp/spfx-controls-react
Version:
Reusable React controls for SharePoint Framework solutions
796 lines • 110 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DynamicForm = exports.DynamicFormBase = void 0;
var tslib_1 = require("tslib");
/* eslint-disable @microsoft/spfx/no-async-await */
var React = tslib_1.__importStar(require("react"));
var strings = tslib_1.__importStar(require("ControlStrings"));
// Controls
var Button_1 = require("@fluentui/react/lib/Button");
var Dialog_1 = require("@fluentui/react/lib/Dialog");
var MessageBar_1 = require("@fluentui/react/lib/MessageBar");
var ProgressIndicator_1 = require("@fluentui/react/lib/ProgressIndicator");
var Stack_1 = require("@fluentui/react/lib/Stack");
var dynamicField_1 = require("./dynamicField");
var filePicker_1 = require("../filePicker");
var sp_core_library_1 = require("@microsoft/sp-core-library");
// pnp/sp, helpers / utils
var sp_1 = require("@pnp/sp");
require("@pnp/sp/lists");
require("@pnp/sp/content-types");
require("@pnp/sp/folders");
require("@pnp/sp/items");
var all_1 = require("@pnp/sp/presets/all");
var lodash_1 = require("lodash");
var SPService_1 = tslib_1.__importDefault(require("../../services/SPService"));
var FormulaEvaluation_1 = require("../../common/utilities/FormulaEvaluation");
var CustomFormatting_1 = tslib_1.__importDefault(require("../../common/utilities/CustomFormatting"));
var SPTaxonomyService_1 = require("../../services/SPTaxonomyService");
var DynamicForm_styles_1 = require("./DynamicForm.styles");
var ThemeUtility_1 = require("../../common/utilities/ThemeUtility");
var react_1 = require("@fluentui/react");
var Icon_1 = require("@fluentui/react/lib/Icon");
var stackTokens = { childrenGap: 20 };
var getstyles = (0, react_1.classNamesFunction)();
var getFieldstyles = (0, react_1.classNamesFunction)();
var theme = (0, ThemeUtility_1.getFluentUIThemeOrDefault)();
var timeout = function (ms) {
return new Promise(function (resolve) { return setTimeout(resolve, ms); });
};
/**
* DynamicForm Class Control
*/
var DynamicFormBase = /** @class */ (function (_super) {
tslib_1.__extends(DynamicFormBase, _super);
function DynamicFormBase(props) {
var _this = _super.call(this, props) || this;
_this.webURL = _this.props.webAbsoluteUrl
? _this.props.webAbsoluteUrl
: _this.props.context.pageContext.web.absoluteUrl;
_this.sortFields = function (fields, customSort) {
var fMap = new Map();
for (var _i = 0, fields_1 = fields; _i < fields_1.length; _i++) {
var field = fields_1[_i];
fMap.set(field.columnInternalName.toLowerCase(), field);
}
var sortedFields = customSort
.map(function (sortColumn) { return sortColumn.toLowerCase(); })
.filter(function (normalizedSortColumn) { return fMap.has(normalizedSortColumn); })
.map(function (normalizedSortColumn) { return fMap.get(normalizedSortColumn); })
.filter(function (field) { return field !== undefined; });
var remainingFields = fields.filter(function (field) { return !sortedFields.includes(field); });
var uniqueRemainingFields = Array.from(new Set(remainingFields));
return tslib_1.__spreadArray(tslib_1.__spreadArray([], sortedFields, true), uniqueRemainingFields, true);
};
_this.renderField = function (field) {
var fieldOverrides = _this.props.fieldOverrides;
var _a = _this.state, hiddenByFormula = _a.hiddenByFormula, isSaving = _a.isSaving, validationErrors = _a.validationErrors;
var styles = getFieldstyles(_this._classNames.subComponentStyles.fieldStyles(), { theme: theme });
// If the field is hidden by a formula or field doesn't exist (usually occurs in custom formatting section layout when field display name changed), don't render it
if (!field || hiddenByFormula.find(function (h) { return h === field.columnInternalName; })) {
return null;
}
// If validation error, show error message
var validationErrorMessage = "";
if (validationErrors[field.columnInternalName]) {
validationErrorMessage = validationErrors[field.columnInternalName];
}
// If field override is provided, use it instead of the DynamicField component
if (fieldOverrides &&
Object.prototype.hasOwnProperty.call(fieldOverrides, field.columnInternalName)) {
return fieldOverrides[field.columnInternalName](tslib_1.__assign(tslib_1.__assign({}, field), { disabled: field.disabled || isSaving }));
}
// Default render
return (React.createElement(dynamicField_1.DynamicField, tslib_1.__assign({ key: field.columnInternalName, styles: styles }, field, { disabled: field.disabled || isSaving, validationErrorMessage: validationErrorMessage, itemsQueryCountLimit: _this.props.itemsQueryCountLimit })));
};
/** Triggered when the user submits the form. */
_this.onSubmitClick = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var _a, listId, listItemId, onSubmitted, onBeforeSubmit, onSubmitError, enableFileSelection, validationErrorDialogProps, returnListItemInstanceOnSubmit, useModernTaxonomyPicker, contentTypeId, fileSelectRendered, shouldBeReturnBack_1, fields, validationDisabled, validationErrors, objects, _loop_1, this_1, i, len, isCancelled, apiError, newETag, iur, error_1, contentTypeIdField, iar, error_2, idField, contentTypeIdField, library, folderFileName, folder, _b, newFolder, fields_2, folderId, iur, error_3, updatedExtendedInfo_1, error_4;
var _c, _d, _e, _f, _g, _h, _j, _k, _l;
return tslib_1.__generator(this, function (_m) {
switch (_m.label) {
case 0:
_a = this.props, listId = _a.listId, listItemId = _a.listItemId, onSubmitted = _a.onSubmitted, onBeforeSubmit = _a.onBeforeSubmit, onSubmitError = _a.onSubmitError, enableFileSelection = _a.enableFileSelection, validationErrorDialogProps = _a.validationErrorDialogProps, returnListItemInstanceOnSubmit = _a.returnListItemInstanceOnSubmit, useModernTaxonomyPicker = _a.useModernTaxonomyPicker;
contentTypeId = this.props.contentTypeId;
if (this.state.contentTypeId !== undefined)
contentTypeId = this.state.contentTypeId;
fileSelectRendered = !listItemId && contentTypeId.startsWith("0x0101") && enableFileSelection === true;
_m.label = 1;
case 1:
_m.trys.push([1, 35, , 36]);
shouldBeReturnBack_1 = false;
fields = (this.state.fieldCollection || []).slice();
fields.forEach(function (field) {
// When a field is required and has no value
if (field.required) {
if ((field.newValue === undefined || field.newValue.length === 0) && (field.value === undefined || field.value.length === 0)) {
if (field.defaultValue === null ||
field.defaultValue === "" ||
field.defaultValue.length === 0 ||
field.defaultValue === undefined) {
if (field.fieldType === "DateTime")
field.defaultValue = null;
else
field.defaultValue = "";
shouldBeReturnBack_1 = true;
}
}
else if (field.newValue === "") {
field.defaultValue = "";
shouldBeReturnBack_1 = true;
}
else if (Array.isArray(field.newValue) && field.newValue.length === 0) {
field.defaultValue = null;
shouldBeReturnBack_1 = true;
}
}
// Check min and max values for number fields
if (field.fieldType === "Number" && field.newValue !== undefined && field.newValue.trim() !== "") {
if ((field.newValue < field.minimumValue) || (field.newValue > field.maximumValue)) {
shouldBeReturnBack_1 = true;
}
}
});
validationDisabled = this.props.useFieldValidation === false;
validationErrors = {};
if (!!validationDisabled) return [3 /*break*/, 3];
return [4 /*yield*/, this.evaluateFormulas(this.state.validationFormulas, true, true, this.state.hiddenByFormula)];
case 2:
validationErrors = (_m.sent());
if (Object.keys(validationErrors).length > 0) {
shouldBeReturnBack_1 = true;
}
_m.label = 3;
case 3:
// If validation failed, return without saving
if (shouldBeReturnBack_1) {
this.setState({
fieldCollection: fields,
isValidationErrorDialogOpen: (validationErrorDialogProps === null || validationErrorDialogProps === void 0 ? void 0 : validationErrorDialogProps.showDialogOnValidationError) === true,
});
return [2 /*return*/];
}
if (fileSelectRendered === true && this.state.selectedFile === undefined && this.props.listItemId === undefined) {
this.setState({
missingSelectedFile: true,
isValidationErrorDialogOpen: (validationErrorDialogProps === null || validationErrorDialogProps === void 0 ? void 0 : validationErrorDialogProps.showDialogOnValidationError) === true,
validationErrors: validationErrors
});
return [2 /*return*/];
}
this.setState({
isSaving: true,
});
objects = {};
_loop_1 = function (i, len) {
var field, fieldType, additionalData, columnInternalName, hiddenFieldName, fieldcolumnInternalName, value_1, uploadedImage;
return tslib_1.__generator(this, function (_o) {
switch (_o.label) {
case 0:
field = fields[i];
fieldType = field.fieldType, additionalData = field.additionalData, columnInternalName = field.columnInternalName, hiddenFieldName = field.hiddenFieldName;
fieldcolumnInternalName = columnInternalName;
if (fieldcolumnInternalName.startsWith('_x') || fieldcolumnInternalName.startsWith('_')) {
fieldcolumnInternalName = "OData_".concat(fieldcolumnInternalName);
}
if (!(field.newValue !== undefined)) return [3 /*break*/, 3];
value_1 = field.newValue;
if (["Lookup", "LookupMulti", "User", "UserMulti", "TaxonomyFieldTypeMulti"].indexOf(fieldType) < 0) {
objects[columnInternalName] = value_1;
}
// Choice fields
if (fieldType === "Choice") {
objects[fieldcolumnInternalName] = field.newValue.key;
}
if (fieldType === "MultiChoice") {
objects[fieldcolumnInternalName] = { results: field.newValue };
}
// Lookup fields
if (fieldType === "Lookup") {
if (value_1 && value_1.length > 0) {
objects["".concat(fieldcolumnInternalName, "Id")] = value_1[0].key;
}
else {
objects["".concat(fieldcolumnInternalName, "Id")] = null;
}
}
if (fieldType === "LookupMulti") {
value_1 = [];
field.newValue.forEach(function (element) {
value_1.push(element.key);
});
objects["".concat(fieldcolumnInternalName, "Id")] = {
results: value_1.length === 0 ? [] : value_1,
};
}
// User fields
if (fieldType === "User") {
objects["".concat(fieldcolumnInternalName, "Id")] = field.newValue.length === 0 ? null : field.newValue;
}
if (fieldType === "UserMulti") {
objects["".concat(fieldcolumnInternalName, "Id")] = {
results: field.newValue.length === 0 ? null : field.newValue,
};
}
// Taxonomy / Managed Metadata fields
if (useModernTaxonomyPicker) {
//Use ITermInfo[] for modern taxonomy picker
if (fieldType === "TaxonomyFieldType") {
objects[fieldcolumnInternalName] = {
__metadata: { type: "SP.Taxonomy.TaxonomyFieldValue" },
Label: (_e = (_d = (_c = value_1[0]) === null || _c === void 0 ? void 0 : _c.labels[0]) === null || _d === void 0 ? void 0 : _d.name) !== null && _e !== void 0 ? _e : "",
TermGuid: (_g = (_f = value_1[0]) === null || _f === void 0 ? void 0 : _f.id) !== null && _g !== void 0 ? _g : "11111111-1111-1111-1111-111111111111",
WssId: "-1",
};
}
if (fieldType === "TaxonomyFieldTypeMulti") {
objects[hiddenFieldName] = field.newValue
.map(function (term) { var _a; return "-1#;".concat(((_a = term.labels[0]) === null || _a === void 0 ? void 0 : _a.name) || "", "|").concat(term.id, ";"); })
.join("#");
}
}
else {
//Use IPickerTerms
if (fieldType === "TaxonomyFieldType") {
objects[fieldcolumnInternalName] = {
__metadata: { type: "SP.Taxonomy.TaxonomyFieldValue" },
Label: (_j = (_h = value_1[0]) === null || _h === void 0 ? void 0 : _h.name) !== null && _j !== void 0 ? _j : "",
TermGuid: (_l = (_k = value_1[0]) === null || _k === void 0 ? void 0 : _k.key) !== null && _l !== void 0 ? _l : "11111111-1111-1111-1111-111111111111",
WssId: "-1",
};
}
if (fieldType === "TaxonomyFieldTypeMulti") {
objects[hiddenFieldName] = field.newValue
.map(function (term) { return "-1#;".concat(term.name, "|").concat(term.key, ";"); })
.join("#");
}
}
// Other fields
if (fieldType === "Location") {
objects[fieldcolumnInternalName] = JSON.stringify(field.newValue);
}
if (!(fieldType === "Thumbnail")) return [3 /*break*/, 3];
if (!additionalData) return [3 /*break*/, 2];
return [4 /*yield*/, this_1.uploadImage(additionalData)];
case 1:
uploadedImage = _o.sent();
objects[fieldcolumnInternalName] = JSON.stringify({
type: "thumbnail",
fileName: uploadedImage.Name,
serverRelativeUrl: uploadedImage.ServerRelativeUrl,
id: uploadedImage.UniqueId,
});
return [3 /*break*/, 3];
case 2:
objects[fieldcolumnInternalName] = null;
_o.label = 3;
case 3: return [2 /*return*/];
}
});
};
this_1 = this;
i = 0, len = fields.length;
_m.label = 4;
case 4:
if (!(i < len)) return [3 /*break*/, 7];
return [5 /*yield**/, _loop_1(i, len)];
case 5:
_m.sent();
_m.label = 6;
case 6:
i++;
return [3 /*break*/, 4];
case 7:
if (!onBeforeSubmit) return [3 /*break*/, 9];
return [4 /*yield*/, onBeforeSubmit(objects)];
case 8:
isCancelled = _m.sent();
if (isCancelled) {
this.setState({
isSaving: false,
});
return [2 /*return*/];
}
_m.label = 9;
case 9:
apiError = void 0;
newETag = undefined;
if (!listItemId) return [3 /*break*/, 14];
_m.label = 10;
case 10:
_m.trys.push([10, 12, , 13]);
return [4 /*yield*/, sp_1.sp.web.lists
.getById(listId)
.items.getById(listItemId)
.update(objects, this.state.etag)];
case 11:
iur = _m.sent();
newETag = iur.data["odata.etag"];
if (onSubmitted) {
onSubmitted(iur.data, returnListItemInstanceOnSubmit !== false
? iur.item
: undefined);
}
return [3 /*break*/, 13];
case 12:
error_1 = _m.sent();
apiError = error_1.message;
if (onSubmitError) {
onSubmitError(objects, error_1);
}
console.log("Error", error_1);
return [3 /*break*/, 13];
case 13: return [3 /*break*/, 32];
case 14:
if (!(contentTypeId === undefined ||
contentTypeId === "" ||
(!contentTypeId.startsWith("0x0120") &&
contentTypeId.startsWith("0x01")))) return [3 /*break*/, 20];
if (!(fileSelectRendered === true)) return [3 /*break*/, 16];
return [4 /*yield*/, this.addFileToLibrary(objects)];
case 15:
_m.sent();
return [3 /*break*/, 19];
case 16:
_m.trys.push([16, 18, , 19]);
contentTypeIdField = "ContentTypeId";
// check if item contenttype is passed, then update the object with content type id, else, pass the object
if (contentTypeId !== undefined && contentTypeId.startsWith("0x01"))
objects[contentTypeIdField] = contentTypeId;
return [4 /*yield*/, sp_1.sp.web.lists.getById(listId).items.add(objects)];
case 17:
iar = _m.sent();
if (onSubmitted) {
onSubmitted(iar.data, this.props.returnListItemInstanceOnSubmit !== false
? iar.item
: undefined);
}
return [3 /*break*/, 19];
case 18:
error_2 = _m.sent();
apiError = error_2.message;
if (onSubmitError) {
onSubmitError(objects, error_2);
}
console.log("Error", error_2);
return [3 /*break*/, 19];
case 19: return [3 /*break*/, 32];
case 20:
if (!contentTypeId.startsWith("0x0120")) return [3 /*break*/, 32];
_m.label = 21;
case 21:
_m.trys.push([21, 31, , 32]);
idField = "ID";
contentTypeIdField = "ContentTypeId";
return [4 /*yield*/, sp_1.sp.web.lists.getById(listId)];
case 22:
library = _m.sent();
folderFileName = this.getFolderName(objects);
if (!!this.props.folderPath) return [3 /*break*/, 23];
_b = library.rootFolder;
return [3 /*break*/, 25];
case 23: return [4 /*yield*/, this.getFolderByPath(this.props.folderPath, library.rootFolder)];
case 24:
_b = _m.sent();
_m.label = 25;
case 25:
folder = _b;
return [4 /*yield*/, folder.addSubFolderUsingPath(folderFileName)];
case 26:
newFolder = _m.sent();
return [4 /*yield*/, newFolder.listItemAllFields()];
case 27:
fields_2 = _m.sent();
if (!fields_2[idField]) return [3 /*break*/, 29];
folderId = fields_2[idField];
// Set the content type ID for the target item
objects[contentTypeIdField] = contentTypeId; // eslint-disable-line @typescript-eslint/no-explicit-any
return [4 /*yield*/, this.updateListItemRetry(library, folderId, objects)];
case 28:
iur = _m.sent();
if (onSubmitted) {
onSubmitted(iur.data, this.props.returnListItemInstanceOnSubmit !== false
? iur.item
: undefined);
}
return [3 /*break*/, 30];
case 29: throw new Error("Unable to read the ID of the just created folder or Document Set");
case 30: return [3 /*break*/, 32];
case 31:
error_3 = _m.sent();
apiError = error_3.message;
if (onSubmitError) {
onSubmitError(objects, error_3);
}
console.log("Error", error_3);
return [3 /*break*/, 32];
case 32:
if (!(listItemId && this.state.fieldCollection.some(function (f) { return f.isAppendOnly; }))) return [3 /*break*/, 34];
return [4 /*yield*/, this._spService.getExtendedListFormData(listId, listItemId, this.webURL)];
case 33:
updatedExtendedInfo_1 = _m.sent();
this.setState(function (prevState) { return ({
fieldCollection: prevState.fieldCollection.map(function (field) {
return field.isAppendOnly
? tslib_1.__assign(tslib_1.__assign({}, field), { notesAppendOnlyHistory: updatedExtendedInfo_1[field.columnInternalName], newValue: '', value: '' }) : field;
})
}); });
_m.label = 34;
case 34:
this.setState({
isSaving: false,
etag: newETag,
infoErrorMessages: apiError ? [{ type: MessageBar_1.MessageBarType.error, message: apiError }] : [],
});
return [3 /*break*/, 36];
case 35:
error_4 = _m.sent();
if (onSubmitError) {
onSubmitError(null, error_4);
}
console.log("Error onSubmit", error_4);
return [3 /*break*/, 36];
case 36: return [2 /*return*/];
}
});
}); };
/**
* Adds selected file to the library
*/
_this.addFileToLibrary = function (objects) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var selectedFile, _a, listId, contentTypeId, onSubmitted, onSubmitError, returnListItemInstanceOnSubmit, idField, contentTypeIdField, library, itemTitle, folder, _b, fileCreatedResult, _c, _d, _e, fields, fileId, iur, error_5;
return tslib_1.__generator(this, function (_f) {
switch (_f.label) {
case 0:
selectedFile = this.state.selectedFile;
_a = this.props, listId = _a.listId, contentTypeId = _a.contentTypeId, onSubmitted = _a.onSubmitted, onSubmitError = _a.onSubmitError, returnListItemInstanceOnSubmit = _a.returnListItemInstanceOnSubmit;
if (!(selectedFile !== undefined)) return [3 /*break*/, 13];
_f.label = 1;
case 1:
_f.trys.push([1, 12, , 13]);
idField = "ID";
contentTypeIdField = "ContentTypeId";
return [4 /*yield*/, sp_1.sp.web.lists.getById(listId)];
case 2:
library = _f.sent();
itemTitle = selectedFile !== undefined && selectedFile.fileName !== undefined && selectedFile.fileName !== ""
? selectedFile.fileName.replace(/["|*|:|<|>|?|/|\\||]/g, "_").trim() // Replace not allowed chars in folder name and trim empty spaces at the start or end.
: "";
if (!!this.props.folderPath) return [3 /*break*/, 3];
_b = library.rootFolder;
return [3 /*break*/, 5];
case 3: return [4 /*yield*/, this.getFolderByPath(this.props.folderPath, library.rootFolder)];
case 4:
_b = _f.sent();
_f.label = 5;
case 5:
folder = _b;
_d = (_c = folder.files).addChunked;
_e = [encodeURI(itemTitle)];
return [4 /*yield*/, selectedFile.downloadFileContent()];
case 6: return [4 /*yield*/, _d.apply(_c, _e.concat([_f.sent()]))];
case 7:
fileCreatedResult = _f.sent();
return [4 /*yield*/, fileCreatedResult.file.listItemAllFields()];
case 8:
fields = _f.sent();
if (!fields[idField]) return [3 /*break*/, 10];
fileId = fields[idField];
// Set the content type ID for the target item
objects[contentTypeIdField] = contentTypeId;
return [4 /*yield*/, this.updateListItemRetry(library, fileId, objects)];
case 9:
iur = _f.sent();
if (onSubmitted) {
onSubmitted(iur.data, returnListItemInstanceOnSubmit !== false
? iur.item
: undefined);
}
return [3 /*break*/, 11];
case 10: throw new Error("Unable to read the ID of the just created file");
case 11: return [3 /*break*/, 13];
case 12:
error_5 = _f.sent();
if (onSubmitError) {
onSubmitError(objects, error_5);
}
console.log("Error", error_5);
return [3 /*break*/, 13];
case 13: return [2 /*return*/];
}
});
}); };
/**
* Triggered when the user makes any field value change in the form
*/
_this.onChange = function (internalName,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
newValue, validate, additionalData) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var fieldCol, field, useModernTaxonomyPicker, user, result, emails, index, element, user, result, validationErrors;
var _this = this;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
fieldCol = (0, lodash_1.cloneDeep)(this.state.fieldCollection || []);
field = fieldCol.filter(function (element, i) {
return element.columnInternalName === internalName;
})[0];
useModernTaxonomyPicker = this.props.useModernTaxonomyPicker;
// Init new value(s)
field.newValue = newValue;
field.stringValue = newValue ? newValue.toString() : '';
field.additionalData = additionalData;
field.subPropertyValues = {};
// Store string values for various field types
if (field.fieldType === "Choice") {
field.stringValue = newValue.text;
}
if (field.fieldType === "MultiChoice") {
field.stringValue = newValue.join(';#');
}
if (field.fieldType === "Lookup" || field.fieldType === "LookupMulti") {
field.stringValue = newValue.map(function (nv) { return nv.key + ';#' + nv.name; }).join(';#');
}
if (useModernTaxonomyPicker) {
if (field.fieldType === "TaxonomyFieldType" || field.fieldType === "TaxonomyFieldTypeMulti") {
if (Array.isArray(newValue) && newValue.length > 0) {
field.stringValue = newValue.map(function (nv) { return nv.labels.map(function (label) { return label.name; }).join(';'); }).join(';');
}
else {
field.stringValue = "";
}
}
}
else {
if (field.fieldType === "TaxonomyFieldType" || field.fieldType === "TaxonomyFieldTypeMulti") {
field.stringValue = newValue.map(function (nv) { return nv.name; }).join(';');
}
}
if (!(field.fieldType === "User" && newValue.length !== 0)) return [3 /*break*/, 3];
if (!(newValue[0].id === undefined ||
parseInt(newValue[0].id, 10).toString() === "NaN")) return [3 /*break*/, 2];
user = newValue[0].secondaryText;
if (user.indexOf("@") === -1) {
user = newValue[0].loginName;
}
return [4 /*yield*/, sp_1.sp.web.ensureUser(user)];
case 1:
result = _a.sent();
field.newValue = result.data.Id; // eslint-disable-line require-atomic-updates
field.stringValue = user;
field.subPropertyValues = {
id: result.data.Id,
title: result.data.Title,
email: result.data.Email,
};
return [3 /*break*/, 3];
case 2:
field.newValue = newValue[0].id;
_a.label = 3;
case 3:
if (!(field.fieldType === "UserMulti" && newValue.length !== 0)) return [3 /*break*/, 9];
field.newValue = [];
emails = [];
index = 0;
_a.label = 4;
case 4:
if (!(index < newValue.length)) return [3 /*break*/, 8];
element = newValue[index];
if (!(element.id === undefined ||
parseInt(element.id, 10).toString() === "NaN")) return [3 /*break*/, 6];
user = element.secondaryText;
if (user.indexOf("@") === -1) {
user = element.loginName;
}
return [4 /*yield*/, sp_1.sp.web.ensureUser(user)];
case 5:
result = _a.sent();
field.newValue.push(result.data.Id);
emails.push(user);
return [3 /*break*/, 7];
case 6:
field.newValue.push(element.id);
_a.label = 7;
case 7:
index++;
return [3 /*break*/, 4];
case 8:
field.stringValue = emails.join(";");
_a.label = 9;
case 9:
validationErrors = tslib_1.__assign({}, this.state.validationErrors);
if (validationErrors[field.columnInternalName])
delete validationErrors[field.columnInternalName];
this.setState({
fieldCollection: fieldCol,
validationErrors: validationErrors
}, function () {
if (validate)
_this.performValidation();
});
return [2 /*return*/];
}
});
}); };
/** Validation callback, used when form first loads (getListInformation) and following onChange */
_this.performValidation = function (skipFieldValueValidation) {
var _a = _this.props, useClientSideValidation = _a.useClientSideValidation, useFieldValidation = _a.useFieldValidation;
var _b = _this.state, clientValidationFormulas = _b.clientValidationFormulas, validationFormulas = _b.validationFormulas;
if (Object.keys(clientValidationFormulas).length || Object.keys(validationFormulas).length) {
_this.setState({
isSaving: true, // Disable save btn and fields while validation in progress
}, function () {
var clientSideValidationDisabled = useClientSideValidation === false;
var fieldValidationDisabled = useFieldValidation === false;
var hiddenByFormula = !clientSideValidationDisabled ? _this.evaluateColumnVisibilityFormulas() : [];
var validationErrors = tslib_1.__assign({}, _this.state.validationErrors);
if (!skipFieldValueValidation && !fieldValidationDisabled)
validationErrors = _this.evaluateFieldValueFormulas(hiddenByFormula);
_this.setState({ hiddenByFormula: hiddenByFormula, isSaving: false, validationErrors: validationErrors });
});
}
};
/** Determines visibility of fields that have show/hide formulas set in Edit Form > Edit Columns > Edit Conditional Formula */
_this.evaluateColumnVisibilityFormulas = function () {
return _this.evaluateFormulas(_this.state.clientValidationFormulas, false);
};
/** Evaluates field validation formulas set in column settings and returns a Record of error messages */
_this.evaluateFieldValueFormulas = function (hiddenFields) {
return _this.evaluateFormulas(_this.state.validationFormulas, true, true, hiddenFields);
};
/**
* Evaluates formulas and returns a Record of error messages or an array of column names that have failed validation
* @param formulas A Record / dictionary-like object, where key is internal column name and value is an object with ValidationFormula and ValidationMessage properties
* @param returnMessages Determines whether a Record of error messages is returned or an array of column names that have failed validation
* @param requireValue Set to true if the formula should only be evaluated when the field has a value
* @returns
*/
_this.evaluateFormulas = function (formulas, returnMessages, requireValue, ignoreFields) {
if (returnMessages === void 0) { returnMessages = true; }
if (requireValue === void 0) { requireValue = false; }
if (ignoreFields === void 0) { ignoreFields = []; }
var fieldCollection = _this.state.fieldCollection;
var results = {};
var _loop_2 = function (i) {
var fieldName = Object.keys(formulas)[i];
if (formulas[fieldName]) {
var field = fieldCollection.find(function (f) { return f.columnInternalName === fieldName; });
if (!field)
return "continue";
if (ignoreFields.indexOf(fieldName) > -1)
return "continue"; // Skip fields that are being ignored (e.g. hidden by formula)
var formula = formulas[fieldName].ValidationFormula;
var message = formulas[fieldName].ValidationMessage;
if (!formula)
return "continue";
var context = _this.getFormValuesForValidation();
if (requireValue && !context[fieldName])
return "continue";
var result = _this._formulaEvaluation.evaluate(formula, context);
if (Boolean(result) !== true) {
results[fieldName] = message;
}
}
};
for (var i = 0; i < Object.keys(formulas).length; i++) {
_loop_2(i);
}
if (!returnMessages) {
return Object.keys(results);
}
return results;
};
/**
* Used for validation. Returns a Record of field values, where key is internal column name and value is the field value.
* Expands certain properties and stores many of them as primitives (strings, numbers or bools) so the expression evaluator
* can process them. For example: a User column named Person will have values stored as Person, Person.email, Person.title etc.
* This is so the expression evaluator can process expressions like '=[$Person.title] == "Contoso Employee 1138"'
* @param fieldCollection Optional. Could be used to compare field values in state with previous state.
* @returns
*/
_this.getFormValuesForValidation = function (fieldCollection) {
var fieldColFromState = _this.state.fieldCollection;
if (!fieldCollection)
fieldCollection = fieldColFromState;
return fieldCollection.reduce(function (prev, cur) {
var value = cur.value;
switch (cur.fieldType) {
case "Lookup":
case "Choice":
case "TaxonomyFieldType":
case "LookupMulti":
case "MultiChoice":
case "TaxonomyFieldTypeMulti":
case "User":
case "UserMulti":
value = cur.stringValue;
break;
case "Currency":
case "Number":
if (cur.value !== undefined && cur.value !== null)
value = Number(cur.value);
if (cur.newValue !== undefined && cur.newValue !== null)
value = Number(cur.newValue);
break;
case "URL":
if (cur.value !== undefined && cur.value !== null)
value = cur.value.Url;
if (cur.newValue !== undefined && cur.newValue !== null)
value = cur.newValue.Url;
value = cur.newValue ? cur.newValue.Url : null;
break;
default:
value = cur.newValue || cur.value;
break;
}
prev[cur.columnInternalName] = value;
if (cur.subPropertyValues) {
Object.keys(cur.subPropertyValues).forEach(function (key) {
prev["".concat(cur.columnInternalName, ".").concat(key)] = cur.subPropertyValues[key];
});
}
return prev;
}, {});
};
/**
* Invoked when component first mounts, loads information about the SharePoint list, fields and list item
*/
_this.getListInformation = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var _a, listId, listItemId, disabledFields, respectETag, customIcons, onListItemLoaded, contentTypeId, listInfo, additionalInfo, numberFields, validationFormulas, contentTypeName, clientValidationFormulas, headerJSON, footerJSON, bodySections, customFormatInfo, spList, item, isEditingItem, etag, extendedInfo, spListItem, appendOnlyFields, tempFields, sortedFields, installedLanguages, error_6;
var _this = this;
var _b, _c;
return tslib_1.__generator(this, function (_d) {
switch (_d.label) {
case 0:
_a = this.props, listId = _a.listId, listItemId = _a.listItemId, disabledFields = _a.disabledFields, respectETag = _a.respectETag, customIcons = _a.customIcons, onListItemLoaded = _a.onListItemLoaded;
contentTypeId = this.props.contentTypeId;
_d.label = 1;
case 1:
_d.trys.push([1, 12, , 13]);
return [4 /*yield*/, this._spService.getListFormRenderInfo(listId, this.webURL)];
case 2:
listInfo = _d.sent();
return [4 /*yield*/, this._spService.getAdditionalListFormFieldInfo(listId, this.webURL)];
case 3:
additionalInfo = _d.sent();
numberFields = additionalInfo === null || additionalInfo === void 0 ? void 0 : additionalInfo.filter(function (f) { return f.TypeAsString === "Number" || f.TypeAsString === "Currency"; });
validationFormulas = additionalInfo.reduce(function (prev, cur) {
if (!prev[cur.InternalName] && cur.ValidationFormula) {
prev[cur.InternalName] = {
ValidationFormula: cur.ValidationFormula,
ValidationMessage: cur.ValidationMessage,
};
}
return prev;
}, {});
// If no content type ID is provided, use the default (first one in the list)
if (contentTypeId === undefined || contentTypeId === "") {
contentTypeId = Object.keys(listInfo.ContentTypeIdToNameMap)[0];
}
contentTypeName = listInfo.ContentTypeIdToNameMap[contentTypeId];
clientValidationFormulas = listInfo.ClientForms.Edit[contentTypeName].reduce(function (prev, cur) {
if (cur.ClientValidationFormula) {
prev[cur.InternalName] = {
ValidationFormula: cur.ClientValidationFormula,
ValidationMessage: cur.ClientValidationMessage,
};
}
return prev;
}, {});
headerJSON = void 0, footerJSON = void 0;
bodySections = void 0;
if (listInfo.ClientFormCustomFormatter && listInfo.ClientFormCustomFormatter[contentTypeId]) {
customFormatInfo = JSON.parse(listInfo.ClientFormCustomFormatter[contentTypeId]);
bodySections = (_b = customFormatInfo.bodyJSONFormatter) === null || _b === void 0 ? void 0 : _b.sections;
headerJSON = customFormatInfo.headerJSONFormatter;
footerJSON = customFormatInfo.footerJSONFormatter;
}
spList = sp_1.sp.web.lists.getById(listId);
item = null;
isEditingItem = listItemId !== undefined && listItemId !== null && listItemId !== 0;
etag = undefined;
extendedInfo = undefined;
if (!isEditingItem) return [3 /*break*/, 8];
spListItem = spList.items.getById(listItemId);
if (contentTypeId.startsWith("0x0120") || contentTypeId.startsWith("0x0101")) {
spListItem.select("*", "FileLeafRef"); // Explainer: FileLeafRef is not loaded by default. Load it to show the file/fo