Backbone, Marionette — simple form validation with Backbone Model, Marionette’s regions and child views

Radek Anuszewski
Get Noticed! 2017 [Radek Anuszewski]
4 min readMar 30, 2017
This post is a part of Get Noticed! 2017 contest by Maciej Aniserowicz

Form validation for Backbone/Marionette can be tricky for person from outside this world, but with regions and child views it can be done from scratch without too much effort

Own solution instead of plugin

It’s easy to find that Backbone and Marionette have plugins for validation: thedersen/backbone.validation, powmedia/backbone-forms or not-only-code/marionette-forms, to name a few. But form validation, especially when form is simple (like mine, only 2 fields; login and password) is rather easy thing to do. Also, for beginner like me, this is a great opportunity to dive a little deeper into libraries ecosystem.

Idea for validation

Validation is pretty simple — when form is submitted, check if login has at least 5 characters and check if password has at least 8 characters. When this conditions are met, send request — otherwise, display information below field. Also, when user corrects data, hide error messages.

Validation on Backbone model

Backbone model provides validate method, which looks weird at first glance (especially that it does not have default implementation and is undefined if you don’t override it). You are absolutely free in how you implement your validation, to indicate that model is valid you should not return anything (or return undefined, it means exactly the same for JavaScript). But, when you want to mark model as invalid, you should return… Whatever you want. Except undefined, of course. And result of return is bound to validationError property on model.
Why is it done like this?
Cause you can do whatever you want with validationError, and depending on your needs, you can return “Something went wrong” and display an alert or return complex object and display very detailed information about what went wrong, like people used to with Angular validation. I choose the latter — but in very simplified version (validate method from Login model, simplified for brevity):

validate: function (attrs, options) {
const validationErrors = {};
if (/*login is too short*/) {
validationErrors.login = {
error: `min`,
message: `Login should contain at least ${this.loginMinLength} characters`,
};
}
if (/*password is too short*/) {
validationErrors.password = {
error: `min`,
message: `Password should contain at least ${this.passwordMinLength} characters`,
};
}
return (validationErrors.login || validationErrors.password) ? validationErrors : undefined;
},

Returned object will be used to display messages below input. If you look closer you will find ${(…)} fragments — it is Template String from ES 6. It significantly simplifies creating strings based on dynamic content.
Unfortunately, I had to inject minimum lengths to model itself:

model: new LoginModel({
loginMinLength: 5,
passwordMinLength: 8,

}),

Despite it’s done with great ES 6 destructuring feature, I don’t think is a good idea, but I can’t figure out how to do it better. If you know cleaner solution, do not hesitate to help me and leave a comment!

Displaying validation messages in Marionette Region with Child View

Marionette’s regions are places where you can display child views, they can be added at runtime — and also they can be removed. I created 2 regions, placed below input in HTML:

<div>
<label for="login"></label>
<input type="text" id="login">
<p id="login-invalid"></p>
</div>
<div>
<label for="password"></label>
<input type="password" id="password">
<p id="password-invalid"></p>
</div>

Declaration in View:

regions: {
loginInvalid: "#login-invalid",
passwordInvalid: "#password-invalid",
},

But when they should be shown or removed?

Backbone model events in Marionette view

Marionette provides modelEvents property, where as key you should provide event type with colon and property name (like keyup:name, where keyup is keyup input event and name is property name on model), and as value you should provide function name, called when event occurs. Function has to be present on View, otherwise you will get an error.

Clearing error message when model changes and change event is emitted

On 2 Backbone model change event:

modelEvents: {
"change:login": "onLoginModelChange",
"change:password": "onPasswordModelChange",
"invalid": "onValidationFailed",
},

Respective regions are cleared:

// Same for password model property change
onLoginModelChange: function () {
this.detachChildView("loginInvalid");
},

Cause I made an assumption that when user see an error and start typing, it could be annoying to see error message.
But what about “invalid”: “onValidationFailed”? When invalid event is emitted?

Showing error messages when model validation fails

As documentation says, validation can be triggered when you try to persist your model. But there is a caveat — save() returned false for invalid model which broke promise chain:

this.model.save().then...

And error was thrown with information that false des not have then method. So I did a workaround — I call isValid method manually, and it triggers invalid event:

if (this.model.isValid()) {
this.model.save().then(_onLoginRequest.bind(this));
}

When model is not valid, invalid event is emitted and validationError property is set on model, with value returned from validate Backbone model method:

onValidationFailed: function () {
const errors = this.model.validationError;
if (errors.login) {
this.showChildView("loginInvalid", new ErrorView({
template: _.template(errors.login.message),
}));
}
if (errors.password) {
this.showChildView("passwordInvalid", new ErrorView({
template: _.template(errors.password.message),
}));
}
},

And child views are shown in respective regions. ErrorView is view without template:

const ErrorView = Mn.View.extend({
})

Because its template is just a message from error. I found something called Behaviors in Marionette, it could be better way to solve it — and it is at least worth to dive into.

Conclusion

With every touch, every single post I feel more excited about adventure with Backbone / Marionette. It’s not easy, cause in my 9–5 I use Angular, so for me it’s more low-level, but any time spent with mentioned libraries is definitely worth it!

--

--

Radek Anuszewski
Get Noticed! 2017 [Radek Anuszewski]

Software developer, frontend developer in AltConnect.pl, mostly playing with ReactJS, AngularJS and, recently, BackboneJS / MarionetteJS.