diff --git a/client/src/js/ng-django-forms.js b/client/src/js/ng-django-forms.js
index 5900413..7b2d9e1 100644
--- a/client/src/js/ng-django-forms.js
+++ b/client/src/js/ng-django-forms.js
@@ -55,33 +55,33 @@ djng_forms_module.directive('djngError', function() {
// This directive overrides some of the internal behavior on forms if used together with AngularJS.
// Otherwise, the content of bound forms is not displayed, because AngularJS does not know about
// the concept of bound forms and thus hides values preset by Django while rendering HTML.
-djng_forms_module.directive('ngModel', function() {
- function restoreInputField(modelCtrl, field) {
+djng_forms_module.directive('ngModel', function($parse) {
+ function restoreInputField(field) {
// restore the field's content from the rendered content of bound fields
switch (field.type) {
case 'radio':
if (field.defaultChecked) {
- modelCtrl.$setViewValue(field.defaultValue);
+ return field.defaultValue;
}
break;
case 'checkbox':
if (field.defaultChecked) {
- modelCtrl.$setViewValue(true);
+ return true;
}
break;
case 'password':
// after an (un)successful submission, reset the password field
- modelCtrl.$setViewValue(null);
+ return null;
break;
default:
- if (field.defaultValue) {
- modelCtrl.$setViewValue(field.defaultValue);
+ if(field.defaultValue) {
+ return field.defaultValue;
}
break;
}
}
- function restoreSelectOptions(modelCtrl, field) {
+ function restoreSelectOptions(field) {
var multivalues = [];
angular.forEach(field.options, function(option) {
if (option.defaultSelected) {
@@ -90,19 +90,70 @@ djng_forms_module.directive('ngModel', function() {
if (field.multiple) {
multivalues.push(option.value);
} else {
- modelCtrl.$setViewValue(option.value);
+ return option.value;
}
}
});
if (field.multiple) {
- modelCtrl.$setViewValue(multivalues);
+ return multivalues;
}
}
- function restoreTextArea(modelCtrl, field) {
- if (field.defaultValue) {
- // restore the field's content from the rendered content of bound fields
- modelCtrl.$setViewValue(field.defaultValue);
+ function restoreTextArea(field) {
+ // restore the field's content from the rendered content of bound fields
+ if(field.defaultValue) {
+ return field.defaultValue;
+ }
+ }
+
+ /**
+ * @private
+ *
+ * If a default value for an input exists (either set as an initial or bound value by django),
+ * then the value assigned to the ng-model attibute, is used to set that default
+ * model value on scope.
+ *
+ *
+ *
+ * var parts = ['data', 'first_name'],
+ * prop = 'first_name',
+ * modelName = 'data', \\ this could be a multi namespaced object or nothing if the prop is set direct on scope
+ * fn = [ Function ],
+ * model;
+ *
+ * @param {object} scope The scope of the ngModel
+ * @param {string} ngModelName The value assigned to the ng-model attribute. Not ngModel.$name
+ * @param {object} value The initial value of the input
+ */
+ function processDefaultValue(scope, ngModelName, value) {
+
+ if(!angular.isDefined(value))
+ return;
+
+ var parts = ngModelName.split('.'),
+ prop = parts.pop(),
+ modelName = parts.join('.'),
+ fn = $parse(model !== '' ? model : prop),
+ model;
+
+ /*
+ * We have a namespace for the model i.e 'data'
+ */
+ if(modelName !== '') {
+
+ fn = $parse(modelName); // generate safe function to retrieve model from scope
+ model = fn(scope) || {}; // if model already exists (default or previous values may have been set on scope) then use it. If not, create a new object
+ model[prop] = value; // apply the default value to the model
+ fn.assign(scope, model); // set the model back on scope
+
+ /*
+ * the property is being assigned directly to scope, not 'hung' off another
+ * namespace object. No need to retrieve as we can overwrite any existing value.
+ */
+ }else{
+
+ fn = $parse(prop);
+ fn.assign(scope, value);
}
}
@@ -116,22 +167,25 @@ djng_forms_module.directive('ngModel', function() {
var modelCtrl = ctrls[0], formCtrl = ctrls[1] || null;
if (!field || !formCtrl)
return;
+
+ var defaultValue;
+
switch (field.tagName) {
case 'INPUT':
- restoreInputField(modelCtrl, field);
+ defaultValue = restoreInputField(field);
break;
case 'SELECT':
- restoreSelectOptions(modelCtrl, field);
+ defaultValue = restoreSelectOptions(field);
break;
case 'TEXTAREA':
- restoreTextArea(modelCtrl, field);
+ defaultValue = restoreTextArea(field);
break;
default:
console.log('Unknown field type');
break;
}
- // restore the form's pristine state
- formCtrl.$setPristine();
+
+ processDefaultValue(scope, attrs.ngModel, defaultValue);
}
};
});
diff --git a/examples/server/forms/combined_validation.py b/examples/server/forms/combined_validation.py
index b55b859..80e3457 100644
--- a/examples/server/forms/combined_validation.py
+++ b/examples/server/forms/combined_validation.py
@@ -9,8 +9,10 @@
class SubscribeForm(NgModelFormMixin, NgFormValidationMixin, subscribe_form.SubscribeForm):
scope_prefix = 'subscribe_data'
form_name = 'my_form'
-
+
def clean(self):
if self.cleaned_data.get('first_name') == 'John' and self.cleaned_data.get('last_name') == 'Doe':
raise ValidationError('The full name "John Doe" is rejected by the server.')
return super(SubscribeForm, self).clean()
+
+
diff --git a/examples/server/forms/subscribe_form.py b/examples/server/forms/subscribe_form.py
index 1cfafb7..b86ae5d 100644
--- a/examples/server/forms/subscribe_form.py
+++ b/examples/server/forms/subscribe_form.py
@@ -54,3 +54,9 @@ class SubscribeForm(Bootstrap3Form):
help_text='The password is "secret"')
confirmation_key = forms.CharField(max_length=40, required=True, widget=forms.HiddenInput(),
initial='hidden value')
+
+
+ def get_widget_attrs(self, bound_field):
+ attrs = super(SubscribeForm, self).get_widget_attrs(bound_field)
+ attrs['ng-model-options'] = "{ debounce : { 'default' : 500, blur : 0 } }"
+ return attrs
diff --git a/examples/server/views/classic_subscribe.py b/examples/server/views/classic_subscribe.py
index 87e101b..e262a30 100644
--- a/examples/server/views/classic_subscribe.py
+++ b/examples/server/views/classic_subscribe.py
@@ -9,3 +9,6 @@ class SubscribeView(FormView):
template_name = 'subscribe-form.html'
form_class = SubscribeForm
success_url = reverse_lazy('form_data_valid')
+
+ def get_initial(self):
+ return {'first_name': 'james'}
diff --git a/examples/server/views/client_validation.py b/examples/server/views/client_validation.py
index 3c9985c..05cc551 100644
--- a/examples/server/views/client_validation.py
+++ b/examples/server/views/client_validation.py
@@ -9,3 +9,6 @@ class SubscribeView(FormView):
template_name = 'client-validation.html'
form_class = SubscribeForm
success_url = reverse_lazy('form_data_valid')
+
+ def get_initial(self):
+ return {'first_name': 'james'}
diff --git a/examples/server/views/combined_validation.py b/examples/server/views/combined_validation.py
index 8bed410..86c7c26 100644
--- a/examples/server/views/combined_validation.py
+++ b/examples/server/views/combined_validation.py
@@ -13,6 +13,9 @@ class SubscribeView(FormView):
form_class = SubscribeForm
success_url = reverse_lazy('form_data_valid')
+ def get_initial(self):
+ return {'first_name': 'james'}
+
def post(self, request, **kwargs):
if request.is_ajax():
return self.ajax(request)
diff --git a/examples/server/views/model_scope.py b/examples/server/views/model_scope.py
index a3e0b4a..52979b5 100644
--- a/examples/server/views/model_scope.py
+++ b/examples/server/views/model_scope.py
@@ -13,6 +13,9 @@ class SubscribeView(FormView):
form_class = SubscribeForm
success_url = reverse_lazy('form_data_valid')
+ def get_initial(self):
+ return {'first_name': 'james'}
+
def post(self, request, **kwargs):
if request.is_ajax():
return self.ajax(request)
diff --git a/examples/test.sqlite b/examples/test.sqlite
new file mode 100644
index 0000000..ace7e9f
Binary files /dev/null and b/examples/test.sqlite differ