I'm learning the development flow of Play Framework following this tutorial http://www.playframework.com/documentation/2.1.1/JavaTodoList.
However I'm getting this compilation error at index.scala.html view:
"value description is not a member of Product"
It's my Product's model:
package app.models;
import java.util.*;
import javax.validation.*;
import play.data.validation.Constraints.*;
/**
* Product.
*/
public class Product
{
public int id;
public String name;
public String description;
public String dimensions;
public double price;
public static List<Product> all()
{
return new ArrayList<Product>();
}
public static void create(Product product)
{
return;
}
public static void delete(Long id)
{
return;
}
}
And here is the view's code:
#(products: List[Product], productForm: Form[Product])
#import helper._
#main("ezbuy") {
<h1>#products.size() product(s)</h1>
<ul>
#for(product <- products) {
<li>
#product.description
#form(routes.Application.deleteProduct(product.id)) {
<input type="submit" value="Delete">
}
</li>
}
</ul>
<h2>Add a new product</h2>
#form(routes.Application.newProduct()) {
#inputText(productForm("label"))
<input type="submit" value="Create">
}
}
I'm just not finding where is the problem, since I've declared the Product's list at the top of the view and it's looping using the #for statement.
Thanks in advance.
There is a Scala class scala.Product (http://www.scala-lang.org/api/current/index.html#scala.Product). Scala automatically imports all from the scala package. I think you got that class and not app.models.Product.
Use the fully qualified class name:
#(products: List[app.models.Product], productForm: Form[app.models.Product])
That error does not occur if you put Product directly into the models package, since models.* are imported by default in Scala templates of Play. So there would be no need to use the fully qualified class name.
Related
I'm trying to request two numbers (first and second) as input via a form using Thymeleaf, and have the numbers added and the answer returned. The code is run from:
http://localhost:8080/add
I've based the program on this link:
https://spring.io/guides/gs/validating-form-input/
I'm also using the latest version of Springboot, currently 1.5.8.
I'm finding this simple task becoming somewhat convoluted (compared to say python), with continual errors that aren't making any sense. My current problem is I'm getting the following error, which even after checking similar posts online, I can't resolve...
Error:
"Exception processing template "add": Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringInputGeneralFieldAttrProcessor'"
Based on these posts I've change "long" to "Long" in my NumberForm, and moved my NumberForm.java under "sec.calculator", but no joy...
Any help would be appreciated !
Here's my code:
1) src/main/java
CalculatorController.java:
package sec.calculator;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
#Controller
public class CalculatorController {
#GetMapping("/add")
public String addForm(Model model) {
model.addAttribute("add", new NumberForm());
return "add";
}
#PostMapping("/add")
public String addSubmit(#ModelAttribute NumberForm add) {
return "result";
}
}
------------------------------------
CalculatorApplication.java
package sec.calculator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class CalculatorApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(CalculatorApplication.class, args);
}
}
------------------------------------
NumberForm.java
package sec.calculator;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
/**import javax.validation.constraints.Size;
*/
public class NumberForm {
#NotNull
#Min(0)
private Long first;
private Long second;
public Long add_out;
public Long add_output() {
return add_out ;
}
public void Setfirst(Long first) {
this.first = first;
}
public void Setsecond(Long second) {
this.second = second;
}
}
-----------------------------------
2) src/main/resources/templates
add.html
<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project
Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<body>
<h1>Form</h1>
<form action="#" th:action="#{/add}" th:object="${NumberForm}" method="post">
<p>First: <input type="text" th:field="*{First}" /></p>
<p>Second: <input type="text" th:field="*{Second}" /></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
</body>
</html>
------------------------------------------
Welcome to SO.
There are several issues here. In particular, Java is very much case-sensitive.
Follow the convention for setters so that Thymeleaf can
understand it:
public void setFirst(Long first) {
this.first = first;
}
public void setSecond(Long second) {
this.second = second;
}
I am assuming you left out the analogous getter methods.
Even better, see Project Lombok - stop the madness entirely and use
#Setter and #Getter or #Data.
In your form, lower-case your field names:
<p>First: <input type="text" th:field="*{first}" /></p>
<p>Second: <input type="text" th:field="*{second}" /></p>
I'd also stay away from variable names like these since they can become very confusing.
Also in your form, you are using th:object="${NumberForm}" but
you named your variable add. Thymeleaf knows nothing about this variable named NumberForm. Your variable is named add of a type NumberForm, but Thymeleaf will need the name of the variable here.
So you can do: model.addAttribute("numberForm", new
NumberForm());
and th:object="${numberForm}"
Using lower-case for your th:object (modelAttribute) is by
convention.
You'll actually need to include some code to sum the values. I'll
leave that part for you.
I am a newbie to play and have started to develop an application to fetch the data from database.
I have the following code in my application:
public static Result list() {
List products = Productslist.getListOfProducts();
return ok(index.render(products));
}
and this gives me the following error:
Actual List cannot be converted to String on method invocation conversion
You can also view my index.scala.html
#{extends 'main.html' /}
#{set title:'Cars in the car lot' /}
<h1>Products in Lot</h1>
<table border=1>
<tr>
<td>productname</td>
<td>Quantity</td>
<td>Price</td>
</tr>
#{list items:products, as:'product'}
<tr>
<td>${product.getProductname()}</td>
<td>${product.getQuantity()}</td>
<td>${product.getPrice()}</td>
</tr>
#{/list}
</table>
The complete code for Application.java is:
package controllers;
import play.*;
import play.mvc.*;
import models.Productslist;
import views.html.*;
import views.*;
import java.util.*;
public class Application extends Controller
{
public static Result list()
{
List products = Productslist.getListOfProducts();
return ok(index.render(products));
}
}
Can anyone help me find the source of the error?
Look like you used the wrong code for the view. Try this instead
#(products: List[Productslist])
#import helper._
#main("Product list") {
<h1>#product.size() product(s)</h1>
<ul>
#for(product <- products) {
<li>
#product.name
</li>
}
</ul>
}
Use play clean-all before running your server
Looks like error is happening on index.render(products) ; where method render is expecting a string but we are passing list. Can you put in the code of method render()
in my current project i've faced a problem of customizing IndicatingAjaxLink in wicket, is there any solution to change standart gif image to my own?
For example we have following listeneer
add(new IndicatingAjaxLink("closeReceivedBillspanel") {
public void onClick(AjaxRequestTarget art) {
// some timeconsuming calculations
}
});
as user clicks this link, the gif with loading appears, and i want to change this gif, is there any solution for this problem?
Have your page implements the IAjaxIndicatorAware interface
public class BasePage extends WebPage implements IAjaxIndicatorAware {
public BasePage(final PageParameters parameters) {
// Home link
AjaxLink<Page> homeLink = new AjaxLink<Page>("homeLink") {
private static final long serialVersionUID = 1L;
#Override
public void onClick(AjaxRequestTarget target) {
setResponsePage(HomePage.class);
}
};
add(homeLink);
}
#Override
public String getAjaxIndicatorMarkupId() {
return "indicator";
}
This way, you can set, in the html, any image you want to display when the loading appears by changing the image in the "img" tag
<div id="indicator" style="display: none;">
<div class="indicator-content">
Please wait... <wicket:link><img src="images/loading.gif" width="16" height="16" alt="loading" /></wicket:link>
</div>
</div>
Create yoru own custom class like, (copy whats inside IndicatingAjaxLink and update)
public class MyIndicatingAjaxLink<T> extends AjaxLink<T> implements IAjaxIndicatorAware {
private final MyAjaxIndicatorAppender indicatorAppender = new MyAjaxIndicatorAppender();
.
//rest of the code is same as IndicatingAjaxLink class
.
}
Also you need a custom AjaxIndicatorAppender within your customIndicatingAjaxLink and you need to override below method of indicatorAppender to return path of your custom image
protected CharSequence getIndicatorUrl()
Let's say the following XML is given:
<?xml version="1.0" encoding="UTF-8"?>
<ResC>
<Err text="Error text 1"/>
<ConRes>
<Err text="Error text 2"/>
<ConList>
<Err text="Error text 3"/>
<Con>
<Err text="Error text 4"/>
</Con>
</ConList>
</ConRes>
</ResC>
As you can see the <Err> element may appear on every level of the XML.
Using Simple I would like to deserialize this XML. So, I have created the following class:
#Element(required=false)
public class Err {
#Attribute
private String text;
public void setText(String text) { this.text = text; }
public String getText() { return text; }
}
However, how do I have to annotate the classes for <ResC>, <ConRes>, <ConList> and <Con>? Do I really have to declare an attribute of type <Err> in every single class in which it may appear? This seems like a lot of overhead. If so, then I would have to check every single object if it contains an error.
Is there any better and easier way? :-)
Thanks,
Robert
The important thing to remember is that Simple XML should be able to follow any structure that you can logically generate using classes. So you could just create a BaseClass that uses an error interface and applies the Decorator pattern so that it passes all of that through to a concrete error class without any of the implementing objects needing to know what they have been given.
That probably made no sense. How about I just show you...okay...I just went away and implemented exactly what I was thinking and here are the results (full code link):
The Main File:
package com.massaiolir.simple.iface;
import java.io.File;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
public class Main {
public static void main(String[] args) throws Exception {
Serializer serial = new Persister();
ResC resc = serial.read(ResC.class, new File("data/testdata.xml"));
System.out.println(" == Printing out all of the error text. == ");
System.out.println(resc.getErrorText());
System.out.println(resc.conRes.getErrorText());
System.out.println(resc.conRes.conList.getErrorText());
for (Con con : resc.conRes.conList.cons) {
System.out.println(con.getErrorText());
}
System.out.println(" == Finished printing out all of the error text. == ");
}
}
It just runs simple and displays the results.
The BaseObject.java class:
package com.massaiolir.simple.iface;
import org.simpleframework.xml.Element;
public class BaseObject implements Error {
#Element(name = "Err", required = false, type = ConcreteError.class)
private Error err;
#Override
public String getErrorText() {
return err.getErrorText();
}
#Override
public void setErrorText(String errorText) {
err.setErrorText(errorText);
}
}
This is the class that everything should extend if it wants 'Err'.
The Error interface:
package com.massaiolir.simple.iface;
public interface Error {
void setErrorText(String errorText);
String getErrorText();
}
The ConcreteError class:
package com.massaiolir.simple.iface;
import org.simpleframework.xml.Attribute;
public class ConcreteError implements Error {
#Attribute
private String text;
#Override
public String getErrorText() {
return text;
}
#Override
public void setErrorText(String errorText) {
this.text = errorText;
}
}
The actual implementing classes are after this point. You will see that they are rather trivial because the real work is being handled in the classes above.
The Con class:
package com.massaiolir.simple.iface;
public class Con extends BaseObject {
}
The ConList class:
package com.massaiolir.simple.iface;
import java.util.ArrayList;
import org.simpleframework.xml.ElementList;
public class ConList extends BaseObject {
#ElementList(entry = "Con", inline = true)
public ArrayList<Con> cons;
}
The ConRes class:
package com.massaiolir.simple.iface;
import org.simpleframework.xml.Element;
public class ConRes extends BaseObject {
#Element(name = "ConList")
public ConList conList;
}
The ResC class:
package com.massaiolir.simple.iface;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;
#Root
public class ResC extends BaseObject {
#Element(name = "ConRes")
public ConRes conRes;
}
And that is all that there is to it. Pretty simple right. I was able to bang that all out in ten minutes. It actually took me longer to write this response than it took me to write the code that I am giving you. If you do not understand anything about the code that I have just written then please let me know. I hope this helps you to understand how you might go about doing something like this.
i have created an class com.test.test.But
public class But extends Bandbox {
private Label mc_who;
public But() {
Executions.createComponents("/WEB-INF/username.zul", this, null);
Components.wireVariables(this, this, '$', true, true);
Components.addForwards(this, this, '$');
}
public String getWho() {
return mc_who.getValue();
}
public void setWho(String who) {
mc_who.setValue(who);
}
}
and an username.zul
<zk>
<label id="mc_who"></label>
</zk>
and index.zul
<window id="test" >
<bandbox>
<bandpopup>
<username who="Joe"/>
<username who="Hellen"/>
</bandpopup>
</bandbox>
</window>
and i am getting this exception
org.zkoss.zk.ui.UiException: Unsupported parent for row: <Bandpopup g4HQ2>
org.zkoss.zul.Row.beforeParentChanged(Row.java:264)
org.zkoss.zk.ui.AbstractComponent.setParent(AbstractComponent.java:959)
org.zkoss.zk.ui.impl.AbstractUiFactory.newComponent(AbstractUiFactory.java:91)
org.zkoss.zk.ui.impl.UiEngineImpl.execCreateChild0(UiEngineImpl.java:714)
org.zkoss.zk.ui.impl.UiEngineImpl.execCreateChild(UiEngineImpl.java:685)
org.zkoss.zk.ui.impl.UiEngineImpl.execCreate0(UiEngineImpl.java:629)
org.zkoss.zk.ui.impl.UiEngineImpl.execCreate(UiEngineImpl.java:596)
org.zkoss.zk.ui.impl.UiEngineImpl.execCreateChild0(UiEngineImpl.ja
Your example is not complete since I don't see row (and grid) in your sample code, while the exception said there is one. Please make a sample that can reproduce the issue.
I went through several iterations as well, and found that the most reliable way is to use the div tag as follows:
<zk>
<div>
<label id="mc_who"></label>
</div>
</zk>
This is an example of the component being used:
<window id="test" >
<bandbox>
<bandpopup>
<username who="Joe"/>
<username who="Hellen"/>
</bandpopup>
</bandbox>
</window>
And the source code:
package com.pontusnetworks.zkwidgets;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.IdSpace;
import org.zkoss.zk.ui.select.Selectors;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zul.Div;
import org.zkoss.zul.Row;
import org.zkoss.zul.Textbox;
public class Username extends Div implements IdSpace {
#Wire
private Textbox mc_who; //will be wired when Components.wireVariables is called
public Username() {
//1. Render the template
Executions.createComponents("/composite/username.zul", this, null);
//2. Wire variables, components and event listeners (optional)
Selectors.wireVariables(this, this, null);
Selectors.wireComponents(this, this, false);
Selectors.wireEventListeners(this, this);
}
public String getWho() {
return mc_who.getValue();
}
public void setWho(String who) {
mc_who.setValue(who);
}
//public void onOK() {..} //Add event listeners if required, and wired by Components.addForwards
}