{% extends "model-scope.html" %} {% load tutorial_tags %} {% block main-intro %}
Combine client-side form validation with server-side form validation
New in version 2.0
Here we drop support for application/x-www-form-urlencoded
submissions and combine
the validation from the two previous examples. This presumably is the best solution, when
integrating Django forms with AngularJS.
{% endblock %} {% block main-tutorial %}
If a Form inherits from both mixin classes NgModelFormMixin
and
NgFormValidationMixin
, then django-angular combines
client-side validation with AngularJS's
model scope.
Refer to the previous examples for a detailed explanation.
In the previous example we have seen, that we can chain asynchronous actions on the button's
<button ng-click="on(…)" type="button" …>
On success a promise calls the function passed into the …then(…)
-clause. By
chaining these clauses, we can invoke actions sequentially, each one after the previous action
has finished.
This can be used to build a chain of independent actions for each button. If an action fails, the
following action inside the …then(…)
is skipped in favour of the function inside the
next …catch(…)
call. If an action shall be executed regardless of its previous
success or failure state, use it inside a …finally(…)
-clause.
<form djng-endpoint="…" …>
By adding ng-disabled="isDisabled()"
In order to chain our actions, we have to start with a promise-clause, which always resolves.
This is why we always have to start our first action such as:
ng-click="do(first_action).then(…)…"
Forms with input elements bound to the scope, normally use a directive with such a pattern:
ng-model="scope_prefix.field_name"
fetch()
, create()
, update()
or delete()
,
we send that subset of data to the server, using the HTTP methods GET, POST, PUT or DELETE
respectively.
Forms sometimes extend over more than one screen height. If a form validation fails, the message
near a rejected field may be outside the visible area. To improve the user experience, it therefore
is good practice to point the user to the field(s), which have been rejected. This can by achieved
by adding a target such as ng-click="do(…).then(…).catch(scrollToRejected())
Specially after a successful login- or logout submission, we might want to reload the current
page, in order to reset the cookie value and/or session states. For this purpose, use an action
such as: ng-click="do(upload()).then(reloadPage())"
To proceed to another page after a successful submission, use an action such as:
ng-click="do(upload()).then(redirectTo('/path/to/view'))"
{"success_url": "/path/to/other/view"}
redirectTo()
is overridden.
Sometimes we might want to delay a further action. If for instance we want to add a 500
miliseconds delay after a successful submission, we then would rewrite our action such as:
ng-click="do(upload()).then(delay(500)).then(reloadPage())"
To improve the user's experience, it is a good idea to give feedback on an action, which
succeeded or failed. Our button directive offers two such functions, one to display an OK tick
on success, and one to display a cross
to symbolize a failed operation. These symbols
replace the buttons <i class="fontawesome or glyphicon"></i>
By using the promises chain, we can easily integrate this into our actions flow:
ng-click="do(update()).then(showOK()).then(delay(500)).then(reloadPage()).catch(showFail()).then(delay(2000)).finally(restore())"
catch(…)
-clause, to run a different action function in case of
a failed submission. The finally(restore())
is executed regardless of the submission
success or failure, it restores the button internal icon to its original state.
Sometimes processing form data can take additional time. To improve the user experience, we
can add some feedback to the submission button. By changing the submit action to
ng-click="do(disable()).then(update()).then(redirectTo()).finally(restore())"
,
In case of potentially long lasting submissions this can be further extended, by replacing
the button's internal icon with a rotating spinner wheel
. To do so, just replace the
disable()
function against spinner()
.
Sometimes we might want to use more than one submit button. In order to distinguish which of
those buttons has been pressed, pass an object to the form submission function, for instance
ng-click="do(update({foo: 'bar'}))"
By adding ng-click="do(update()).then(emit('name', {'foo': 'bar'}))"
my_form
, then sending an object, such
as {"my_form": {"fieldname1": "value1", "fieldname2": "value2", "fieldname3": "value3"}}
,
in the response's payload, will set the named form fields with the given values.
{% endblock main-tutorial %}
{% block main-sample-code %}
{% autoescape off %}
This configuration is the most flexible one. Use it on productive web-sites.
Note: The submit buttons are disabled, until the client-side Form validation has
validated all the fields.