React.js how to call method on child component through refs? - coffeescript

I have a CustomerForm component like so:
#CustomerForm = React.createClass
getInitialState: ->
name: ''
street: ''
location: ''
addendum: ''
render: ->
React.DOM.form
className: 'ui form'
React.DOM.div
className: 'field'
React.DOM.label null, 'Name'
React.DOM.input
type: 'text'
name: 'name'
value: #state.name
onChange: #handleChange
React.DOM.div
className: 'field'
React.DOM.label null, 'Address'
React.DOM.div
className: 'three fields'
React.DOM.div
className: 'field'
React.DOM.input
type: 'text'
name: 'street'
placeholder: 'Street'
value: #state.street
onChange: #handleChange
React.DOM.div
className: 'field'
React.DOM.input
type: 'text'
name: 'location'
placeholder: 'Location'
value: #state.location
onChange: #handleChange
React.DOM.div
className: 'field'
React.DOM.input
type: 'text'
name: 'addendum'
placeholder: 'Addendum'
value: #state.street
onChange: #handleChange
handleChange: (e) ->
#setState "#{e.target.name}": e.target.value
valid: ->
#state.name && #state.street && #state.location
As you can see it exposes a method called valid(). I also have a CustomerModal component which is a semantic-ui modal dialog that renders the form:
#CustomerModal = React.createClass
render: ->
React.DOM.div
className: 'ui active modal'
React.DOM.i className: 'close icon'
React.DOM.div className: 'header', 'Customer'
React.DOM.div
className: 'content'
React.createElement(CustomerForm, ref: 'form')
React.DOM.div className: 'actions',
React.DOM.button
type: 'submit'
disabled: !#refs.form.valid()
'Submit'
As given by the semantic-ui's modal markup I'd like to have the submit button on the CustomerModal component. In addition I would like that submit button to be disabled as long as the form is not valid, hence calling valid() on the CustomerForm component, because the component itself should determine if it's valid or not.
However, when I try to use the #refs object in the above way, I always get the error TypeError: this.refs.form is undefined
I am quite new to Ract.js and this is part of my learning experience with it in a small Rails project, using CoffeeScript.

You shouldn't need to use React's refs to access direct descendants.
You can maintain direct references in scope.
Aside, you might benefit from something like
{form, div, label, input} = React.DOM
You wouldn't use refs the way you have anyway. Always you can get the component with findDOMNode over the ref as an identifier. Then with that reference you could call a component function.
Notice the React.findDOMNode line
https://facebook.github.io/react/docs/more-about-refs.html#completing-the-example
ae final prognosis:
I think it's like in the docs where they say "if you find yourself wondering how to make crazy Calder mobiles geometry with refs, maybe it's time to rethink your data structure." Of course they say "state", and then you are off to learn Immutables and Flux. And if you get really advanced you can sometimes drop React completely and just use Flux, with WebGL. My opinion.
https://github.com/Terebinth/Vickers is my latest complete React codebase.

Related

Is it possible to get a set of checkboxes in react-hook-form to bind to a single array?

I am using react-hook-form with material-ui checkboxes. The following code works fine; however, each checkbox gets bound to its own field. In partial, when I hit submit, the output is something like this: {option1: true, option2: undefined, option3: true}
What I am hoping for is to get the output from all three checkboxes to bind into a single array, i.e. for the output to be something like this: {checkboxFieldName: [option1, option3]}.
I know that when using Formik, this is possible when the FormControlLabels of all checkboxes have the same names. So, just wondering if something similar is possible with react-hook-form.
Thank you in advance.
const options = [
{ key: 'option1', label: 'Option 1', val: 'option1' },
{ key: 'option2', label: 'Option 2', val: 'option2' },
{ key: 'option3', label: 'Option 3', val: 'option3' },
]
function App() {
const { handleSubmit, control } = useForm();
const onSubmit = data => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<FormControl>
<FormLabel>Check all suitable options</FormLabel>
<FormGroup>
{options.map((option) => (
<FormControlLabel
key={option.key}
name='checkboxFieldName'
value={option.val}
control={
<Controller
control={control}
name={option.key}
render={({ field: { onChange, value }}) => (
<Checkbox
checked={value}
onChange={e => onChange(e.target.checked)}
/>)}
/>}
label={option.label}
/>)
)}
</FormGroup>
</FormControl>
<Button type="submit">Save</Button>
</form>
);
}
I tried to do the same thing with Material UI but I ended up with more problems. Basically, you need to store selected checkboxes somewhere else (within an array) and set them to react-hook-form's state by using setValue function: https://react-hook-form.com/api/useform/setvalue/
Example: https://blog.logrocket.com/using-material-ui-with-react-hook-form/
If you don't want to do it manually, I would suggest using react-spectrum. It has a CheckboxGroup component and it stores all the selected values in an array: https://react-spectrum.adobe.com/react-spectrum/CheckboxGroup.html
Another solution is that you don't try to convert material-ui's object (checkboxFieldName: {option1: true, option2: undefined, option3: true}) into an array ({checkboxFieldName: [option1, option3]}) before submitting. Once you submit your form, you can convert your checkboxFieldName object into an array before passing the data to an API. Such as:
let checkboxFieldName = {option1: true, option2: undefined, option3: true}
let redefinedCheckboxFieldName = []
Object.entries(checkboxFieldName).forEach(([key, value]) => {
if(value){
redefinedCheckboxFieldName.push(key)
}
})

Using Laravel to Add Dynamic Attributes to Form Fields

I am trying to create a dynamic value for a form attribute that is auto-populated based on a previous setting stored in the database. It works fine in HTML with a little Laravel and looks like:
<input type="text" class="class" id="firstName" placeholder="First Name" value="{{ $user->firstName }}">
But I want to fully generate the entire form in Laravel. I'm unsure how to pass the value into the array. I can't seem to get the form to pull the information. Here is how it is currently looking:
{{ Form::text('first_name', '', [
'class' => 'class',
'id' => 'firstName',
'placeholder' => 'First Name',
'value' => $user->firstName
])}}
Try this:
{{ Form::text('first_name', $user->firstName, [
'class' => 'class',
'id' => 'firstName',
'placeholder' => 'First Name'
])}}
For more information regarding this topic, visit this
See, if that works.
To specify a default value in laravel's form generator, the second value you pass in is made for you:
{{ Form::text('first_name', $user->firstName,
[
'class' => 'class',
'id' => 'firstName',
'placeholder' => 'First Name',
]
) }}
Please note that from laravel 5, being published next week, the form helpers are removed (and for the actual state the installable replacement packages don't work very well/without bugs). So if you are planning on upgrading to laravel 5 better don't use this, instead go with html form elements.

Laravel form with placeholder, class and input::old

I am trying to get used to work with Laravel's blade.
I would like to create a text input called company.
The input field needs to have an id and a class.
I also want to show a placeholder if there is no data in the database, or the data stored if already exists.
Finally, I would like to keep the introduced input in case of errors.
I would like to use something similar at this:
{{ Form::text(
'company',
isset($user->company)?$user->company:array('placeholder'=>'Your company'),
array('class' => 'field required', 'id' => 'company'),
Input::old('company')
) }}
Any help would be appreciated. Thanks!
The easy way, using form model binding:
{{ Form::model($user, [ ...options...]) }}
{{ Form::text(
'company', // refers to $user->company
null, // auto populated with old input in case of error OR $user->company
array('class' => 'field required', 'id' => 'company',
'placeholder' => 'Your company') // placeholder is html attribute, don't use model data here
) }}
And if you don't want form model binding, this is all you need:
{{ Form::text(
'company',
$user->company, // auto populated with old input in case of error
array('class' => 'field required', 'id' => 'company',
'placeholder' => 'Your company')
) }}
Laravel will handle re-populating inputs for you, so long as the key in the POST data is the same as your input’s name attribute.
With Form::text(), the first parameter is the field name, the second parameter is the default value you want, and the third parameter is an array of HTML attributes you want set. So, you would have:
{{ Form::text('company', null, array(
'class' => '',
'id' => '',
'placeholder' => '',
)) }}
Obviously replaced the class, id, and placeholder values with your desired values.
Found it!
It works fine for me if I do this:
{{ Form::text(
'company',
Input::old( 'company', $user -> company ) ,
array( 'class' => 'field required', 'id' => 'company', 'placeholder' => 'Your company' )
) }}

Validating a choice field against an array Symfony2

I am building a form class in Symfony2. In my class, I have a choice field. I built a function to return my choice array:
public function getCardTypes() {
return array('visa' => 'Visa', 'mc' => 'MasterCard', 'amex' => 'American Express');
}
Later, I add a choice field to my form with this array:
$builder->add('PaymentCCType', 'choice', array('choices' => $this->getCardTypes()));
And then in getDefaultOptions function I have a choice constraint for this field:
'PaymentCCType' => new Choice(array('choices' => $this->getCardTypes())),
I seem to be having a problem with this validator. When I submit this form, I get the following error underneath my select box: "The value you selected is not a valid choice". Of course, I am using one of the choices in my array.
What am I doing wrong?
/* edit */
I have noticed that of the 4 fields I have like this, I only get the error on 3 of them. the one where the choice is month (simple 1-12), validation works.
/* edit 2 */
the issue appears to occur when the array key does not match the value. i switched my array to array('Visa' => 'Visa', 'MasterCard' => 'MasterCard', 'American Express' => 'American Express') and now it works.
Is there any way around this? I feel like I can't be the only one with this issue. it occurs even when you have a regular (non-associative) array like array('Visa', 'MasterCard', 'American Express')
IMHO you should do it in different way, create class with ChoiceListInterface with methods:
public function getChoices()
{
return self::$choices;
}
public static function getTypeChoicesKeys()
{
return array_keys(self::$choices);
}
in form class:
$builder->add('type', 'choice',
array(
'expanded' => true,
'multiple' => false,
'choice_list' => new TypeChoices(),
'required' => true,
)
)
in validation.yml
type:
- NotNull: ~
- Choice: { callback: [TypeChoices, getTypeChoicesKeys] }
edit
In response to my issue, the Symfony team pointed out the choice validator accepts an array of possible values (not possible choices like the choice field). the easiest way to do this is to use the array_keys function:
'PaymentCCType' => new Choice(array('choices' => array_keys($this->getCardTypes()))),

Zend framework - addPrefixPath and namespaces

I tried to use $element->addPrefixPath() to load my custom validator but it would not find the class until I added a namespace autoload to the ini file.
I thought the whole point of addPrefixPath was to only load the validator class for the form and not the whole application. Is this correct?
It depends upon how you invoke the validator and attach it to the element:
Using an instance
Using "abbreviated" format (my own terminology)
As an instance:
$validator = new My_Validate_SomeValidator(array(
'option1' => 'value1',
'option2' => 'value2',
));
$element->addValidator($validator, true);
In this case, you are instantiating the validator yourself. So, adding My_ as an autoloader namespace is required.
Alternatively, using the "abbreviated" format:
$element->addValidator(array('SomeValidator', array(
'option1' => 'value1',
'option2' => 'value2',
), true);
or
$element->setValidators(array(
array('EmailAddress', true),
array('SomeValidator', true, array(
'option1' => 'value1',
'option2' => 'value2',
),
));
In this case, you are giving the element only an abbreviated name - 'SomeValidator' - for the validator, implicitly expecting the element to handle the instantiation. So it makes sense that the element needs to be given some namespace/path information so it can do the job.