jrief / django-angular

Let AngularJS play well with Django
http://django-angular.awesto.com/
MIT License
1.23k stars 293 forks source link

Feature Request: better support for inline formsets #78

Open AgDude opened 10 years ago

AgDude commented 10 years ago

I am currently working on implementing inline formsets locally. Has anyone else used django-angular with inline formsets.

What I have in mind is putting the inline forms into an array, so I can use ng-repeat as I add or remove forms.

so the scope_prefix object would look something like this:

{ field1 : ,
field2: ,
child_model: [
  {childfield1, 
  childfield2},
  {childfield1, 
  childfield2},
...
]}

The other issue to address is putting ng-model on the django management forms, so those can be manipulated as forms are added/removed.

I am opening this issue as a place to keep track of how to implement this, if my workload permits I will update the demo and docs when I get it working. Any feedback or ideas are greatly appreciated.

AgDude commented 10 years ago

I have added ng-model to the management form by modifying it in the as_ul method on the formset class. This feels like a bit of hack, but since the management form is dynamically generated in a property method it is not easy to override.

AngularFormSet(forms.models.BaseInlineFormSet):

    def as_ul(self):
        forms = ' '.join([form.as_ul() for form in self])
        management_form = self.management_form
        for name, field in management_form.fields.iteritems():
            field.widget.attrs['ng-model'] = '%s.%s' % (self.prefix, name)
        return mark_safe('\n'.join([six.text_type(management_form), forms]))
AgDude commented 10 years ago

Pushing an unknown number of forms into the array is going to be problematic. Clearly we will need to initialize the array in the controller, but it can't be empty. When we put an ng-model property like this:

ng-model = "form_data.children[0].childfield1"

form_data.children[0] is undefined, so we can't set childfield1. What we really need is an empty object. Basically we need to subclass Array so that get returns an empty object if it doesn't exist. There is an excellent discussion of subclassing Array at the link below. It is doable, but not a trivial task. http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/

For my application I am going to set the max number of forms at 20, and initialize the array like so:

Array.apply(null, new Array(20)).map(function(){return {}})

This is going to work just fine for my use case, but doesn't seem robust enough to put into django-angular.

Edit: on further consideration there are some real drawback to this approach. First, children.length will be meaningless, since it will always be 20. ng-repeat is going to get a bunch of empty objects.

I think I'll put something in the controller to remove the empty objects after the page loads. Obviously far from ideal.

jrief commented 10 years ago

The Zen of this project is to do stuff such as form rendering the “Djagno”-way. First, when I started, I was proud to not have any Javascript code at all. Well, these times are gone, but JS still takes a small footprint of the project. I therefore prefer to use Django templates for rendering, etc. This does not mean to abandon Javascript completely, but if you can choose between doing it on the server or doing it on the client, I prefer the server. I know, this is in stark contrast to the REST philosophy, but that's my intention.

Since I'am short of time, I'll come back to this in a month or so. Jacob

AgDude commented 10 years ago

I think that Zen is one of the things I like about this project, myself having been acquainted with Django much longer than Angular. I'll keep that in mind as I am working on my implementation.

One of my requirements is to be able to add/remove inline forms on the client, so some javascript is going to be needed.

vaibhav-jain commented 9 years ago

I have a InlineFormset that I want to validate . Can this plugin do that.???

jrief commented 9 years ago

AngularJS doesn't know about the concept of formsets. In AngularJS the form validation is done using the $scope.formname.fieldname. If you manage to get the same names for the fields, form validation should also work. Let me know fi you can figure it out yourself. As a hint: the unit tests run nested forms, so please check there too.

vaibhav-jain commented 9 years ago

Thanks I will try. Right now I am trying to integrate bootstrap formvalidation plugin. But due to different names it is now working.

craftycorvid commented 8 years ago

I solved the Management Form issue in what I believe is a more elegant way, using a template tag to render it.

@register.simple_tag
def djng_formset_management_form(formset):
    management_form = formset.management_form
    scope_prefix = formset.form.scope_prefix
    for name, field in management_form.fields.iteritems():
        field.widget.attrs['ng-model'] = ("%s['%s-%s']" % (scope_prefix, formset.prefix, name)) if scope_prefix else \
            '%s-%s' % (formset.prefix, name)
    return management_form
craftycorvid commented 8 years ago

There's one final issue that I haven't seen solved in any of these tickets regarding formsets. add_prefix in NgFormBaseMixin changes dots to dashes, which works fine for regular forms because that same add_prefix is called during validation. This is not true during formset validation, however.

_construct_form in django.forms.models.BaseModelFormSet has the dash hard-coded. Is this a bug in django?

I'm going to work around it for now, but this would have to be addressed if this will someday be supported in django-angular.

adrienbrunet commented 7 years ago

@Ivan0xFF Would you like to contribute and add your work to django-angular? Better support and documentation is one of the last thing lacking for this module. (Beside Angular2 which is another subject)