adding umbNestedProperty to build up a json path for a targeted nested property type.
This commit is contained in:
+49
@@ -0,0 +1,49 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc component
|
||||
* @name Umbraco.umbBlockListBlockContent
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* The component for a style-inheriting block of the block list property editor.
|
||||
*/
|
||||
angular
|
||||
.module("umbraco")
|
||||
.component("umbNestedProperty", {
|
||||
transclude: true,
|
||||
template: '<div ng-transclude></div>',
|
||||
controller: NestedPropertyController,
|
||||
controllerAs: 'vm',
|
||||
bindings: {
|
||||
propertyTypeAlias: "@",
|
||||
elementTypeIndex: "@"
|
||||
},
|
||||
require: {
|
||||
umbNestedProperty: "?^^umbNestedProperty"
|
||||
}
|
||||
});
|
||||
|
||||
function NestedPropertyController($scope) {
|
||||
var vm = this;
|
||||
vm.$onInit = function () {
|
||||
|
||||
};
|
||||
|
||||
// returns a jsonpath for where this property is located in a hierarchy
|
||||
// this will call into all hierarchical parents
|
||||
vm.getValidationPath = function () {
|
||||
|
||||
var path = vm.umbNestedProperty ? vm.umbNestedProperty.getValidationPath() : "$";
|
||||
if (vm.propertyTypeAlias && vm.elementTypeIndex) {
|
||||
path += ".[nestedValidation].[" + vm.elementTypeIndex + "].[" + vm.propertyTypeAlias + "]";
|
||||
return path;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
})();
|
||||
+3
-1
@@ -15,7 +15,7 @@ angular.module("umbraco.directives")
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
templateUrl: 'views/components/property/umb-property.html',
|
||||
link: function (scope) {
|
||||
link: function (scope, element, attr, ctrls) {
|
||||
|
||||
scope.controlLabelTitle = null;
|
||||
if(Umbraco.Sys.ServerVariables.isDebuggingEnabled) {
|
||||
@@ -43,6 +43,8 @@ angular.module("umbraco.directives")
|
||||
$scope.propertyActions = actions;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
+2
-1
@@ -63,7 +63,8 @@
|
||||
templateUrl: Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + "/views/propertyeditors/nestedcontent/nestedcontent.editor.html",
|
||||
scope: {
|
||||
ngModel: '=',
|
||||
tabAlias: '='
|
||||
tabAlias: '=',
|
||||
itemIndex: '@'
|
||||
},
|
||||
link: link
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
**/
|
||||
function valServer(serverValidationManager) {
|
||||
return {
|
||||
require: ['ngModel', '?^^umbProperty', '?^^umbVariantContent'],
|
||||
require: ['ngModel', '?^^umbProperty', '?^^umbVariantContent', '?^^umbNestedProperty'],
|
||||
restrict: "A",
|
||||
scope: {},
|
||||
link: function (scope, element, attr, ctrls) {
|
||||
@@ -21,6 +21,7 @@ function valServer(serverValidationManager) {
|
||||
|
||||
// optional reference to the varaint-content-controller, needed to avoid validation when the field is invariant on non-default languages.
|
||||
var umbVariantCtrl = ctrls.length > 2 ? ctrls[2] : null;
|
||||
var umbNestedPropertyCtrl = ctrls.length > 3 ? ctrls[3] : null;
|
||||
|
||||
var currentProperty = umbPropCtrl.property;
|
||||
var currentCulture = currentProperty.culture;
|
||||
@@ -75,7 +76,10 @@ function valServer(serverValidationManager) {
|
||||
|
||||
if (modelCtrl.$invalid) {
|
||||
modelCtrl.$setValidity('valServer', true);
|
||||
|
||||
//clear the server validation entry
|
||||
// TODO: We'll need to handle this differently since this will need to target the actual 'fieldName' or validation
|
||||
// path if there is one
|
||||
serverValidationManager.removePropertyError(currentProperty.alias, currentCulture, fieldName, currentSegment);
|
||||
stopWatch();
|
||||
}
|
||||
@@ -105,9 +109,35 @@ function valServer(serverValidationManager) {
|
||||
stopWatch();
|
||||
}
|
||||
}
|
||||
unsubscribe.push(serverValidationManager.subscribe(currentProperty.alias,
|
||||
|
||||
// TODO: If this is a property/field within a complex editor which means it could be a nested/nested/nested property/field
|
||||
// we need to figure out a way to get it's "Path" (or jsonpath) which can be represented by something like:
|
||||
// $.[nestedValidation].[0].[prop1].[nestedValidation].[0].[prop2]
|
||||
// Or ... if we have names instead of indexes (which is seems like we do)
|
||||
// $.nestedValidation.[type1].[prop1].[nestedValidation].[type2].[prop2]
|
||||
// This would mean:
|
||||
// - the first row/item in a complex editor
|
||||
// - within the property 'prop1'
|
||||
// - the first row/item in a complex editor
|
||||
// - within the property 'prop2'
|
||||
// So how can we figure out this path? The only way is really by looking up our current hierarchy of items
|
||||
// TODO: OK, so we thought we had it with umb-property being able to know the content type BUT this doesn't work
|
||||
// because the validation results could have a many rows for the same content type, we need to have the index available
|
||||
// so the firest example above works much better.
|
||||
// ... OK ... looks like we have an index to work with, but we'll need to update the block editor to support this too.
|
||||
|
||||
var propertyValidationPath = umbNestedPropertyCtrl ? umbNestedPropertyCtrl.getValidationPath() : null;
|
||||
|
||||
unsubscribe.push(serverValidationManager.subscribe(
|
||||
currentProperty.alias,
|
||||
currentCulture,
|
||||
fieldName,
|
||||
// use the propertyValidationPath for the fieldName value if there is one since if there is one it means it's a complex
|
||||
// editor and as such the 'fieldName' will be empty. The serverValidationManager knows how to handle the jsonpath
|
||||
// string as the fieldName.
|
||||
// TODO: This isn't quite true! If there is a fieldName specified, then it will need to be added to the
|
||||
// validation path. We should pass in the fieldName to umbNestedPropertyCtrl.getValidationPath(); since this could very well be targeting a specific field
|
||||
|
||||
propertyValidationPath ? propertyValidationPath : fieldName,
|
||||
serverValidationManagerCallback,
|
||||
currentSegment)
|
||||
);
|
||||
|
||||
@@ -162,6 +162,12 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
|
||||
//There will always be at least 4 parts for content properties since all model errors for properties are prefixed with "_Properties"
|
||||
//If it is not prefixed with "_Properties" that means the error is for a field of the object directly.
|
||||
|
||||
// TODO: This 4 part dot notation isn't ideal and instead it would probably be nicer to have a json structure as the key (which could be converted
|
||||
// to base64 if we cannot do that since it's a 'key'). That way the key can be flexible and 'future proof' since I'm sure something in the future
|
||||
// will change for this. Another idea is to just have a single key for one property type and have the model error a json structure that handles
|
||||
// everything. This would probably be the 'nicest' way but would require quite a lot of work. We are part way there with how we are doing
|
||||
// validation for complex editors.
|
||||
|
||||
// Example: "_Properties.headerImage.en-US.mySegment.myField"
|
||||
// * it's for a property since it has a _Properties prefix
|
||||
// * it's for the headerImage property type
|
||||
|
||||
@@ -36,7 +36,6 @@ function serverValidationManager($timeout) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function getPropertyErrors(self, propertyAlias, culture, segment, fieldName) {
|
||||
if (!Utilities.isString(propertyAlias)) {
|
||||
throw "propertyAlias must be a string";
|
||||
@@ -98,7 +97,30 @@ function serverValidationManager($timeout) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function parseComplexEditorError(errorMsg) {
|
||||
var json = JSON.parse(errorMsg);
|
||||
|
||||
var nestedValidation = json["nestedValidation"];
|
||||
if (!nestedValidation) {
|
||||
throw "Invalid JSON structure for complex property, missing 'nestedValidation'";
|
||||
}
|
||||
|
||||
// each key represents an element type, the key is it's alias
|
||||
var keys = Object.keys(nestedValidation);
|
||||
|
||||
// TODO: Could we use an individual instance of serverValidationManager for each element type? It could/should work the way
|
||||
// it does today since it currently manages all callbacks for all simple properties on a content item based on a content type.
|
||||
// Hrmmm... only thing is then how to dispose/cleanup of these instances?
|
||||
|
||||
// TODO: ... actually, because we are registering a JSONPath into the 'fieldName' for when complex editors subscribe, perhaps
|
||||
// the only thing we need to do is build up all of the different JSONPath's and their errors here based on this object and then
|
||||
// execute callbacks for each? So I think we need to make a function recursively return all possible keys! ... we can even have tests
|
||||
// for that :)
|
||||
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
@@ -173,7 +195,14 @@ function serverValidationManager($timeout) {
|
||||
if (!segment) {
|
||||
segment = null;
|
||||
}
|
||||
|
||||
|
||||
// TODO: Check if the fieldName is a jsonpath, we will know this if it starts with $.
|
||||
// in which case we need to handle this a little differently.
|
||||
if (fieldName && fieldName.startsWith("$.")) {
|
||||
// TODO: Or... Do we even need to deal with it differently? Maybe with some luck
|
||||
// we can just store that path and use it. Lets see how this goes.
|
||||
}
|
||||
|
||||
if (propertyAlias === null) {
|
||||
callbacks.push({
|
||||
propertyAlias: null,
|
||||
@@ -370,6 +399,10 @@ function serverValidationManager($timeout) {
|
||||
* Adds an error message for the content property
|
||||
*/
|
||||
addPropertyError: function (propertyAlias, culture, fieldName, errorMsg, segment) {
|
||||
|
||||
// TODO: We need to handle the errorMsg in a special way to check if this is a json structure. If it is we know we are dealing with
|
||||
// a complex editor and in which case we'll need to adjust how everything works.
|
||||
|
||||
if (!propertyAlias) {
|
||||
return;
|
||||
}
|
||||
@@ -383,31 +416,39 @@ function serverValidationManager($timeout) {
|
||||
segment = null;
|
||||
}
|
||||
|
||||
//only add the item if it doesn't exist
|
||||
if (!this.hasPropertyError(propertyAlias, culture, fieldName, segment)) {
|
||||
this.items.push({
|
||||
propertyAlias: propertyAlias,
|
||||
culture: culture,
|
||||
segment: segment,
|
||||
fieldName: fieldName,
|
||||
errorMsg: errorMsg
|
||||
});
|
||||
}
|
||||
|
||||
//find all errors for this item
|
||||
var errorsForCallback = getPropertyErrors(this, propertyAlias, culture, segment, fieldName);
|
||||
//we should now call all of the call backs registered for this error
|
||||
var cbs = this.getPropertyCallbacks(propertyAlias, culture, fieldName, segment);
|
||||
//call each callback for this error
|
||||
for (var cb in cbs) {
|
||||
executeCallback(this, errorsForCallback, cbs[cb].callback, culture, segment);
|
||||
// if the error message is json it's a complex editor validation response that we need to parse
|
||||
if (errorMsg.startsWith("{")) {
|
||||
parseComplexEditorError(errorMsg);
|
||||
}
|
||||
else {
|
||||
|
||||
//only add the item if it doesn't exist
|
||||
if (!this.hasPropertyError(propertyAlias, culture, fieldName, segment)) {
|
||||
this.items.push({
|
||||
propertyAlias: propertyAlias,
|
||||
culture: culture,
|
||||
segment: segment,
|
||||
fieldName: fieldName,
|
||||
errorMsg: errorMsg
|
||||
});
|
||||
}
|
||||
|
||||
//find all errors for this item
|
||||
var errorsForCallback = getPropertyErrors(this, propertyAlias, culture, segment, fieldName);
|
||||
//we should now call all of the call backs registered for this error
|
||||
var cbs = this.getPropertyCallbacks(propertyAlias, culture, fieldName, segment);
|
||||
//call each callback for this error
|
||||
for (var cb in cbs) {
|
||||
executeCallback(this, errorsForCallback, cbs[cb].callback, culture, segment);
|
||||
}
|
||||
|
||||
//execute variant specific callbacks here too when a propery error is added
|
||||
var variantCbs = this.getVariantCallbacks(culture, segment);
|
||||
//call each callback for this error
|
||||
for (var cb in variantCbs) {
|
||||
executeCallback(this, errorsForCallback, variantCbs[cb].callback, culture, segment);
|
||||
}
|
||||
|
||||
//execute variant specific callbacks here too when a propery error is added
|
||||
var variantCbs = this.getVariantCallbacks(culture, segment);
|
||||
//call each callback for this error
|
||||
for (var cb in variantCbs) {
|
||||
executeCallback(this, errorsForCallback, variantCbs[cb].callback, culture, segment);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
+7
-4
@@ -20,10 +20,13 @@
|
||||
|
||||
<div class="umb-block-list__block" ng-class="{'--open':block.isOpen}">
|
||||
|
||||
<umb-block-list-scoped-block-content ng-if="block.config.stylesheet" class="umb-block-list__block--content blockelement__draggable-element" view="{{block.view}}" stylesheet="/{{::block.config.stylesheet}}" api="vm.blockEditorApi" block="block" index="$index">
|
||||
</umb-block-list-scoped-block-content>
|
||||
<umb-block-list-block-content ng-if="!block.config.stylesheet" class="umb-block-list__block--content" view="{{block.view}}" api="vm.blockEditorApi" block="block" index="$index">
|
||||
</umb-block-list-block-content>
|
||||
<umb-nested-property property-type-alias="{{property.propertyAlias}}"
|
||||
element-type-index="{{$index}}">
|
||||
<umb-block-list-scoped-block-content ng-if="block.config.stylesheet" class="umb-block-list__block--content blockelement__draggable-element" view="{{block.view}}" stylesheet="/{{::block.config.stylesheet}}" api="vm.blockEditorApi" block="block" index="$index">
|
||||
</umb-block-list-scoped-block-content>
|
||||
<umb-block-list-block-content ng-if="!block.config.stylesheet" class="umb-block-list__block--content" view="{{block.view}}" api="vm.blockEditorApi" block="block" index="$index">
|
||||
</umb-block-list-block-content>
|
||||
</umb-nested-property>
|
||||
|
||||
<div class="umb-block-list__block--actions">
|
||||
<button type="button" class="btn-reset umb-outline action --settings" localize="title" title="actions_editSettings" ng-click="vm.blockEditorApi.openSettingsForBlock(block);" ng-if="block.showSettings === true">
|
||||
|
||||
@@ -54,10 +54,6 @@
|
||||
labels.content_createEmpty = data[1];
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
vm.$onInit = function() {
|
||||
|
||||
inlineEditing = vm.model.config.useInlineEditingAsDefault;
|
||||
|
||||
+6
@@ -586,8 +586,14 @@
|
||||
for (var p = 0; p < tab.properties.length; p++) {
|
||||
var prop = tab.properties[p];
|
||||
|
||||
// store the original alias before we change below, see notes
|
||||
prop.propertyAlias = prop.alias;
|
||||
|
||||
// NOTE: This is super ugly, the reason it is like this is because it controls the label/html id in the umb-property component at a higher level.
|
||||
// not pretty :/ but we can't change this now since it would require a bunch of plumbing to be able to change the id's higher up.
|
||||
prop.alias = model.alias + "___" + prop.alias;
|
||||
|
||||
// TODO: Do we need to deal with this separately?
|
||||
// Force validation to occur server side as this is the
|
||||
// only way we can have consistency between mandatory and
|
||||
// regex validation messages. Not ideal, but it works.
|
||||
|
||||
+14
-7
@@ -1,13 +1,20 @@
|
||||
<div class="umb-pane">
|
||||
<div ng-repeat="property in tab.properties" class="umb-nested-content-property-container">
|
||||
<div ng-repeat="property in tab.properties" class="umb-nested-content-property-container">
|
||||
|
||||
<umb-property property="property" ng-class="{'umb-nested-content--not-supported': property.notSupported, 'umb-nested-content--mandatory': property.ncMandatory}" data-element="property-{{property.alias}}">
|
||||
<umb-property-editor model="property"></umb-property-editor>
|
||||
</umb-property>
|
||||
<umb-property property="property"
|
||||
ng-class="{'umb-nested-content--not-supported': property.notSupported, 'umb-nested-content--mandatory': property.ncMandatory}"
|
||||
data-element="property-{{property.alias}}">
|
||||
|
||||
<div ng-if="property.notSupported" class="umb-nested-content-overlay"></div>
|
||||
<umb-nested-property property-type-alias="{{property.propertyAlias}}"
|
||||
element-type-index="{{itemIndex}}">
|
||||
<umb-property-editor model="property"></umb-property-editor>
|
||||
</umb-nested-property>
|
||||
|
||||
<p ng-if="property.notSupported">{{property.notSupportedMessage}}</p>
|
||||
</umb-property>
|
||||
|
||||
</div>
|
||||
<div ng-if="property.notSupported" class="umb-nested-content-overlay"></div>
|
||||
|
||||
<p ng-if="property.notSupported">{{property.notSupportedMessage}}</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@
|
||||
</div>
|
||||
|
||||
<div class="umb-nested-content__content" ng-if="vm.currentNode.key === node.key && !vm.sorting">
|
||||
<umb-nested-content-editor ng-model="node" tab-alias="ncTabAlias" />
|
||||
<umb-nested-content-editor ng-model="node" item-index="{{$index}}" tab-alias="ncTabAlias" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user