indexiatech / ember-forms

Smart, Intuitive forms for Ember.js styled with Bootstrap, Multi layouts and Validation support.
http://indexiatech.github.io/ember-forms
Apache License 2.0
218 stars 45 forks source link

ember-forms isn't working inside handlebar each helper #13

Open jelhan opened 10 years ago

jelhan commented 10 years ago

There seems to be a problem with property binding when using ember-forms inside a handlebar each helper.

This is working fine with Ember.TextField view:

  {{#each person in model.people}}
     {{view Ember.TextField valueBinding="person.firstName"}}
  {{/each}}

This isn't working with em-input helper:

  {{#each person in model.people}}
     {{em-input property="person.firstName" label="First Name" placeholder="Please enter first name."}}
  {{/each}}

I updated your JSBin for demonstrating: http://jsbin.com/pexolude/9/edit

ember-addons commented 10 years ago

@jelhan Thanks, will take a look at it later.

jelhan commented 10 years ago

After investigating the code a little bit I think problem is in /src/control_mixin.coffee. This mixin should bind ember-forms view to model:

        Ember.Binding.from("model.#{@get('propertyName')}").to('value').connect(@)

It expects the property to be in model context. So properties in other contextes like views or handlebars each / with helper aren't accessible.

In above context it binds the value to model.person.firstName while it should bind value to firstName property on a specific person in persons array of model.

jelhan commented 10 years ago

I researched more for finding a solution or even a work-a-round but wasn't successful. Just found out, that accessing the property by given index is working, but I have no idea how to make a work-a-round out of it:

  {{#each model.people}}
      {{#em-form model=model}}
          {{em-input property="people.0.firstName" label="First Name" placeholder="Please enter first name."}}
          {{em-input property="people.0.lastName" label="Last Name" placeholder="Please enter last name."}}
      {{/em-form}}
  {{/each}}

Handlebars each helper provides index / key but did not find any way how to use them for a work-a-round.

jelhan commented 10 years ago

Found the solution. Have to pass property of each helper as model:

  {{#each person in model.people}}
      {{#em-form model=person}}
      {{/em-form}}
  {{/each}}

Updated JSBin with this solution to show it working: http://jsbin.com/pexolude/27/edit

But this generates a new form for each element in the array. So it's not possible to have a shared submit button etc.

asaf commented 10 years ago

Cool,

Not sure if it helps but also look at: #12, I plan to add support for generating forms by json dynamically.

mblackritter commented 10 years ago

@asaf :+1:, especially if it also comes with a solution for nested model forms. :)

e247 commented 9 years ago

Hi, Just wondering if there's any news on this? I'm trying to generate forms dynamically from json but have hit this bug. I couldn't get @jelhan's solution to work. I'm happy to help in any way if anyone can point me in the right direction. Will also share the dynamic form generation code once it's working if it's of interest to anyone. Below is what I thought should work:

{{#em-form model=model action="submit"}}
   {{#each field in registerForm.fields}}
      {{em-input label=field.label property=field.name}}
   {{/each}}
{{/em-form}}
e247 commented 9 years ago

I think it's to do with manually setting up bindings based on the string passed in. As described about 3/4 of the way down this page: http://alexspeller.com/simple-forms-with-ember/

joankaradimov commented 9 years ago

I decided to dig a little inside the control mixin as @jelhan suggested. I am a complete Ember beginner and I suspect some of my "discoveries" are rather obvious. Still I decided to share them as they might be useful to someone who attempts a fix.

What I did was to modify the init function in addon/mixins/control.js to look like this:

  init: function() {
    this._super();

    var propertyIsModel = this.get('parentView.propertyIsModel');

    var formView = this;
    while (formView && formView.get('tagName') != 'form') {
      formView = formView.get('parentView');
    }
    console.log(formView);

    if(propertyIsModel) {
        return Em.Binding.from("model" + '.' + (this.get('propertyName')) + '.content').to('selection').connect(this);
    } else {
        return Em.Binding.from("model" + '.' + (this.get('propertyName'))).to('value').connect(this);
    }

  },

The console.log and the 4 lines above it are what I added. Running this with an em-input prints the form view that has the model into the JS console. Running it with em-input inside each prints null's.

It looks like the em-form is completely absent from the chain of parent views of the em-input. This is probably the reason the binding does not work.

This led me to this workaround. It does not require multiple forms and has worked for me so far:

{{#em-form model=model}}
    {{#each model.propertyNames key="id" as |propertyName|}}
        {{component "em-input" model=model property=propertyName label=propertyName}}
    {{/each}}
{{/em-form}}

Note the addition of model=model in the em-input. The model=model inside the em-form is not used in my code. I'm not sure if it can be removed in the general case, though.