Skip to content

Instantly share code, notes, and snippets.

@zkwentz
Last active October 5, 2015 00:00
Show Gist options
  • Save zkwentz/b56338ec62440b8f15dc to your computer and use it in GitHub Desktop.
Save zkwentz/b56338ec62440b8f15dc to your computer and use it in GitHub Desktop.
Ember validations, defined on a model, server-side compatible

Problem

Ember-validations, was built to define the validations outside the model, and if you define them on your model, it will cause server-side errors to no longer work correctly (still unsure why).

Solution

In this example, we pull the validations off the model, and attach them to our error-wrapper component.

The solution outlined here allows gives you three benefits:

  • Validations are in one place, the model.
  • Errors can be added to any input element with relative ease.
  • Server-side validations are unaffected.

Demo

`import Ember from 'ember'`
`import EmberValidations from 'ember-validations'`
ErrorWrapperComponent = Ember.Component.extend(
EmberValidations,
model: null
shouldShowError: false
propertyName: null
immediate: false
init: ->
validationsKey = "model.validations.#{@get('property')}"
validations = @get(validationsKey)
if !!validations
validations = {}
validations["model.#{@get('property')}"] = @get(validationsKey)
@set('validations',validations) unless validations is {}
error = Ember.computed("errors.model.#{@get('property')}.@each",=>
@get("errors.model.#{@get('property')}")?.get('firstObject')
)
@set('error',error)
@_super()
hasErrors: (->
!!@get('error')
).property('error')
localizedProperty: (->
model = @get('model')
if !!model
modelName = model._internalModel.modelName.underscore()
"#{modelName}.#{@get('property').underscore()}"
).property('property','model')
showError: (->
if @get('hasErrors')
@get('immediate') or @get('shouldShowError')
).property('immediate','hasErrors','shouldShowError')
actions:
checkForErrors: (event) ->
@_super(event)
@set('shouldShowError',true)
)
`export default ErrorWrapperComponent`
div class={hasErrors:has-errors showError:show-error}
== yield (action 'checkForErrors')
if error
if propertyName
span.error-message #{propertyName} #{error}
else
span.error-message #{t localizedProperty} #{error}
`import DS from 'ember-data'`
Post = DS.Model.extend(
title: DS.attr('string')
body: DS.attr('string')
comments: DS.hasMany('comment',async:true)
user: DS.belongsTo('user',async:true)
validations:
user:
belongsTo:
message: "cannot be blank."
title:
presence: true
body:
presence: true
)
`export default Post`
...
= error-wrapper model=model property="title" as |checkForErrors|
= input value=model.title name="title" placeholder="Post title" focus-out=checkForErrors type="text"
= error-wrapper model=model property="body" as |checkForErrors|
= textarea value=model.body name="body"
= error-wrapper model=model property="user" as |checkForErrors|
= ember-selectize select-item=checkForErrors selection=model.user content=allUsers optionLabelPath="content.userName" optionValuePath="content.id" value=model.user.id
...
`import PresenceValidator from 'ember-validations/validators/local/presence'`
BelongsToValidator = PresenceValidator.extend(
call: ->
if Ember.isEmpty(@model.get(@property).get('content'))
@errors.pushObject(@options.message)
)
`export default BelongsToValidator`

belongsTo validator

Special note, one of the validators I'm using in this example, is a belongsTo which doesn't come with ember-validations addon out of the box.

I included it as a demo, so that you can see that it would work with a select box, just as easily as a normal text input.

I've included the source to the belongs to validator above.

error-wrapper

A short brief on the usage of the error-wrapper component.

One thing that immediately sticks out is the funny template, that yields but includes that (action 'checkForErrors') bit. It's a bit of a hack, but basically allows you to yield to the component on the page, and allow whatever you yield to trigger the checkForErrors action in the component itself.

Hence the as |checkForErrors| on the template for post. That as is the action, that says, show the error. Which is why we add it to select-item and focus-out on the input elements.

Options:

immediate

default: false

If you use this, it will immediately show the errors, rather than waiting for the checkForErrors action to be run.

propertyName

default: null

If you set this, it will override the derived property name from that you passed in, and use this for the error message instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment