gcanti / tcomb-form-native

Forms library for react-native
MIT License
3.15k stars 458 forks source link

Custom Template issue during validation #289

Open vivekmago opened 7 years ago

vivekmago commented 7 years ago

Version

Tell us which versions you are using:

Expected behaviour

Am trying to display a form with a list view using custom templates but the list items are Text and not TextInputs. If I could turn validation off for fields or maybe am trying to abuse the control. Please let me know if I need to rethink my use.

Actual behaviour

The list appears as expected and the form behaves perfectly but when I try to save the list it fails in form validation as there is nothing to validate as it is just a list of labels.

Steps to reproduce

Code to repro

var options = {
  auto: 'placeholders',
  fields: {
    name : {
      error: 'Enter your name'
    },    
    email : {
      error: 'Enter valid email address'
    },
    phone : {
      error: 'Enter valid phone number'
    },
    photoURL : {
      hidden : true
    },
    locations: {
      template: ListTemplate,
      disableOrder: true,
      disableAdd: true,
      disableRemove: true,
    }
  }
};

function ListTemplate(locals) {
  if (!_.isEmpty(locals.value)) {
    return (
      <View>
          <Text style={{fontSize:17}}>Registered Locations</Text>
          <ListView
            style={{marginBottom:10}}
            enableEmptySections={true}
            dataSource={
              new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2})
              .cloneWithRows(locals.value)
            }
            renderRow={(rowData) => <Text style={{fontWeight:'300',padding:5}} >{rowData}</Text>}
          />
      </View>
    );    
  }
  return null;
}

var Person = t.struct({
  name: t.String,              // a required string
  phone: Phone,
  photoURL: t.maybe(t.String),
  email: t.maybe(Email),  // an optional string
  locations: t.maybe(t.list(t.String)),
});

Stack trace and console log

Hint: it would help a lot if you enable the debugger ("Pause on exceptions" in the "Source" panel of Chrome dev tools) and spot the place where the error is thrown

Stack error undefined is not an object (evaluating 'this.refs[i].validate'

validate method in component.js line # 594.

anrosent commented 7 years ago

@vivekmago I hit this issue when implementing a custom Factory, which I resolved by refactoring it to be a class instead of a stateless function component, which can't have refs. Doing the same to ListTemplate might do the trick?

seanmadi commented 7 years ago

Let me know if you figure this out. Having the same problem.

jamesholcomb commented 7 years ago

Not sure if this is related, but I encountered this error using a mobx store injector on a factory component:

@inject("userStore")
class MyTemplate extends t.form.Struct {
  static propTypes = {
    userStore: React.PropTypes.object.isRequired,
    options: React.PropTypes.shape({
      start: React.PropTypes.object.isRequired
    })
  }
}

When debugging, refs showed that the MyTemplate instance was an Injector instead of MyTemplate

If I remove the mobx decorator, no exception.

alexcouret commented 7 years ago

Hello,

I'm having the same issue, in this for loop :

for (let i = 0, len = this.state.value.length; i < len; i++ ) {
      result = this.refs[i].validate();
      errors = errors.concat(result.errors);
      value.push(result.value);
}

In this loop, I have this.refs being an Object {}, and this.state.value being my array of items, which is for instance [{checked: false, label: 'test'}]. So this.refs[i] is indeed undefined.

From what I saw by logging without the template, this.refs should be like {0: Struct}.

I built the list like this :

const schema = {
           ...
            taskList: t.list(t.struct({
                checked: t.Boolean,
                label: t.String,
            })),
           ...
        };

It works with the default template, but not with my custom template :

       ...
        <View>
            {
                this.state.value.taskList.map((item, i) => (
                    <View
                        key={`task-${i}`}
                        style={{flexDirection: 'row', marginBottom: 10}}>
                        <TextInput
                            style={{
                                borderBottomWidth: 1,
                                flex: 3,
                            }}
                            onChangeText={(text) => this.updateTaskItem(i, text)}
                            value={item.label}
                        />
                        <Switch
                            style={{flex: 1, marginLeft: 10}}
                            onValueChange={(value) => this.updateTaskItem(i, value)}
                            value={item.checked}
                        />
                        <Button
                            style={{flex: 1}}
                            color={COLOR.darkOrange}
                            text={'x'}
                            height={30}
                            width={30}
                            onPress={() => this.updateTaskList(i)}
                        />
                    </View>
                ))
            }
        </View>
        ...

I'm not so sure what's wrong, since I didn't have any issue using custom templates for dates for instance.

Thanks.