Printing lists with unknown length - reason

I've got a list(string) with an unknown length which I would like to render in my ReasonReact component. I've read that there is no built-in support for printing complex data structures. But how would one go about rendering a list in its component in a pure functional way without using Javascript? I've found a snippet that achieves this using recursion on pattern matching. I presume there must be an easier way as this seems like a rather often-used operation.
The Javascript equivalent of what I'm trying to achieve is Array.toString().

If I understand you correctly, there are two steps you want accomplished:
To create a string out of the list(string), preferably formatted in the same way Array.toString does, i.e. comma-separated without surrounding square brackets, and without using any JavaScript-specific APIs.
To render the string as a ReasonReact component.
Step 1: String conversion
This can be done using String.concat:
let myList = ["a", "b", "c"];
let myString = String.concat(", ", myList);
which will return "a, b, c"
Step 2: Render as ReasonReact component
Rendering strings in ReasonReact are done using ReasonReact.string. Here's a complete, runnable example of a component taking a prop items of type list(string) and rendering it in a <span> element.
module ListRenderer = {
let component = ReasonReact.statelessComponent("ListRenderer");
let make = (~items, _children) => {
...component,
render: _self =>
<span> {ReasonReact.string(String.concat(", ", items))} </span>
};
};
ReactDOMRe.renderToElementWithId(<ListRenderer items=["a", "b", "c"] />, "preview");
Playground link

Related

Where is the documentation to write an event handler for input text box?

Originally I wanted to know:
How do I write a handler for this?
type state = string;
type action = | ChangeName(string)
let reducer = (_state, action) => switch action { | ChangeName(text) => text }
[#react.component]
let make = () => {
let (state, dispatch) = React.usefReducer(reducer, "");
/* What is the parameter type and how do I decode it? */
let onChange = ??? => dispatch(ChangeText(????));
<input value=state onChange/>
}
Specifically, what is the parameter type for the punned onChange handler and how do I decode it?
Every reference I come across is for JS, which I'm having difficulty translating to Re.
EDIT
The answer I found by scraping github:
let onChange = event => dispatch(ChangeName(ReactEvent.Form.target(event)##value));
Say I'd like to use another JSX element, where's the documentation? OR, is their a supposition that people coming to this from elsewhere have knowledge apriori? (I'm mostly comfortable with 'c').
You can get the types of all the DOM attributes from https://github.com/rescript-lang/rescript-react/blob/v0.10.1/src/ReactDOM.res
This file contains bindings to ReScript-React's subset of DOM attributes. It has:
onChange: ReactEvent.Form.t => unit
ReactEvent.Form module is declared at https://github.com/rescript-lang/rescript-react/blob/v0.10.1/src/ReactEvent.resi#L168
When looking for anything specific to ReScript-React, search that repo.
Looks like you have the correct code to handle the event now. Btw, you have in some places the variant constructor ChangeName and in others ChangeText, I assume the correct one is one of those. The compiler will of course catch this too :-)

Dynamic list of urls in Twirl/Play

I'm trying to create a dynamic list of URLs in Twirl template with Play with no luck. I can't seem to map over a collection to create list of URLs for some reason. This is what I have:
#casesCollection.map(e => {
<td>#e.name</td>
})
The part within the href does not compile. Any ideas how this issue can be solved?
You've not given enough details about the type of casesCollection and the error you're getting.
Here's a simplified example that works:
In the controller:
def index = Action {
val casesCollection = List("A", "B", "C")
Ok(views.html.xxx(casesCollection))
}
The view xxx.scala.html:
#(casesCollection: List[String])
<ul>
#casesCollection.map { e =>
<li>#e</li>
}
</ul>
In my example casesCollection is a simple List of Strings. You may have a list of some sort of objects, but it should be easy to adapt.

#helper.repeat in PlayFramework

I don't understand how to describe #helper.repeat in play2.2.1.
#helper.form(action = routes.Application.send) {
#helper.repeat(
field = form1("input"), min = 3
) {fld =>
#helper.inputText(
field = fld, '_label ->"input"
)
}
}
It is the part fld => #helper.inputText(field = fld) that I can't understand.
What does it mean?
I know Java, so I assume it is a functional writing, but in above code, where does the variable fld come from?
And why the tip of the arrow indicates #helper.inputText(field = fld)?
why is fld the value of field in #helper.inputText?
I have googled, but I couldn't find an enough explanation for a beginner.
I am not sure of Scala's grammar.
Please explain above code for a beginner?
Original Answer
This seems to be a bit overcomplicated. There is no need to assign values by hand. Usually you would write a repeater like this:
#helper.repeat(form1("input"), min = 3) { fld =>
#helper.inputText(fld, '_label ->"input")
}
In functional programming this is a so called higher-order function. You may know other scala built in higher-order functions like map, filter or foreach. #helper.repeat is very similar to foreach. form1("input") refers to a collection of values you want to display. min = 1 tells the repeater to show at least one field. Finally within { fld => ... } you iterate over all values defined in your collection.
In other words: { fld => ... } is just an anonymous function that takes a single Field parameter (fld) and displays a text input for that specific field.
Follow Up
Ok, I'm trying to follow up your questions from the comments. Let's start by the signature of helper.repeat. There is no magic involved here, it's just a regular Scala function:
helper.repeat(field: Field, min: Int)(fieldRenderer: (fld: Field) => Html): Seq[Html]
In Scala, functions can have multiple parameter lists. Here we have two. The first parameter list takes two parameters: field and min. The second parameter list takes a single function as parameter: fieldRenderer. fieldRenderer itself takes again a single parameter (fld) and returns Html.
The important thing is, you are not passing "data" but a function instead. To clear this up:
The signature fieldRenderer: (fld: Field) => Html
is equal to def fieldRenderer(fld: Field): Html
This means, you can do anything within this function, as long as it returns Html. That's exactly what happens in the example at the very top. You pass a Field and return Html. You do that by calling #helper.inputText.
Now repeat first gets a List[Field] from field you pass as first parameter. This list corresponds to the String List of your container class. Also repeat ensures there is at least one element in that list. This is, because you passed 1 as min. Then the function fieldRenderer is applied to all elements in our list.
A pseudo code implementation of repeat could look like this:
def repeat(field: Field, min: Int)(fieldRenderer: (fld: Field) => Html): Seq[Html] {
val list: List[Field] = field.getFields
var output: List[Html] = List()
for (i = 0; i < list.length; i++) {
val element: Field = list.get(i)
val html: Html = fieldRenderer(element)
output += html
}
return output
}

Cannot access the parameter of a Menu.param from a Lift Snippet

I'm trying to extract the parameter from a Lift Menu.param within a snippet so that I can use it to create a named Comet. However, I get a NullPointerException when I try to pass the parameter to the snippet using SnippetDisptach in my Boot.scala, as suggested here:
http://comments.gmane.org/gmane.comp.web.lift/44299
I've created the Menu item as follows:
object AnItemPage {
// create a parameterized page
def menu = Menu.param[Item]("Item", "Item",
s => fetchItem(s), item => item._id.toString) / "item"
private def fetchItem(s:String) : Box[Item] = synchronized {
ItemDAO.findById(ObjectId.massageToObjectId(s))
}
}
I've added the menu to SiteMap. I've also created a Snippet which I would like to pick up the Item parameter. (I'm using fmpwizard's InsertNamedComet library here):
class AddCometItemPage(boxedItem: Box[Item]) extends InsertNamedComet with DispatchSnippet{
val item : Item = boxedItem.openOr(null)
override lazy val name= "comet_item_" + item._id.toString
override lazy val cometClass= "UserItemCometActor"
def dispatch = null
}
My next step is to crate an instance of this class as demonstrated by David Pollak here:
http://comments.gmane.org/gmane.comp.web.lift/44299
This is what I have added to my Boot.scala:
LiftRules.snippetDispatch.append {
case "item_page" => new AddCometItemPage(AnItemPage.menu.currentValue)
}
My item.html references this snippet:
<div class="lift:item_page">
I get the following null pointer exception when I compile and run this:
Exception occurred while processing /item/5114eb4044ae953cf863b786
Message: java.lang.NullPointerException
net.liftweb.sitemap.Loc$class.siteMap(Loc.scala:147)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.siteMap(Menu.scala:170)
net.liftweb.sitemap.Loc$class.allParams(Loc.scala:123)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.allParams(Menu.scala:170)
net.liftweb.sitemap.Loc$class.net$liftweb$sitemap$Loc$$staticValue(Loc.scala:87)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.net$liftweb$sitemap$Loc$$staticValue(Menu.scala:170)
net.liftweb.sitemap.Loc$$anonfun$paramValue$2.apply(Loc.scala:85)
net.liftweb.sitemap.Loc$$anonfun$paramValue$2.apply(Loc.scala:85)
net.liftweb.common.EmptyBox.or(Box.scala:646)
net.liftweb.sitemap.Loc$class.paramValue(Loc.scala:85)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.paramValue(Menu.scala:170)
net.liftweb.sitemap.Loc$$anonfun$currentValue$3.apply(Loc.scala:114)
net.liftweb.sitemap.Loc$$anonfun$currentValue$3.apply(Loc.scala:114)
net.liftweb.common.EmptyBox.or(Box.scala:646)
net.liftweb.sitemap.Loc$class.currentValue(Loc.scala:114)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.currentValue(Menu.scala:170)
bootstrap.liftweb.Boot$$anonfun$lift$8.apply(Boot.scala:107)
bootstrap.liftweb.Boot$$anonfun$lift$8.apply(Boot.scala:106)
net.liftweb.util.NamedPF$$anonfun$applyBox$1.apply(NamedPartialFunction.scala:97)
net.liftweb.util.NamedPF$$anonfun$applyBox$1.apply(NamedPartialFunction.scala:97)
net.liftweb.common.Full.map(Box.scala:553)
net.liftweb.util.NamedPF$.applyBox(NamedPartialFunction.scala:97)
net.liftweb.http.LiftRules.snippet(LiftRules.scala:711)
net.liftweb.http.LiftSession$$anonfun$net$liftweb$http$LiftSession$$findSnippetInstance$1.apply(LiftSession.scala:1506)
net.liftweb.http.LiftSession$$anonfun$net$liftweb$http$LiftSession$$findSnippetInstance$1.apply(LiftSession.scala:1506)
net.liftweb.common.EmptyBox.or(Box.scala:646)
net.liftweb.http.LiftSession.net$liftweb$http$LiftSession$$findSnippetInstance(LiftSession.scala:1505)
net.liftweb.http.LiftSession$$anonfun$locateAndCacheSnippet$1$1$$anonfun$apply$88.apply(LiftSession.scala:1670)
net.liftweb.http.LiftSession$$anonfun$locateAndCacheSnippet$1$1$$anonfun$apply$88.apply(LiftSession.scala:1669)
Has anybody any idea where I'm going wrong? I've not been able to find a lot of information on Menu.param.
Thank you very much for your help.
f
I have never tried what you are doing, so I am not sure the best way to accomplish it. The way you are using the Loc Param, you are extracting a variable from a URL pattern. In your case, http://server/item/ITEMID where ITEMID is the string representation of an Item, and which is the value that gets passed to the fetchItem function. The function call will not have a value if you just arbitrarily call it, and from what I can see you are requesting a value that is not initialized.
I would think there are two possible solutions. The first would be to use S.location instead of AnItemPage.menu.currentValue. It will return a Box[Loc[Any]] representing the Loc that is currently being accessed (with the parameters set). You can use that Loc to retrive currentValue and set your parameter.
The other option would be to instantiate the actor in your snippet. Something like this:
item.html
<div data-lift="AnItemPage">
<div id="mycomet"></div>
</div>
And then in your AnItemPage snippet, something like this:
class AnItemPage(item: Item) {
def render = "#mycomet" #> new AddCometItemPage(item).render
}
I haven't tested either of those, so they'll probably need some tweaking. Hopefully it will give you a general idea.

How to define a `separator` tag in play-1.x without modifing play's source code

I want to define a tag separator tag, which inside a list tag, can add separator between items.
The sample code is:
List<String> users = new ArrayList<String>();
users.add("Jeff");
users.add("Mike");
#{list users, as: 'user'}
#{separator ' + ' /}
<span>${user}</span>
#{/list}
If I don't use the separator tag, the code will be:
#{list users, as: 'user'}
${user_isFirst ? '' : ' + '}
<span>${user}</span>
#{/list}
The generated html code will be:
<span>Jeff</span> + <span>Mike</span>
I tried defined a fastTag:
public static void _separator(Map<?, ?> args, Closure body, PrintWriter out, GroovyTemplate.ExecutableTemplate template, int fromLine) {
Object value = args.get("arg");
// TODO how to get the value of `as` defined in parent `list` tag?
out.print(value);
}
But the problem is I can't get the value of as defined in list tag (which is user) in this case.
You can create a custom list tag in groovy like this
#{list items:_arg, as:'tmp'}
%{
attrs = [:]
attrs.put(_as, tmp)
}%
#{ifnot tmp_isFirst}${_sep}#{/ifnot}
#{doBody vars:attrs /}
#{/list}
and use it like this
#{myList users, as:'user', sep:','}
${user}
#{/myList}
You should trace into your FastTag implementation. I think you'll see all the variables in scope inside the args map. This is from memory - so, sorry if not.
That said, I think it might be simpler if you copy the Java code for #{list} and add a new parameter, like
#{list users, as: 'user', separator: '+' }
and handle the logic in there. It seems a bit cleaner too from a design point of view - if it is a separator, how come you can put it anywhere you like in the code (and why not put it in twice!).
A final option is to look at Groovy or Java collection operators. http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html