Files
ERP/sophal/js/form/form.widget.js

484 lines
12 KiB
JavaScript

/*
* Axelor Business Solutions
*
* Copyright (C) 2005-2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
(function() {
"use strict";
var ui = angular.module('axelor.ui');
/**
* The Form widget.
*
*/
ui.formWidget('Form', {
priority: 100,
css: "dynamic-form",
scope: false,
compile: function(element, attrs) {
element.hide();
element.find('[x-field],[data-field]').each(function(){
var elem = $(this),
name = elem.attr('x-field') || elem.attr('data-field');
if (name && elem.attr('ui-button') === undefined) {
if (!elem.attr('ng-model')) {
elem.attr('ng-model', 'record.' + name);
}
if (!elem.attr('ng-required')) {
// always attache a required validator to make
// dynamic `required` attribute change effective
elem.attr('ng-required', false);
}
}
});
return ui.formCompile.apply(this, arguments);
},
link: function(scope, element, attrs, controller) {
element.on('submit', function(e) {
e.preventDefault();
});
scope.$watch('record', function formRecordWatch(rec, old) {
if (element.is(':visible')) {
return;
}
scope.ajaxStop(function() {
element.show();
axelor.$adjustSize();
});
});
}
});
/**
* This directive is used filter $watch on scopes of inactive tabs.
*
*/
ui.directive('uiTabGate', function() {
return {
compile: function compile(tElement, tAttrs) {
return {
pre: function preLink(scope, element, attrs) {
scope.$watchChecker(function(current) {
if (current.$$popupStack.length) return true;
if (current.tabSelected === undefined) {
return !scope.tab || scope.tab.selected === undefined || scope.tab.selected;
}
return current.tabSelected;
});
}
};
}
};
});
/**
* This directive is used to filter $watch on scopes of hidden forms.
*
*/
ui.directive('uiFormGate', function() {
return {
compile: function compile(tElement, tAttrs) {
return {
pre: function preLink(scope, element, attrs) {
var parent = null;
scope.$watchChecker(function(current) {
if (scope.tabSelected === false) {
return false;
}
if (parent === null) {
parent = element.parents('[ui-show]:first');
}
// hack for hidden nested editors (#2173)
if (scope.$$forceWatch) {
return true;
}
return !(parent.hasClass('ui-hide') || parent.hasClass('ui-hide'));
});
}
};
}
};
});
/**
* This directive is used to filter $watch on scopes based on some condition.
*
*/
ui.directive('uiWatchIf', ['$parse', function($parse) {
return {
compile: function compile(tElement, tAttrs) {
return {
pre: function preLink(scope, element, attrs) {
var value = false,
expression = $parse(attrs.uiWatchIf);
scope.$watchChecker(function (current) {
if (current === scope) {
return value = expression(scope);
}
return value;
});
}
};
}
};
}]);
function toBoolean(value) {
if (value && value.length !== 0) {
var v = angular.lowercase("" + value);
value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
} else {
value = false;
}
return value;
}
/**
* This directive is used to speedup uiFormGate.
*/
ui.directive('uiShow', function() {
return {
scope: true, // create new scope to always watch the expression
link: function link(scope, element, attrs) {
scope.$$shouldWatch = true;
scope.$watch(attrs.uiShow, function uiShowWatchAction(value){
var val = toBoolean(value);
element.css({ display: val ? '' : 'none', opacity: 0 }).toggleClass('ui-hide', !val);
if (val) {
element.animate({ opacity: 1 }, 300);
}
});
}
};
});
/**
* This directive is used by view-pane to attach/detach element from DOM tree
*/
ui.directive('uiAttach', function () {
return function (scope, element, attrs) {
var parent = null;
var detachTimer = null;
var uiAttachWatch = function uiAttachWatch(attach) {
var result = toBoolean(attach);
if (result) {
if (parent) {
if (detachTimer) {
clearTimeout(detachTimer);
detachTimer = null;
} else {
element.appendTo(parent);
}
parent = null;
scope.$broadcast('dom:attach');
}
} else {
parent = element.parent();
scope.$broadcast('dom:detach');
detachTimer = setTimeout(function () {
detachTimer = null;
element.detach();
}, 200);
}
};
uiAttachWatch.uiAttachWatch = true;
scope.$watch(attrs.uiAttach, uiAttachWatch, true);
scope.$on('$destroy', function () {
if (detachTimer) {
clearTimeout(detachTimer);
detachTimer = null;
}
if (parent) {
parent = null;
element.remove();
}
});
};
});
/**
* This directive can be used by widget to restore scroll when element is re-attached to DOM tree.
*/
ui.directive('uiAttachScroll', function () {
return function (scope, element, attrs) {
setTimeout(function () {
var elem = element;
var scrollTop = 0;
if (attrs.uiAttachScroll) {
elem = element.find(attrs.uiAttachScroll);
}
elem.on('scroll', function () {
scrollTop = this.scrollTop;
});
function resetScroll() {
elem.scrollTop(scrollTop);
}
scope.$on('dom:attach', resetScroll);
scope.$on('tab:select', resetScroll);
}, 300);
};
});
ui.directive('uiWidgetStates', ['$parse', '$interpolate', function($parse, $interpolate) {
function isValid(scope, name) {
if (!name) return scope.isValid();
var ctrl = scope.form;
if (ctrl) {
ctrl = ctrl[name];
}
if (ctrl) {
return ctrl.$valid;
}
}
function withContext(scope, record) {
var values = _.extend({}, scope._context, scope._jsonContext, record);
return _.extend(values, {
$user: axelor.config['user.login'],
$group: axelor.config['user.group'],
$userId: axelor.config['user.id'],
});
}
function handleCondition(scope, field, attr, condition, negative) {
if (!condition || _.isBoolean(condition)) {
return;
}
scope.$on("on:record-change", function(e, rec, force) {
if (field && field.jsonField) {
handle(scope.record);
} else if (rec === scope.record || force) {
handle(rec);
}
});
scope.$on("on:grid-selection-change", function(e, context) {
if (field && field.jsonField) return;
if (!scope._isDetailsForm) {
handle(context);
}
});
scope.$watch("isReadonly()", watcher);
scope.$watch("isRequired()", watcher);
scope.$watch("isValid()", watcher);
var expr = $parse(condition);
function watcher(current, old) {
var rec = scope.record;
if (rec === undefined && current === old) return;
if (rec === undefined && scope.getContext) {
rec = scope.getContext();
}
handle(rec);
}
function handle(rec) {
var value;
try {
value = !!axelor.$eval(scope, expr, withContext(scope, rec));
} catch (e) {
console.error('FAILED:', condition, e);
}
// defer attr change to allow field init, see RM-14998
scope.$applyAsync(function () {
scope.attr(attr, negative ? !value : value);
});
}
}
function handleHilites(scope, field) {
if (!field || _.isEmpty(field.hilites)) {
return;
}
var hilites = field.hilites || [];
var exprs = _.map(_.pluck(hilites, 'condition'), function (s) { return $parse(s); });
function handle(rec) {
for (var i = 0; i < hilites.length; i++) {
var hilite = hilites[i];
var expr = exprs[i];
var value = false;
try {
value = axelor.$eval(scope, expr, withContext(scope, rec));
} catch (e) {
console.error('FAILED:', hilite, e);
}
if (value) {
return scope.attr('highlight', {
hilite: hilite,
passed: value
});
}
}
return scope.attr('highlight', {});
}
scope.$on("on:record-change", function(e, rec) {
if (rec === scope.record) {
handle(rec);
}
});
}
function handleBind(scope, field) {
if (!field.bind || !field.name) {
return;
}
var expr = $interpolate(field.bind);
var last = null;
function handle(rec) {
var value;
try {
value = expr(withContext(scope, rec));
if (value.length === 0) {
value = null;
}
} catch (e) {
console.error('FAILED:', field.bind, e);
}
if (scope.setValue && scope.record && last !== value) {
scope.setValue(last = value);
}
}
scope.$on("on:record-change", function(e, rec) {
if (field && field.jsonField) {
handle(scope.record);
} else if (rec && rec === scope.record) {
handle(rec);
}
});
}
function handleValueExpr(scope, field) {
if (!field.valueExpr || !field.name) {
return;
}
var expr = $parse(field.valueExpr);
function handle(rec) {
var value;
try {
value = axelor.$eval(scope, expr, withContext(scope, rec));
if (value && value.length === 0) {
value = null;
}
} catch (e) {
console.error('FAILED:', field.valueExpr, e);
}
if (scope.setValue && scope.record) {
scope.setValue(value, false);
}
}
scope.$on("on:record-change", function(e, rec) {
scope.$timeout(function () {
if (field && field.jsonField) {
handle(scope.record);
} else if (rec && rec === scope.record) {
handle(rec);
}
});
});
}
function handleFor(scope, field, attr, conditional, negative) {
if (field[conditional]) {
handleCondition(scope, field, attr, field[conditional], negative);
}
}
function handleForField(scope) {
var field = scope.field;
if (!field) return;
handleFor(scope, field, "valid", "validIf");
handleFor(scope, field, "hidden", "hideIf");
handleFor(scope, field, "hidden", "showIf", true);
handleFor(scope, field, "readonly", "readonlyIf");
handleFor(scope, field, "required", "requiredIf");
handleFor(scope, field, "collapse", "collapseIf");
handleFor(scope, field, "canNew", "canNew");
handleFor(scope, field, "canView", "canView");
handleFor(scope, field, "canEdit", "canEdit");
handleFor(scope, field, "canRemove", "canRemove");
handleFor(scope, field, "canSelect", "canSelect");
handleHilites(scope, field);
handleBind(scope, field);
handleValueExpr(scope, field);
}
function handleForView(scope) {
var field = scope.schema;
if (!field) return;
handleFor(scope, field, "canNew", "canNew");
handleFor(scope, field, "canEdit", "canEdit");
handleFor(scope, field, "canSave", "canSave");
handleFor(scope, field, "canCopy", "canCopy");
handleFor(scope, field, "canDelete", "canDelete");
handleFor(scope, field, "canArchive", "canArchive");
handleFor(scope, field, "canAttach", "canAttach");
}
return function(scope, element, attrs) {
scope.$evalAsync(function() {
if (element.is('[ui-form]')) {
return handleForView(scope);
}
handleForField(scope);
});
};
}]);
})();