use val-tab directive in doc type editor

This commit is contained in:
Mads Rasmussen
2021-07-09 14:53:52 +02:00
parent 03128d1085
commit 69ebd6aebd
12 changed files with 119 additions and 141 deletions

View File

@@ -9,21 +9,12 @@
const vm = this;
vm.$onInit = onInit;
vm.updateName = updateName;
vm.removeGroup = removeGroup;
vm.whenNameFocus = whenNameFocus;
vm.whenFocus = whenFocus;
vm.changeSortOrderValue = changeSortOrderValue;
function onInit() {
// we need a group name for the validation and angular doesn't allow dashes in form name.
// this is a workaround to make it work
const identifier = vm.group.key.replaceAll('-', '');
vm.formName = `groupForm${identifier}`;
}
function updateName (group) {
if (vm.onUpdateName) {
vm.onUpdateName({ group });
@@ -66,10 +57,11 @@
allowRemove: '<',
onRemove: '&',
sorting: '<',
valServerFieldName: '@',
onNameFocus: '&',
onFocus: '&',
onChangeSortOrderValue: '&'
onChangeSortOrderValue: '&',
valServerFieldName: '@',
valTabAlias: "@"
},
controller: umbContentTypeGroupController
};

View File

@@ -40,9 +40,10 @@
sortable: '<',
onEdit: '&',
onRemove: '&',
onChangeSortOrderValue: '&',
valServerFieldAlias: '@',
valServerFieldLabel: '@',
onChangeSortOrderValue: '&'
valTabAlias: '@'
},
controllerAs: 'vm',
controller: umbContentTypePropertyController

View File

@@ -3,7 +3,7 @@
function GroupsBuilderDirective(contentTypeHelper, contentTypeResource, mediaTypeResource,
dataTypeHelper, dataTypeResource, $filter, iconHelper, $q, $timeout, notificationsService,
localizationService, editorService, eventsService, overlayService) {
localizationService, editorService, eventsService, overlayService, contentEditingHelper) {
function link(scope, el, attr, ctrl) {
@@ -26,6 +26,7 @@
scope.genericTab = {
key: null,
name: "Generic",
alias: contentEditingHelper.genericTabAlias,
parentKey: null,
type: TYPE_TAB,
sortOrder: 0,
@@ -41,6 +42,10 @@
tab.indexInGroups = newValue.findIndex(group => group.key === tab.key);
});
newValue.forEach(group => {
group.validationAlias = contentEditingHelper.generateTabValidationAlias(group, newValue);
});
checkGenericTabVisibility();
if (!scope.openTabKey && scope.hasGenericTab) {

View File

@@ -1,55 +0,0 @@
(function () {
'use strict';
function valGroupsInTabDirective(angularHelper) {
function link(scope, el, attrs, ctrl) {
const valFormManager = ctrl[1];
const formCtrl = ctrl[0];
if (!valFormManager) {
return;
}
// valformmanager onValidationStatusChanged only emit updates when the parent form status changes.
// In this case we need to know the status for all sub forms so we can update each tab validation status correctly based on the child group form states.
// the code finds the child group form for a given tab and if any of those group forms are invalid the tab will be flag with an error.
scope.$watch(() => angularHelper.countAllFormErrors(formCtrl), () => {
update();
});
function update () {
const { tab, groups } = scope.$eval(attrs.valGroupsInTab);
if (!tab && !groups) {
tab.hasError = false;
return;
}
const tabGroups = [];
const tabGroupsIdentifiers = groups.filter(group => group.parentKey === tab.key).map(group => group.key.replaceAll('-', ''));
for (const [key, value] of Object.entries(formCtrl)) {
if (key.startsWith('groupForm')) {
const groupIdentifier = key.replace('groupForm', '');
if (groupIdentifier && tabGroupsIdentifiers.indexOf(groupIdentifier) !== -1) {
tabGroups.push(value);
}
}
}
tab.hasError = tabGroups.filter(group => group.$invalid).length > 0;
}
}
var directive = {
require: ['?^^form', '?^^valFormManager'],
restrict: "A",
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('valGroupsInTab', valGroupsInTabDirective);
})();

View File

@@ -12,14 +12,21 @@ function valTab($timeout) {
restrict: "A",
link: function (scope, element, attr, ctrs) {
var evts = [];
var form = ctrs[0];
var tabAlias = scope.tab.alias;
var tab = scope.$eval(attr.valTab) || scope.tab;
if (!tab) {
return;
}
var tabAlias = tab.alias || tab.key;
let closestEditor = element.closest(".blockelement-inlineblock-editor");
closestEditor = closestEditor.length === 0 ? element.closest(".umb-editor-sub-view") : closestEditor;
closestEditor = closestEditor.length === 0 ? element.closest(".umb-editor") : closestEditor;
scope.tabHasError = false;
setSuccess();
function setValidity (form) {
if (!form.$valid) {
@@ -27,23 +34,50 @@ function valTab($timeout) {
//check if the validation messages are contained inside of this tabs
if (tabContent.find(".ng-invalid").length > 0) {
scope.tabHasError = true;
setError();
} else {
scope.tabHasError = false;
setSuccess();
}
}
else {
scope.tabHasError = false;
setSuccess();
}
}
function setError () {
scope.tabHasError = true;
tab.hasError = true;
}
function setSuccess () {
scope.tabHasError = false;
tab.hasError = false;
}
function subscribe () {
for (let control of form.$$controls) {
var unbind = scope.$watch(() => control.$invalid, function () {
setValidity(form);
});
evts.push(unbind);
}
}
function unsubscribe () {
evts.forEach(event => event());
}
// we need to watch validation state on individual controls so we can update specific tabs accordingly
$timeout(() => {
for (let control of form.$$controls) {
scope.$watch(() => control.$invalid, function () {
setValidity(form);
});
}
scope.$watchCollection(() => form.$$controls, function (newValue) {
unsubscribe();
subscribe();
});
});
scope.$on('$destroy', function () {
unsubscribe();
});
}
};

View File

@@ -183,19 +183,20 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
genericTabAlias: "_umb_genericTab",
generateTabValidationAlias: function (group, groups) {
const prefix = "tab-content-";
let validationAlias = this.genericTabAlias;
if (group.parentKey) {
const parentGroup = groups.find(tab => tab.key === group.parentKey);
validationAlias = parentGroup.alias;
validationAlias = parentGroup.alias || parentGroup.key;
}
// tabs should use their own alias so direct properties will trigger the right tab
if (group.type === 1) {
validationAlias = group.alias;
validationAlias = group.alias || group.key;
}
return validationAlias;
return prefix + validationAlias;
},
registerGenericTab: function (groups) {

View File

@@ -6,7 +6,7 @@
</umb-editor-tab-bar>
<umb-box ng-repeat="tab in tabs" ng-show="tab.key === activeTabKey && tab.properties.length > 0">
<umb-box-content data-element="tab-content-{{tab.validationAlias}}">
<umb-box-content data-element="{{tab.validationAlias}}">
<umb-property
data-element="property-{{property.alias}}"
ng-repeat="property in tab.properties track by property.alias"
@@ -29,7 +29,7 @@
<div id="group-{{group.id}}">{{ group.label }}</div>
</div>
<div class="umb-group-panel__content" data-element="tab-content-{{group.validationAlias}}">
<div class="umb-group-panel__content" data-element="{{group.validationAlias}}">
<umb-property
data-element="property-{{property.alias}}"
ng-repeat="property in group.properties track by property.alias"

View File

@@ -1,61 +1,59 @@
<div data-element="group-{{vm.group.name}}" class="umb-group-builder__group" ng-class="{'-inherited': vm.group.inherited, 'umb-group-builder__group-handle -sortable': vm.sorting && !vm.group.inherited}" tabindex="0" ng-focus="vm.whenFocus()">
<ng-form name="{{ vm.formName }}">
<div class="umb-group-builder__group-title-wrapper" ng-if="vm.allowName">
<div class="umb-group-builder__group-title-wrapper" ng-if="vm.allowName" data-element="{{vm.valTabAlias}}">
<ng-form name="groupNameForm" data-element="group-name">
<div class="umb-group-builder__group-title control-group -no-margin" ng-class="{'-inherited': vm.group.inherited}">
<i class="umb-group-builder__group-title-icon icon-navigation" ng-if="vm.sorting && !vm.group.inherited"></i>
<input data-element="group-name-field"
class="umb-group-builder__group-title-input"
type="text"
localize="placeholder"
placeholder="@placeholders_entername"
name="groupName"
ng-model="vm.group.name"
ng-class="{'-placeholder': vm.group.name == ''}"
ng-change="vm.updateName(vm.group)"
ng-disabled="vm.group.inherited"
umb-auto-focus
ng-focus="vm.whenNameFocus()"
required
val-server-field="{{vm.valServerFieldName}}"
data-lpignore="true" />
<ng-form name="groupNameForm" data-element="group-name">
<div class="umb-group-builder__group-title control-group -no-margin" ng-class="{'-inherited': vm.group.inherited}">
<i class="umb-group-builder__group-title-icon icon-navigation" ng-if="vm.sorting && !vm.group.inherited"></i>
<input data-element="group-name-field"
class="umb-group-builder__group-title-input"
type="text"
localize="placeholder"
placeholder="@placeholders_entername"
name="groupName"
ng-model="vm.group.name"
ng-class="{'-placeholder': vm.group.name == ''}"
ng-change="vm.updateName(vm.group)"
ng-disabled="vm.group.inherited"
umb-auto-focus
ng-focus="vm.whenNameFocus()"
required
val-server-field="{{vm.valServerFieldName}}"
data-lpignore="true" />
<div class="umb-group-builder__group-title-val-message" ng-messages="groupNameForm.groupName.$error" show-validation-on-submit>
<div class="umb-validation-label -arrow-left" ng-message="valServerField">{{groupNameForm.groupName.errorMsg}}</div>
<div class="umb-validation-label -arrow-left" ng-message="required"><localize key="required"></localize></div>
</div>
<div class="umb-group-builder__group-title-val-message" ng-messages="groupNameForm.groupName.$error" show-validation-on-submit>
<div class="umb-validation-label -arrow-left" ng-message="valServerField">{{groupNameForm.groupName.errorMsg}}</div>
<div class="umb-validation-label -arrow-left" ng-message="required"><localize key="required"></localize></div>
</div>
</ng-form>
<div class="umb-group-builder__group-inherited-label" ng-if="vm.group.inherited">
<i class="icon icon-merge"></i>
<localize key="contentTypeEditor_inheritedFrom"></localize>: {{ vm.group.inheritedFromName }}
<span ng-repeat="contentTypeName in vm.group.parentTabContentTypeNames">
<button type="button" class="btn-link btn-small p0" ng-click="openDocumentType(vm.group.parentTabContentTypes[$index])">{{ contentTypeName }}</button>
<span ng-if="!$last">, </span>
</span>
</div>
</ng-form>
<ng-form name="groupSortOrderForm" class="umb-group-builder__group-sort-order">
<div ng-if="vm.sorting">
<input name="groupSortOrder" type="number" class="umb-property-editor-tiny" style="margin-bottom: 0;" ng-model="vm.group.sortOrder" ng-disabled="vm.group.inherited" ng-blur="vm.changeSortOrderValue()" required />
<!-- This is a manual validation message not bound to a validator -->
<div class="umb-validation-label -arrow-left" ng-if="groupSortOrderForm.groupSortOrder.$error.required && tab.showSortOrderMissing"><localize key="required"></localize></div>
<div ng-messages="groupSortOrderForm.groupSortOrder.$error" show-validation-on-submit>
<div class="umb-validation-label -arrow-left" ng-message="required"><localize key="required"></localize></div>
</div>
</div>
</ng-form>
<div class="umb-group-builder__group-remove" ng-if="vm.allowRemove">
<i class="icon-trash" ng-click="vm.removeGroup()"></i>
</div>
<div class="umb-group-builder__group-inherited-label" ng-if="vm.group.inherited">
<i class="icon icon-merge"></i>
<localize key="contentTypeEditor_inheritedFrom"></localize>: {{ vm.group.inheritedFromName }}
<span ng-repeat="contentTypeName in vm.group.parentTabContentTypeNames">
<button type="button" class="btn-link btn-small p0" ng-click="openDocumentType(vm.group.parentTabContentTypes[$index])">{{ contentTypeName }}</button>
<span ng-if="!$last">, </span>
</span>
</div>
<div class="umb-group-builder__group-content">
<ng-transclude></ng-transclude>
<ng-form name="groupSortOrderForm" class="umb-group-builder__group-sort-order">
<div ng-if="vm.sorting">
<input name="groupSortOrder" type="number" class="umb-property-editor-tiny" style="margin-bottom: 0;" ng-model="vm.group.sortOrder" ng-disabled="vm.group.inherited" ng-blur="vm.changeSortOrderValue()" required />
<!-- This is a manual validation message not bound to a validator -->
<div class="umb-validation-label -arrow-left" ng-if="groupSortOrderForm.groupSortOrder.$error.required && tab.showSortOrderMissing"><localize key="required"></localize></div>
<div ng-messages="groupSortOrderForm.groupSortOrder.$error" show-validation-on-submit>
<div class="umb-validation-label -arrow-left" ng-message="required"><localize key="required"></localize></div>
</div>
</div>
</ng-form>
<div class="umb-group-builder__group-remove" ng-if="vm.allowRemove">
<i class="icon-trash" ng-click="vm.removeGroup()"></i>
</div>
</ng-form>
</div>
<div class="umb-group-builder__group-content">
<ng-transclude></ng-transclude>
</div>
</div>

View File

@@ -1,7 +1,7 @@
<div data-element="property-{{vm.property.alias}}" class="umb-group-builder__property" ng-class="{'-active': vm.dialogIsOpen, '-active': vm.property.propertyState=='active', '-inherited': vm.property.inherited, '-locked': vm.property.locked, 'umb-group-builder__property-handle -sortable': vm.sortable && !vm.property.inherited, '-sortable-locked': vm.sortable && vm.property.inherited}">
<!-- property meta text -->
<div class="umb-group-builder__property-meta" ng-class="{'-full-width': vm.sortable}">
<div class="umb-group-builder__property-meta" ng-class="{'-full-width': vm.sortable}" data-element="{{vm.valTabAlias}}">
<ng-form name="propertyTypeForm">
<div class="control-group -no-margin" ng-if="!vm.sortable">

View File

@@ -1,4 +1,4 @@
<div ng-click="vm.click()" class="umb-group-builder__tab" ng-class="{'is-active': vm.isOpen, 'is-inherited': vm.tab.inherited, 'umb-group-builder__tab-handle -sortable': vm.sorting && !vm.tab.inherited, 'has-error': vm.tab.hasError}">
<div ng-click="vm.click()" class="umb-group-builder__tab" ng-class="{'is-active': vm.isOpen, 'is-inherited': vm.tab.inherited, 'umb-group-builder__tab-handle -sortable': vm.sorting && !vm.tab.inherited, 'has-error': vm.tab.hasError}" val-tab="vm.tab">
<div>
<div class="umb-group-builder__tab-inherited-label" ng-if="vm.tab.inherited">
<localize key="contentTypeEditor_inheritedFrom"></localize>: {{ vm.tab.inheritedFromName }}

View File

@@ -6,7 +6,7 @@
</umb-editor-tab-bar>
<umb-box ng-repeat="tab in vm.tabs" ng-show="tab.key === vm.activeTabKey && tab.properties.length > 0">
<umb-box-content data-element="tab-content-{{tab.validationAlias}}">
<umb-box-content data-element="{{tab.validationAlias}}">
<umb-property
data-element="property-{{property.alias}}"
ng-repeat="property in tab.properties track by property.alias"
@@ -35,7 +35,7 @@
<div id="group-{{group.id}}">{{ group.label }}</div>
</div>
<div class="umb-group-panel__content" data-element="tab-content-{{group.validationAlias}}">
<div class="umb-group-panel__content" data-element="{{group.validationAlias}}">
<umb-property
data-element="property-{{property.alias}}"
ng-repeat="property in group.properties track by property.alias"

View File

@@ -17,7 +17,7 @@
allow-change-name="false">
</umb-content-type-tab>
</li>
<li ng-repeat="(tabIndex, tab) in tabs track by tab.key" ng-class="{'umb-group-builder__tab-sortable': sortingMode}" umb-droppable="droppableOptionsTab" data-tab-key="{{tab.key}}" val-groups-in-tab="{ tab: tab, groups: model.groups }">
<li ng-repeat="(tabIndex, tab) in tabs track by tab.key" ng-class="{'umb-group-builder__tab-sortable': sortingMode}" umb-droppable="droppableOptionsTab" data-tab-key="{{tab.key}}">
<umb-content-type-tab
tab="tab"
is-open="tab.key === openTabKey"
@@ -125,8 +125,9 @@
allow-remove="canRemoveGroup(group) && !sortingMode"
on-remove="removeGroup(group)"
sorting="sortingMode"
on-change-sort-order-value="onChangeGroupSortOrderValue(group)"
val-server-field-name="{{'Groups[' + $index + '].Name'}}"
on-change-sort-order-value="onChangeGroupSortOrderValue(group)">
val-tab-alias="{{group.validationAlias}}">
<ul class="umb-group-builder__properties" ui-sortable="sortableOptionsProperty" ng-model="group.properties" ng-show="group.properties.length > 0 || sortingMode">
<li ng-repeat="(propertyIndex, property) in group.properties track by property.alias" ng-class="{'umb-group-builder__property-sortable': sortingMode && !property.inherited}">
@@ -136,9 +137,10 @@
sortable="sortingMode"
on-edit="editPropertyTypeSettings(property)"
on-remove="deleteProperty(group.properties, property)"
on-change-sort-order-value="onChangePropertySortOrderValue(group)"
val-server-field-alias="{{'Groups[' + groupIndex + '].Properties[' + propertyIndex + '].Alias'}}"
val-server-field-label="{{'Groups[' + groupIndex + '].Properties[' + propertyIndex + '].Label'}}"
on-change-sort-order-value="onChangePropertySortOrderValue(group)">
val-tab-alias="{{group.validationAlias}}">
</umb-content-type-property>
</li>
</ul>