When all settings require an explicit save, this functionality will be shared between each view (with slight changes, e.g. click handlers.)
106 lines
4.0 KiB
JavaScript
106 lines
4.0 KiB
JavaScript
CMS.Views.ValidatingView = Backbone.View.extend({
|
|
// Intended as an abstract class which catches validation errors on the model and
|
|
// decorates the fields. Needs wiring per class, but this initialization shows how
|
|
// either have your init call this one or copy the contents
|
|
initialize : function() {
|
|
this.listenTo(this.model, 'invalid', this.handleValidationError);
|
|
this.selectorToField = _.invert(this.fieldToSelectorMap);
|
|
},
|
|
|
|
errorTemplate : _.template('<span class="message-error"><%= message %></span>'),
|
|
|
|
events : {
|
|
"change input" : "clearValidationErrors",
|
|
"change textarea" : "clearValidationErrors"
|
|
},
|
|
fieldToSelectorMap : {
|
|
// Your subclass must populate this w/ all of the model keys and dom selectors
|
|
// which may be the subjects of validation errors
|
|
},
|
|
_cacheValidationErrors : [],
|
|
|
|
handleValidationError : function(model, error) {
|
|
// error is object w/ fields and error strings
|
|
for (var field in error) {
|
|
var ele = this.$el.find('#' + this.fieldToSelectorMap[field]);
|
|
this._cacheValidationErrors.push(ele);
|
|
this.getInputElements(ele).addClass('error');
|
|
$(ele).parent().append(this.errorTemplate({message : error[field]}));
|
|
}
|
|
},
|
|
|
|
clearValidationErrors : function() {
|
|
// error is object w/ fields and error strings
|
|
while (this._cacheValidationErrors.length > 0) {
|
|
var ele = this._cacheValidationErrors.pop();
|
|
this.getInputElements(ele).removeClass('error');
|
|
$(ele).nextAll('.message-error').remove();
|
|
}
|
|
},
|
|
|
|
saveIfChanged : function(event) {
|
|
// returns true if the value changed and was thus sent to server
|
|
var field = this.selectorToField[event.currentTarget.id];
|
|
var currentVal = this.model.get(field);
|
|
var newVal = $(event.currentTarget).val();
|
|
this.clearValidationErrors(); // curr = new if user reverts manually
|
|
if (currentVal != newVal) {
|
|
this.model.save(field, newVal);
|
|
return true;
|
|
}
|
|
else return false;
|
|
},
|
|
// these should perhaps go into a superclass but lack of event hash inheritance demotivates me
|
|
inputFocus : function(event) {
|
|
$("label[for='" + event.currentTarget.id + "']").addClass("is-focused");
|
|
},
|
|
inputUnfocus : function(event) {
|
|
$("label[for='" + event.currentTarget.id + "']").removeClass("is-focused");
|
|
},
|
|
|
|
getInputElements: function(ele) {
|
|
var inputElements = 'input, textarea';
|
|
if ($(ele).is(inputElements)) {
|
|
return $(ele);
|
|
}
|
|
else {
|
|
// put error on the contained inputs
|
|
return $(ele).find(inputElements);
|
|
}
|
|
},
|
|
|
|
showNotificationBar: function(message, primaryClick, secondaryClick) {
|
|
if(this.notificationBarShowing) {
|
|
return;
|
|
}
|
|
var self = this;
|
|
this.confirmation = new CMS.Views.Notification.Warning({
|
|
title: gettext("You've made some changes"),
|
|
message: message,
|
|
actions: {
|
|
primary: {
|
|
"text": gettext("Save Changes"),
|
|
"class": "action-save",
|
|
"click": function() {
|
|
primaryClick();
|
|
self.confirmation.hide();
|
|
self.notificationBarShowing = false;
|
|
}
|
|
},
|
|
secondary: [{
|
|
"text": gettext("Cancel"),
|
|
"class": "action-cancel",
|
|
"click": function() {
|
|
if(secondaryClick) {
|
|
secondaryClick();
|
|
}
|
|
self.confirmation.hide();
|
|
self.notificationBarShowing = false;
|
|
}
|
|
}]
|
|
}});
|
|
this.notificationBarShowing = true;
|
|
this.confirmation.show();
|
|
}
|
|
});
|