Unbound record field name in Reason Component - reason

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

Related

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

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>
}

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.

Dynamic Country - city select on form

newbie alert,
I am really enjoying perl Catalyst, however, i have googled and cant find a solution for Country - City dynamic selection. when i select a country from the dropdown, i would like the cities to change to that coutries cities only. How can i achieve this in Perl, Catalyst using HTML::FormHandler.
PS
The data is coming from mysql db with a one to many relatioship
has_field 'city_id' => (
label => 'City',
type => 'Select',
empty_select => 'Choose city',
required => 1,
required_message => 'Please enter city.',
);
has_field 'country_code' => (
label => 'Country',
type => 'Select',
empty_select => 'Choose country',
required => 1,
required_message => 'Please enter your country.',
);
has_field 'submit' => (
type => 'Submit',
value => 'Save',
element_class => ['btn']
);
sub options_country_code {
my $self = shift;
return unless $self->schema;
my #countries = $self->schema->resultset('Country')->all;
my #options = map { { value => $_->country_code, label => $_->country_name } } #countries;
unshift #options, { value => 0, label => 'Choose Country' };
return #options;
}
__PACKAGE__->meta->make_immutable;
1;
I found exactly what I was looking for, thank you for the effort, I appreciate it very much.
The solution I needed is here
I am using this code as my base:
<html>
<head>
<script type="text/javascript">
function setmenu2() {
var menu1 = document.getElementById('menu1');
var sel = menu1.options[menu1.selectedIndex].text;
var menu2 = document.getElementById('menu2');
if (sel == "Foo") {
menu2.innerHTML = "<option>Foo-1</option>"
+"<option>Foo-2</option>";
} else {
menu2.innerHTML = "<option>Bar-1</option>"
+"<option>Bar-2</option>"
+"<option>Bar-3</option>";
}
}
</script>
</head>
<body>
<select id="menu1" onchange="setmenu2()">
<option>please select...</option>
<option>Foo</option>
<option>Bar</option>
</select>
<select id="menu2">
<option>- ?? -</option>
</select>
</body>
</html>
So far this is what I have:
<script type="text/javascript">
function setcities() {
var country_select = document.getElementById('country_code');
var sel = country_select.options[country_select.selectedIndex].value;
var city_select = document.getElementById('city_id');
if (sel == "AFG") {
city_select.innerHTML = "<option value='1' id='city_id.1'>Kabul</option>"
+"<option value='2' id='city_id.2'> Qandahar</option>";
} else if (sel == "AGO"){
city_select.innerHTML = "<option value='56' id='city_id.1'>Luanda</option>"
+"<option value='57' id='city_id.2'>Huambo</option>"
+"<option value='58' id='city_id.3'>Lobito</option>";
} else {
city_select.innerHTML = "<option>nothing</option>";
}
}
</script>
<select id="country_code" onchange="setcities()">
<option value="" id="country_code.0">please select...</option>
<option value="AFG" id="country_code.1">Afghanistan</option>
<option value="AGO" id="country_code.2">Angola</option>
<option value="AIA" id="country_code.3">Anguilla</option>
</select>
<select id="city_id">
<option>- ?? -</option>
</select>

How to make checkbox checked based on the data from database in mvc 4 entity framework?

I am strong checkbox checked data in db. If the checkbox was checked i want to display it as checked in view. Is it possible to achieve this?
Here is my supporting code.
public ActionResult Index()
{
var groups = db.tm_grp_group.Where(a=>a.grp_isactive==true);
var permissions = db.tm_perm_level;
GroupPermissionVM model = new GroupPermissionVM
{
GroupList = new SelectList(groups, "grp_id", "grp_name"),
Permissions = permissions.Select(p => new PermissionVM
{
perm_id = p.perm_id,
perm_levelname = p.perm_levelname
})
};
return View(model);
}
This is index.cshtml
#model Permission.Models.GroupPermissionVM
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm())
{
#Html.LabelFor(m=>m.GroupID)
#Html.DropDownListFor(m => m.GroupID, Model.GroupList, "Please select", new { id = "ddlgrp" })
#Html.EditorFor(m => m.Permissions)
<p><input type="submit" value="Submit" /></p>
}
Inside Home/Editortemplate/PermissionVM.cshtml
#model Permission.Models.PermissionVM
#{
ViewBag.Title = "PermissionVM";
}
<div>
#Html.HiddenFor(m => m.perm_id)
#Html.HiddenFor(m => m.perm_description)
#Html.CheckBoxFor(m => m.perm_status)
#Html.LabelFor(m => m.perm_status,Model.perm_levelname)
</div>
I have table structure like this
grp_id perm_id
1 1 1
2 2 2
permlevel_name perm_status
1 Screen Level True
2 custom Level True
Grp_id is dropdownlistbox in this context. So if i select 1 corresponding permlevel_name is screen level and it is checked. So when i make selection as 1 below screenlevel checkbox should be cheked. Is it possible to achieve this?
in your code where you call PermissionVM.cshtml File.

How to dynamically access/ define a table row with pageobject gem?

I have a table like this
<iframe title="ManageLegs">
<table title="Leg Details">
<tr>
<th />
<th class="headerRow"><div>Dep Date</div></th>
<th class="headerRow"><div>ETD</div></th>
</tr>
<tr>
<td>1</td>
<td>2015-02-01</td>
<td>0900</td>
</tr>
<tr>
<td>2</td>
<td>2015-02-15</td>
<td>1100</td>
</tr>
</table>
</iframe>
Each row has its number in the first cell.
Thing is, that the number of rows varies, and when defining the PageObject for that site, I don´t know how many rows there will be.
Currently I use this code on the PageObject to access the table cells
in_iframe(:title => 'ManageLegs') do |frame|
table(:leg_details, title: 'Leg Details', :frame => frame)
# 1st leg
text_field(:departure_date1) { leg_details_element['1']['Dep Date'].text_field_element }
# 2nd leg
text_field(:departure_date2) { leg_details_element['2']['Dep Date'].text_field_element }
# 3rd leg ...
# ...
end
But this is not DRY at all.
Any advice on how to simplify this?
Thx in advance,
Christian
Personally, I like to define the rows as widgets. This proivdes a descriptive API for the page.
The Leg class to represent a row:
class Leg < PageObject::Elements::TableRow
def index
cell_element(index: 0).text
end
def dep_date
cell_element(index: 1).text
end
def etd
cell_element(index: 2).text
end
end
PageObject.register_widget :leg, Leg, :tr
The page object:
class MyPage
include PageObject
in_iframe(:title => 'ManageLegs') do |frame|
legs(:leg) do
table_element(:title => 'Leg Details', :frame => frame)
.leg_elements(:tag_name => 'tr')[1..-1]
end
end
end
Example usage:
page = MyPage.new(browser)
page.leg_elements.each do |leg|
p leg.index
p leg.dep_date
p leg.etd
end
#=> "1"
#=> "2015-02-01"
#=> "0900"
#=> "2"
#=> "2015-02-15"
#=> "1100"
Just write a method in your page:
in_iframe(:title => 'ManageLegs') do |frame|
table(:leg_details, title: 'Leg Details', :frame => frame)
end
def departure_date(number)
leg_details_element[number]['Dep Date'].text_field_element
end