Got validation working properly with pre-values, updated the content editing helper to be more generic and work with different editors like the data type editor that supports umb-property.

This commit is contained in:
Shannon
2013-08-20 16:02:10 +10:00
parent 949748a7f7
commit ddef2cc7ff
9 changed files with 110 additions and 43 deletions

View File

@@ -38,14 +38,10 @@ function contentEditingHelper($location, $routeParams, notificationsService, ser
* @description
* re-binds all changed property values to the origContent object from the newContent object and returns an array of changed properties.
*/
reBindChangedProperties: function (origContent, newContent) {
reBindChangedProperties: function (allOrigProps, allNewProps) {
var changed = [];
//get a list of properties since they are contained in tabs
var allOrigProps = this.getAllProps(origContent);
var allNewProps = this.getAllProps(newContent);
function getNewProp(alias) {
if (alias.startsWith("_umb_")) {
return null;
@@ -78,10 +74,8 @@ function contentEditingHelper($location, $routeParams, notificationsService, ser
* It's worth noting that when a 403 occurs, the data is still saved just never published, though this depends on if the entity is a new
* entity and whether or not the data fulfils the absolute basic requirements like having a mandatory Name.
*/
handleValidationErrors: function (content, modelState) {
//get a list of properties since they are contained in tabs
var allProps = this.getAllProps(content);
handleValidationErrors: function (allProps, modelState) {
//find the content property for the current error, for use in the loop below
function findContentProp(props, propAlias) {
return _.find(props, function (item) {
@@ -132,21 +126,36 @@ function contentEditingHelper($location, $routeParams, notificationsService, ser
* @description
* A function to handle what happens when we have validation issues from the server side
*/
handleSaveError: function (err, scope) {
handleSaveError: function (args) {
if (!args.err) {
throw "args.err cannot be null";
}
if (!args.allNewProps && !angular.isArray(args.allNewProps)) {
throw "args.allNewProps must be a valid array";
}
if (!args.allOrigProps && !angular.isArray(args.allOrigProps)) {
throw "args.allOrigProps must be a valid array";
}
//When the status is a 403 status, we have validation errors.
//Otherwise the error is probably due to invalid data (i.e. someone mucking around with the ids or something).
//Or, some strange server error
if (err.status === 403) {
if (args.err.status === 403) {
//now we need to look through all the validation errors
if (err.data && (err.data.modelState)) {
if (args.err.data && (args.err.data.ModelState)) {
this.handleValidationErrors(args.allNewProps, args.err.data.ModelState);
this.handleValidationErrors(err.data, err.data.modelState);
if (!this.redirectToCreatedContent(err.data.id, err.data.modelState)) {
if (!this.redirectToCreatedContent(args.err.data.id, args.err.data.ModelState)) {
//we are not redirecting because this is not new content, it is existing content. In this case
// we need to detect what properties have changed and re-bind them with the server data. Then we need
// to re-bind any server validation errors after the digest takes place.
this.reBindChangedProperties(scope.content, err.data);
if (args.rebindCallback && angular.isFunction(args.rebindCallback)) {
args.rebindCallback();
}
serverValidationManager.executeAndClearAllSubscriptions();
}
@@ -154,11 +163,11 @@ function contentEditingHelper($location, $routeParams, notificationsService, ser
return true;
}
else {
dialogService.ysodDialog(err);
dialogService.ysodDialog(args.err);
}
}
else {
dialogService.ysodDialog(err);
dialogService.ysodDialog(args.err);
}
return false;
@@ -166,7 +175,7 @@ function contentEditingHelper($location, $routeParams, notificationsService, ser
/**
* @ngdoc function
* @name umbraco.services.contentEditingHelper#handleSaveError
* @name umbraco.services.contentEditingHelper#handleSuccessfulSave
* @methodOf umbraco.services.contentEditingHelper
* @function
*

View File

@@ -85,7 +85,7 @@ function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogServ
* In both of the above, the successCallback must accept these parameters: data, status, headers, config
* If using the errorCallback it must accept these parameters: data, status, headers, config
* The success callback must return the data which will be resolved by the deferred object.
* The error callback must return an object containing: {errorMsg: errorMessage, data: originalData }
* The error callback must return an object containing: {errorMsg: errorMessage, data: originalData, status: status }
*/
resourcePromise: function (httpPromise, opts) {
var deferred = $q.defer();
@@ -101,7 +101,8 @@ function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogServ
return {
//NOTE: the default error message here should never be used based on the above docs!
errorMsg: (angular.isString(opts) ? opts : 'An error occurred!'),
data: data
data: data,
status: status
};
}
@@ -137,7 +138,8 @@ function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogServ
//return an error object including the error message for UI
deferred.reject({
errorMsg: result.errorMsg,
data: result.data
data: result.data,
status: result.status
});
}

View File

@@ -61,11 +61,22 @@ function ContentEditController($scope, $routeParams, $location, contentResource,
contentEditingHelper.handleSuccessfulSave({
scope: $scope,
newContent: data,
rebindCallback: contentEditingHelper.reBindChangedProperties(scope.content, data)
rebindCallback: contentEditingHelper.reBindChangedProperties(
contentEditingHelper.getAllProps($scope.content),
contentEditingHelper.getAllProps(data))
});
}, function (err) {
contentEditingHelper.handleSaveError(err, $scope);
}, function (err) {
var allNewProps = contentEditingHelper.getAllProps(err.data);
var allOrigProps = contentEditingHelper.getAllProps($scope.content);
contentEditingHelper.handleSaveError({
err: err,
allNewProps: allNewProps,
allOrigProps: contentEditingHelper.getAllProps($scope.content),
rebindCallback: contentEditingHelper.reBindChangedProperties(allOrigProps, allNewProps)
});
});
};
@@ -89,7 +100,11 @@ function ContentEditController($scope, $routeParams, $location, contentResource,
});
}, function (err) {
contentEditingHelper.handleSaveError(err, $scope);
contentEditingHelper.handleSaveError({
err: err,
allNewProps: contentEditingHelper.getAllProps(err.data),
allOrigProps: contentEditingHelper.getAllProps($scope.content)
});
});
};

View File

@@ -102,7 +102,17 @@ function DataTypeEditController($scope, $routeParams, $location, dataTypeResourc
});
}, function (err) {
contentEditingHelper.handleSaveError(err, $scope);
//NOTE: in the case of data type values we are setting the orig/new props
// to be the same thing since that only really matters for content/media.
contentEditingHelper.handleSaveError({
err: err,
allNewProps: $scope.preValues,
allOrigProps: $scope.preValues,
rebindCallback: function () {
createPreValueProps(err.data.preValues);
}
});
});
};

View File

@@ -59,11 +59,22 @@ function mediaEditController($scope, $routeParams, mediaResource, notificationsS
contentEditingHelper.handleSuccessfulSave({
scope: $scope,
newContent: data,
rebindCallback: contentEditingHelper.reBindChangedProperties(scope.content, data)
rebindCallback: contentEditingHelper.reBindChangedProperties(
contentEditingHelper.getAllProps($scope.content),
contentEditingHelper.getAllProps(data))
});
}, function (err) {
contentEditingHelper.handleSaveError(err, $scope);
var allNewProps = contentEditingHelper.getAllProps(err.data);
var allOrigProps = contentEditingHelper.getAllProps($scope.content);
contentEditingHelper.handleSaveError({
err: err,
allNewProps: allNewProps,
allOrigProps: contentEditingHelper.getAllProps($scope.content),
rebindCallback: contentEditingHelper.reBindChangedProperties(allOrigProps, allNewProps)
});
});
};
}

View File

@@ -1,8 +1,8 @@
<div>
<input name="requiredField" type="text" id="{{model.alias}}" class="umb-textstring span7 textstring"
ng-model="model.value"
required />
val-server="value"/>
<span class="help-inline" val-msg-for="requiredField" val-toggle-msg="required">Required</span>
<span class="help-inline" val-msg-for="requiredField" val-toggle-msg="valServer">{{propertyForm.requiredField.errorMsg}}</span>
</div>

View File

@@ -22,10 +22,14 @@ describe('contentEditingHelper tests', function () {
data: content,
status: 403
};
err.data.modelState = {};
err.data.ModelState = {};
//act
var handled = contentEditingHelper.handleSaveError(err, {content: content});
var handled = contentEditingHelper.handleSaveError({
err: err,
allNewProps: contentEditingHelper.getAllProps(content),
allOrigProps: contentEditingHelper.getAllProps(content)
});
//assert
expect(handled).toBe(true);
@@ -39,7 +43,11 @@ describe('contentEditingHelper tests', function () {
};
//act
var handled = contentEditingHelper.handleSaveError(err, null);
var handled = contentEditingHelper.handleSaveError({
err: err,
allNewProps: [],
allOrigProps: []
});
//assert
expect(handled).toBe(false);
@@ -55,7 +63,11 @@ describe('contentEditingHelper tests', function () {
};
//act
var handled = contentEditingHelper.handleSaveError(err, { content: content });
var handled = contentEditingHelper.handleSaveError({
err: err,
allNewProps: contentEditingHelper.getAllProps(content),
allOrigProps: contentEditingHelper.getAllProps(content)
});
//assert
expect(handled).toBe(false);
@@ -69,9 +81,10 @@ describe('contentEditingHelper tests', function () {
//arrange
var content = mocksUtils.getMockContent(1234);
var allProps = contentEditingHelper.getAllProps(content);
//act
contentEditingHelper.handleValidationErrors(content, { Name: ["Required"] });
contentEditingHelper.handleValidationErrors(allProps, { Name: ["Required"] });
//assert
expect(serverValidationManager.items.length).toBe(1);
@@ -84,9 +97,10 @@ describe('contentEditingHelper tests', function () {
//arrange
var content = mocksUtils.getMockContent(1234);
var allProps = contentEditingHelper.getAllProps(content);
//act
contentEditingHelper.handleValidationErrors(content, { "Property.bodyText": ["Required"] });
contentEditingHelper.handleValidationErrors(allProps, { "Property.bodyText": ["Required"] });
//assert
expect(serverValidationManager.items.length).toBe(1);
@@ -99,9 +113,10 @@ describe('contentEditingHelper tests', function () {
//arrange
var content = mocksUtils.getMockContent(1234);
var allProps = contentEditingHelper.getAllProps(content);
//act
contentEditingHelper.handleValidationErrors(content, { "Property.bodyText.value": ["Required"] });
contentEditingHelper.handleValidationErrors(allProps, { "Property.bodyText.value": ["Required"] });
//assert
expect(serverValidationManager.items.length).toBe(1);
@@ -114,10 +129,11 @@ describe('contentEditingHelper tests', function () {
//arrange
var content = mocksUtils.getMockContent(1234);
var allProps = contentEditingHelper.getAllProps(content);
//act
contentEditingHelper.handleValidationErrors(
content,
allProps,
{
"Name": ["Required"],
"UpdateDate": ["Invalid date"],
@@ -202,7 +218,9 @@ describe('contentEditingHelper tests', function () {
newContent.tabs[1].properties[2].value = "origValue4";
//act
var changed = contentEditingHelper.reBindChangedProperties(origContent, newContent);
var changed = contentEditingHelper.reBindChangedProperties(
contentEditingHelper.getAllProps(origContent),
contentEditingHelper.getAllProps(newContent));
//assert
expect(changed.length).toBe(2);