Erros de formulário em visualizações Chaplin JS

Ao criar visualizações de formulário para modelos em um aplicativo Chaplin JS , é uma boa ideia aproveitar o poder do padrão Mediator Pub / Sub de Chaplin para o envio de seu formulário para renderizar erros de validação e servidor em suas visualizações de formulário.

Digamos que temos um modelo Party :

class Party extends Model
url
: '/parties.json'
defaults
:
ladies
: 0
fellas
: 4
celebrities
: 0

validate
: (attrs, options) ->
if attrs.ladies / attrs.fellas < 1
"Sausage fest"

e sua visualização de “edição” correspondente:

class PartyEditView extends View
templateName
: 'parties/form'

autoRender
: true
events
:
"submit form": 'saveParty'

saveParty
: (e) ->
e
.preventDefault()
# Some method to get model attributes from view values goes here
@model.save(@model.attributes)

Então, digamos que nosso partido não passe na validação. Provavelmente queremos saber sobre isso, certo? Direito. Vamos primeiro fazer um modelo de erro simples e uma visão para ele:

class Error extends Model
defaults
:
text
: "Something went wrong"

class ErrorView extends View
templateName
: 'error'
autoRender
: true
autoAttach
: true

Portanto, nosso PartyEditView deve fazer algo com esse lixo de erro quando algo der errado:

class PartyEditView extends View
templateName
: 'parties/form'

autoRender
: true
events
:
"submit form": 'saveParty'
regions
:
'.errors': 'errors'

initialize
: ->
super
@error = false

# When the Party is invalid, do something:
@model.on "invalid", (model, error) =>
@renderError(error)

saveParty
: (e) ->
e
.preventDefault()
@error.dispose() if @error
# Some method to get model attributes from view values goes here
@model.save(@model.attributes)

@renderError: (error) ->
model
= new Error({text: error})
@error = new ErrorView model: model, region: 'errors'

Portanto, agora, quando tentarmos salvar o modelo, o invalidevento será acionado . Vinculamos um ouvinte a invalidpara disparar o renderErrormétodo, o que cria um novo Errormodelo e uma visão para ele. A visão renderiza e se anexa automaticamente à errorsregião, que definimos no regionsobjeto da visão . Ótimo.

Mas e se ele passar em nossa validação de front-end e o servidor achar que ainda é uma festa muito chata? Precisamos de uma maneira de processar os erros que o servidor retorna.

Em primeiro lugar, vamos dar uma olhada em nosso PartyEditView. O savePartymétodo parece suspeito. Por que uma View se preocupa com a persistência do modelo? Devíamos mudar para outro lugar. O controlador parece um bom lugar para esse tipo de comportamento.@model.save

class PartiesController extends Controller
save
: (model) ->
@model.save model.attributes

Mas como o controlador sabe quando deve salvar o modelo? O serviço Pub / Sub de Chaplin é extremamente útil em casos como este. Vamos fazer com que o view publique uma mensagem “save_party” que o controlador ouvirá.

class PartyEditView extends View
saveParty
: (e) ->
e
.preventDefault()
@error.dispose() if @error

# publish the save_party message and pass the model along
@publishEvent 'save_party', @model
class PartiesController extends Controller
initialize
: ->
super

# Listen for word about any cool parties and fire the save method
@subscribeEvent 'save_party', @save

save
: (model) ->
@model.save model.attributes,
success
: (model,response) =>
alert
'Party was saved!'
@redirectTo '/'
error
: (model, response) =>
# Fire the view's renderError method and pass it the server response
@view.renderError(response)

Normalmente, a resposta do servidor será um objeto, portanto, precisaremos modificar renderErrorum pouco o método de nossa visualização para extrair algum texto útil para exibição. Eu tenho meu controlador Rails configurado para entregar full_messages na resposta:

render :json => @party.errors.full_messages, status: 422

Portanto, a fim de mostrar texto relevante para o usuário, vamos fazer algo parecido com isso em nosso PartyEditView‘s renderErrormétodo:

renderError: (error) ->
@error.dispose() if @error

if typeof error is 'object'
# Extract text from each error in server response
for error in $.parseJSON(error.responseText)
errorObj
=
text
: error
model
= new Error(errorObj)
@error = new ErrorView model: model, region: 'errors'
else
# Display validation error, which is just a string
errorObj
=
text
: error
model
= new Error(errorObj)
@error = new ErrorView model: model, region: 'errors'

Aí está. Validação de renderização automática e erros de servidor em sua visualização. O ideal é que você pegue essa funcionalidade e a inclua em um modelo de vista de base que pode ser estendido:

class ModelView extends View
initialize
: ->
super
@error = false
@registerRegion '.form-errors', 'errors'
@subscribeEvent 'renderError', @renderError
@model.on "invalid", (model, error) =>
@renderError(error)

renderError
: (error) ->
@error.dispose() if @error
if typeof error is 'object'
for error in $.parseJSON(error.responseText)
errorObj
=
text
: error
model
= new Error(errorObj)
@error = new ErrorView model: model, region: 'errors'
else
errorObj
=
text
: error
model
= new Error(errorObj)
@error = new ErrorView model: model, region: 'errors'

Em seguida, você pode ter erro de renderização em qualquer visualização que desejar, usando o @registerRegionmétodo para direcionar um contêiner para renderizar as visualizações em:

class PartyEditView extends ModelView
templateName
: 'party/form'
autoRender
: true
events
:
"submit form": 'saveParty'
initialize
: ->
super
@registerRegion '.party-errors', 'errors'
saveParty
: (e) ->
e
.preventDefault()
@error.dispose() if @error
@publishEvent 'save_party', @model

class NoiseViolationEditView extends ModelView
templateName
: 'violation/form'
events
:
"submit form": 'saveViolation'
initialize
: ->
super
@registerRegion '.legal-errors', 'errors'
saveViolation
: (e) ->
e
.preventDefault()
@error.dispose() if @error
@publishEvent 'save_violation', @model