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
}
Related
I have a list of words allWords and I am trying to create a new list from allWords containing only those words that begin with a specific letter currLetter. Looking at the docs, collection.map() seems like a great choice. However the statement below won't compile since .starts(with: ) returns a boolean.
targetWords = allWords.map { $0.starts(with: currLetter) }
Can anyone point me in the right direction?
While not described in the documentation of collection type instance methods, the solution is to use filter() instead of map():
targetWords = allWords.filter { $0.starts(with: currLetter) }
Given the input value:
input =
name:'Foo'
id:'d4cbd9ed-fabc-11e6-83e6-307bd8cc75e3'
ref:5
addtData:'d4cbd9ed-fabc-11e6-83e6-307bd8cc75e3'
data:'bar'
When I try to destructure the input via a function like this:
simplify: (input)->
{ name, ref, id } = input
...the return value is still the full input or a copy of the input.
Am I missing something simple here? How can I access the destructured value. If you can't access the value via a return, it seems that destructuring has little value outside of locally scoped values.
While this isn't necessarily an advantage, the only way I was able to transpile and get the correct answer was to assign the destructure values to the local scope using # (aka this).
input =
name:'foo'
data:'bar'
id: 12314
key:'children'
ref:1
f = (input)->
{ #name, #id } = input
r = {}
f.call(r, input)
console.log r # Object {name: "foo", id: 12314}
working example - link
If someone has a better way to approach this, please add an answer so I can select it as this doesn't seem like the best way.
I am currently using ReSharper V8.1. I've only recently began using ReSharper and have found some interest in their LiveTemplate Macros. I've conjured up a solution to return a list of HotspotItems from a constant, similar to ReSharper's predefined macro "Comma-delimited list of values". In the method I take the constant variable of the template parameter and do a split string on them to provide a collection of HotSpotItems. Unfortunately it doesn't work if I use the macro more than one time within a template. Below is an extreme hack job showing my implementation of the method HotspotItems of IMacroImplementation.
I am hoping that someone out there may have done some work in this area and could possibly provide an example of how they've implemented IMacroImplementation which provides a list of items from a constant and also allows for multiple uses within a single template.
Thank you.
public override HotspotItems GetLookupItems(IHotspotContext context)
{
HotspotItems hotSpotItems = null;
foreach (var hotspot in context.HotspotSession.Hotspots)
{
if (hotspot.Expression != null && ((MacroCallExpressionNew)hotspot.Expression).Definition is Macros.DisplayMultipleItems)
{
//hotspot.CurrentValue
var multiItems = ((MacroCallExpressionNew) hotspot.Expression).Definition as DisplayMultipleItems;
if (!multiItems.ItemSet)
{
var expression = hotspot.Expression as MacroCallExpressionNew;
IMacroParameterValueNew baseValue = expression.Parameters[0].GetValue(context.SessionContext.Solution.GetLifetime(), context.HotspotSession);
string templateValue = baseValue.GetValue();
multiItems.ItemSet = true;
if (!string.IsNullOrEmpty(templateValue) && templateValue.Split(',').Any())
{
var lookupItems = templateValue.Split(',').Select(param => new TextLookupItem(param)).Cast<ILookupItem>().ToList();
if (hotSpotItems == null)
hotSpotItems = new HotspotItems(lookupItems);
else
{
foreach (var item in lookupItems)
{
hotSpotItems.Items.Add(item);
}
}
}
}
}
}
return hotSpotItems;
}
You should fire up dotPeek and point it to the ReSharper bin directory and take a look at ListMacroDef and ListMacroImpl, which is the implementation for the comma-delimited list macro.
The definition derives from SimpleMacroDefinition. It gets given the parameters in the call to GetPlaceholder, looks at the first and splits it by comma, returning the first item as the placeholder.
ListMacroImpl is just as simple. Its constructor has an [Optional] parameter of type MacroParameterValueCollection. This is the list of parameter values specified in the hotspot editor. You'll want to check for null and take the first parameter, which will be your delimited list. It then overrides GetLookupItems and returns HotspotItems.Empty if the parameter value is null, or parses the value and returns a list of TextLookupItem.
You don't need to look at the session and list of hotspots - that will get you all hotspots in the session, when you're only interested in the current hotspot, and ReSharper will create a new IMacroImplementation for each hotspot and give you those values in your constructor.
I'm trying to replace a function call like (simplified) Utility.GetString(MyEntity.SomePropertyWithRelatedEntity)=="abc" with an expression visitor into something like p => p.SubRelatedEntities.FirstOrDefault(sre => sre.SomeFlag==true).SomePropertyWithRelatedEntity.
It means, the datamodel goes like:
MyEntity -> RelatedEntity -> SubRelatedEntity
I'm trying to return a string value from the SubRelatedEntity, based on some rules in the RelatedEntity, so I don't have to re-write / copy/paste the whole filtering rules in every usage; that's why I put inside a "call-signature", so my expression visitor can identify it and replace the fake-call to Utility.GetString to some complicated lambda expressions.
My expression visitor contains something like:
public override Expression Visit(Expression node)
{
if (node == null)
return null;
Expression result = null;
if (node.NodeType == ExpressionType.Call)
{
MethodCallExpression mce = node as MethodCallExpression;
if (mce.Method.DeclaringType == typeof(Utility) && mce.Method.Name == "GetString")
{
Expression<Func<RelatedEntity, string>> exp = re => re.SubRelatedEntities.FirstOrDefault(sre => sre.SomeFlag == true).SomeStringValue;
result = exp.Body;
}
else
result = base.Visit(node);
}
else
result = base.Visit(node);
return result;
}
Now, the problem is, the "sre" parameter is not bound when called the injected lambda expression. After much research, I see the lambda parameters should be replaced with another expression visitor, specifically searching for the new parameters and replacing them with the old ones. In my situation, however, I don't have an "old parameter" - I have the expression MyEntity.SomePropertyWithRelatedEntity (e.g. an property filled with the related entities) which I need to insert somehow in the generated lambda.
I hope my problem is understandable. Thank you for any insights!
After getting no answers for long time and trying hard to find a solution, I've solved it at the end :o)! It goes like this:
The newly injected lambda expression gets an ParameterExpression - well, this is a 'helper', used when directly calling the lambda, what I don't want (hence, 'parameter not bound' exception when ToEnumerable is called). So, the clue is to make a specialized ExpressionVisitor, which replaces this helper with the original expression, which is of course available in the Arguments[] for the method call, which I try to replace.
Works like a charm, like this you can reuse the same LINQ expressions, something like reusable sub-queries, instead of writing all the same LINQ stuff all time. Notice as well, that expression calling a method is not allowed in EF, in Linq2Sql it worked. Also, all the proposed web articles only replace the parameter instances, when constructing/merging more LINQ expressions together - here, I needed to replace a parameter with an faked-method-call argument, e.g. the method should not be called, it only stands for a code-marker, where I need to put my LINQ sub-query.
Hope this helps somebody, at the end it's pretty simple and logical, when one knows how the expression trees are constructed ;-).
Bye,
Andrej
I'm trying to write a class for a scala project and I get this error in multiple places with keywords such as class, def, while.
It happens in places like this:
var continue = true
while (continue) {
[..]
}
And I'm sure the error is not there since when I isolate that code in another class it doesn't give me any error.
Could you please give me a rule of thumb for such errors? Where should I find them? are there some common syntactic errors elsewhere when this happens?
It sounds like you're using reserved keywords as variable names. "Continue", for instance, is a Java keyword.
You probably don't have parentheses or braces matched somewhere, and the compiler can't tell until it hits a structure that looks like the one you showed.
The other possibility is that Scala sometimes has trouble distinguishing between the end of a statement with a new one on the next line, and a multi-line statement. In that case, just drop the ; at the end of the first line and see if the compiler's happy. (This doesn't seem like it fits your case, as Scala should be able to tell that nothing should come after true, and that you're done assigning a variable.)
Can you let us know what this code is inside? Scala expects "expressions" i.e. things that resolve to a particular value/type. In the case of "var continue = true", this does not evaluate to a value, so it cannot be at the end of an expression (i.e. inside an if-expression or match-expression or function block).
i.e.
def foo() = {
var continue = true
while (continue) {
[..]
}
}
This is a problem, as the function block is an expression and needs to have an (ignored?) return value, i.e.
def foo() = {
var continue = true
while (continue) {
[..]
}
()
}
() => a value representing the "Unit" type.
I get this error when I forget to put an = sign after a function definition:
def function(val: String):Boolean {
// Some stuff
}