How to pass onClick handler from parent to child components through optional labeled arguments - reason

I want to pass an event handler down a series of components from parent to child. Here is a working example:
Index.re
ReactDOMRe.renderToElementWithId(
<App myHandler={_evt => Js.log("my handler clicked")} />,
"root",
);
App.re
let component = ReasonReact.statelessComponent(__MODULE__);
let make = (~myHandler, _children) => {
...component,
render: _self => <MyComponent myHandler />,
};
MyComponent.re
let component = ReasonReact.statelessComponent(__MODULE__);
let make = (~myHandler, _children) => {
...component,
render: _self =>
<div onClick=myHandler> {ReasonReact.string("click me")} </div>,
}
As this code is now, the myHandler argument is required at each component usage otherwise we get error:
This call is missing an argument of type (~myHandler: ReactEvent.Mouse.t => unit)
Labeled arguments can be made optional by adding a =? in the function declaration like so:
let component = ReasonReact.statelessComponent(__MODULE__);
let make = (~myHandler=?, _children) => {
...component,
render: _self =>
<div onClick=myHandler> {ReasonReact.string("click me")} </div>,
};
However this gives a compilation error:
5 │ ...component,
6 │ render: _self =>
7 │ <div onClick=myHandler> {ReasonReact.string("click me")} </div>,
8 │ };
This has type:
option('a)
But somewhere wanted:
ReactEvent.Mouse.t => unit
I thought that perhaps the compiler might need a hint. So then I tried explicitly adding that type to the function declaration like this:
let component = ReasonReact.statelessComponent(__MODULE__);
let make = (~myHandler: ReactEvent.Mouse.t => unit=?, _children) => {
...component,
render: _self =>
<div onClick=myHandler> {ReasonReact.string("click me")} </div>,
};
But then the error flips on me and gives me:
4 │ let make = (~myHandler: ReactEvent.Mouse.t => unit=?, _children) => {
5 │ ...component,
6 │ render: _self =>
This pattern matches values of type
ReactEvent.Mouse.t => unit
but a pattern was expected which matches values of type
option('a)
And now I'm all confused.

Just to provide a proper answer, since it seems unlikely that this will get closed as a duplicate
Optional arguments are turned into options on the callee's side of things. Otherwise, how would you represent the absence of the argument. There are no nulls, remember, so nullability must be expressed explicitly.
myHandler is therefore an option(ReactEvent.Mouse.t => unit). onClick is of course also an optional argument which will be turned into an option, but since this is, normally, done automatically we can't just feed it an option directly.
Fortunately, someone already thought of this scenario and added a syntactic construct to be able to pass options explicitly as optional arguments. Just add a ? before the expression passed to the argument, and you should be good:
<div onClick=?myHandler> {ReasonReact.string("click me")} </div>,

So for the optional argument to work you have to handle the optional case itself in the MyComponent component.
let component = ReasonReact.statelessComponent(__MODULE__);
let make = (~myHandler=?, _children) => {
...component,
render: _self => {
let myHandler = switch myHandler {
| None => (_e) => Js.log("default");
| Some(handler) => handler;
};
<div onClick=myHandler> {ReasonReact.string("click me")} </div>
}

Related

Laravel form validation wrong when using checkbox to hide/show fields causes

I have a form in Laravel 6 that uses a checkbox to hide/show additional fields. So the fields are hidden, but when the user checks the box ('attorney') and the fields appear, they are required. Otherwise, when they are hidden (checkbox unchecked) they are not required.
The following are the validation rules for the form:
public function rules()
{
return [
'name' => 'required',
'email' => 'required|email|unique:users,email',
'password' => 'required',
'role' => 'required',
'role.*' => 'exists:roles,id',
'attorney' => 'nullable',
'bar_number' => 'nullable|required_if:attorney,1',
'law_firm_email' => 'nullable|required_if:attorney,1',
'law_firm_address' => 'nullable|required_if:attorney,1',
'law_firm_name' => 'nullable|required_if:attorney,1',
];
}
This hide/unhide works fine for users who check the box, and the form works for those that submit that extra info, but for those that don't check the box (those extra fields remain hidden) the form fails with the error "The bar number must be a string" (bar number is first field in list). The hidden fields are still being validated even though the checkbox is unchecked (the condition required_if should be false). I have tried 'checked' and 'on' for the checkbox value, same result.
This is the checkbox in the view:
<div class="col-md-6"></br>
<input id="attorney" type="checkbox" name="attorney" value="{{ old('attorney') }}"> Check if registering a law firm
#if ($errors->has('attorney'))
<span class="help-block">
<strong>{{ $errors->first('attorney') }}</strong>
</span>
#endif
</div>
This is the JavaScript that hides/shoes the DIV with the additional fields:
<script>
$(document).ready(function() {
$('input[name="attorney"]').change(function() {
if(this.checked) {
$('#bar_number_div').fadeIn('fast');
} else {
$('#bar_number_div').fadeOut('fast');
}
});
});
</script>
The JavaScript works fine as expected. The validation config is my issue.
There are no errors in the console.
I thought the required_if condition in the validation rules should work as expected, i.e., when the checkbox "attorney" is unchecked the required validation rule is not applied.
You can try with on in required_if
public function rules() {
return [
'name' => 'required',
'email' => 'required|email|unique:users,email',
'password' => 'required',
'role' => 'required',
'role.*' => 'exists:roles,id',
'attorney' => 'nullable',
'bar_number' => 'nullable|required_if:attorney,on',
'bar_number' => 'nullable|required_if:attorney,on',
'law_firm_email' => 'nullable|required_if:attorney,on',
'law_firm_address' => 'nullable|required_if:attorney,on',
'law_firm_name' => 'nullable|required_if:attorney,on',
];
}
Remove value from input checkbox
<input id="attorney" type="checkbox" name="attorney">

This expression has type ... but an expression was expected of type

I am using bs-material-ui-icon binding, but I am given type error when try to use it component.
module Cell = {
type status =
| Empty
| Tick
| Cross;
/* let icon = React.createElement(MaterialUIIcons.AccessAlarm); */
[#react.component]
let make = _children => {
<div> <MaterialUIIcons.AccessAlarm /> </div>; /** <MaterialUIIcons.AccessAlarm /> erorr **/
};
};
Here is the error message it gave:
This expression has type
'a =>
ReasonReact.component(ReasonReact.stateless,
ReasonReact.noRetainedProps,
ReasonReact.actionless)
but an expression was expected of type
React.component('a) = 'a => React.element
Type
ReasonReact.component(ReasonReact.stateless,
ReasonReact.noRetainedProps,
ReasonReact.actionless)
=
ReasonReact.componentSpec(ReasonReact.stateless,
ReasonReact.stateless,
ReasonReact.noRetainedProps,
ReasonReact.noRetainedProps,
ReasonReact.actionless)
is not compatible with type React.element
I am using react-jsx 3 (if this matter)
Like #glennsl said, this is because you're missing JSXv2 and JSXv3. There is a new branch that supports JSXv3 which you can find here https://github.com/jsiebern/bs-material-ui/tree/hooks . It was published on npm in hooks tag.

action has wrong type in ReasonReact reducer

I'm trying to create a simple todo app, this is an input component and I need a reducer to update the state of the input. This code is throwing the error - This pattern matches values of type action but a pattern was expected which matches values of type unit => string
For some reason it expects action to be unit => string and I have no idea why. Can anyone help?
type state = string;
type action =
| InputChange(string);
let component = ReasonReact.reducerComponent("Input");
let make = (~onSubmit, _children) => {
...component,
initialState: () => "",
reducer: action =>
switch (action) {
| InputChange(text) => ReasonReact.Update(text)
},
render: ({state: todo, send}) =>
<input
className="input"
value=todo
type_="text"
placeholder="What do you want todo"
onChange={e => send(ReactEvent.Form.target(e)##value)}
onKeyDown={
e =>
if (ReactEvent.Keyboard.key(e) == "Enter") {
onSubmit(todo);
send(() => "");
}
}
/>,
};
The type of action is inferred by the use of send in render, where you pass it () => "", a function of type unit => string. It should be send(InputChange("")).
You're also missing the state argument on reducer. It should be reducer: (action, state) => ..., or reducer: (action, _state) => ... to avoid the unused warning, since you're not using it.

Unbound record field name in Reason Component

Borrowing just about all of Yawar's helpful answer, I have the following:
$cat src/index.re
let products = [|
{name: "Football", price: 49.99},
{name: "Basebll", price: 1.99},
{name: "Poker", price: 33.99}
|];
ReactDOMRe.renderToElementWithId(
<ProductTable products />
);
$cat src/productRow.re
let component = ReasonReact.statelessComponent("ProductRow");
let echo = ReasonReact.stringToElement;
let make = (~name: string, ~price: float, _) => {
...component,
render: (_) => {
<tr>
<td>{echo(name)}</td>
<td>{price |> string_of_float |> echo}</td>
</tr>
}
};
$cat src/ProductTable.re
let component = ReasonReact.statelessComponent("ProductTable");
let echo = ReasonReact.stringToElement;
let make = (~products, _) => {
...component,
render: (_) => {
let productRows = products
|> Array.map(({name, price}) => <ProductRow key=name name price />)
|> ReasonReact.arrayToElement;
<table>
<thead>
<tr>
<th>{echo("Name")}</th>
<th>{echo("Price")}</th>
</tr>
</thead>
<tbody>productRows</tbody>
</table>
}
}
I'm getting the following compile-time error:
7 ┆ render: (_) => {
8 ┆ let productRows = products
9 ┆ |> Array.map(({name, price}) => <ProductRow key=name name price />
)
10 ┆ |> ReasonReact.arrayToElement;
11 ┆
Unbound record field name
How can I fix this compile-time error?
The Reason compiler will infer a lot of types for you but for records, you have to first declare what fields it has (and their types) ahead of time.
Pulling from #yawar's helpful answer, you simply need to include the type definition at the top of your file:
type product = {name: string, price: float};
With that, the compiler will be able to tell that products is of type array(product) and should continue along happily from there. Give it a try here

cakephp 3: change class input error

I build my form template according the documentation. It seemed everything was fine until I get fields errors. Now I have two problems:
How can I change the class name of the forms fields when they get error?
Solution:
$this->loadHelper('Form', [
'templates' => 'your_template_file',
'errorClass' => 'your-class',
]);
How can I set escape => false in the error-message from cakephp, when the field get error? Because I have icon within that div, such as
<div class="error-message"><i class="fa fa-times"></i> My error</div>
Well, I got part of th solution. To escape HTML I could put $this->Form->error('field', null, ['escape' => false]); in all fields, but it´s a hard manually task. I´d like to keep escape with default of all fields errors. I could edit the FormHelper.php class. However, I think that is not good idea.
My form template is:
'formStart' => '<form {{attrs}} class="form-horizontal" novalidate>',
'inputContainer' => '{{content}}',
'input' => '<input type="{{type}}" name="{{name}}" {{attrs}} class="form-control"/>',
'checkbox' => '<input type="checkbox" value="{{value}}" name="{{name}}" {{attrs}}/>',
'textareaContainerError' => '{{content}}',
'textarea' => '<textarea name="{{name}}" {{attrs}} class="form-control"></textarea>',
'select' => '<select name="{{name}}" {{attrs}} class="form-control">{{content}}</select>',
'button' => '<button {{attrs}} class="btn btn-primary">{{text}}</button>',
'nestingLabel' => '{{input}}',
'formGroup' => '{{input}}',
to the second part of the question: you can extend FormHelper like in code below, so that escape will be set to false by default
// extended FormHelper, this goes in src/View/Helper
namespace App\View\Helper;
use Cake\View\Helper;
class MyFormHelper extends Helper\FormHelper
{
public function error($field, $text = null, array $options = [])
{
if (!isset($options['escape'])) {
$options['escape'] = false;
}
return parent::error($field, $text, $options);
}
}
next create alias for this helper in AppController.php
public $helpers = [
'Form' => ['className' => 'MyForm']
];
this also allows you to add more customization of your own and at any time, you can go back to default implementation of FormHelper, just remove that alias from AppController.php.
For those who wants an 'easy solution' to escape error message on some fields, you cant simply set escape options to false :
<?= $this->Form->input('email', [
"label" => "Email",
"error" => [
"escape" => false
]
]) ?>