I'm winding throught this Yabe tutorial and have been happily get bugs and solving them on my own.... until now.
in
http://localhost:9000/#documentation/guide9
This is the part about customizable edit window..
For whatever reason, when I post a new message, via
http://localhost:9000/admin/new
I receive a null pointer around tags...
In /app/controllers/Admin.java (around line 48)
44: post.content = content;
45: post.tags.clear();
46: }
47: //Set tags list
48: for(String tag : tags.split("\\s+")){
49: if(tag.trim().length() > 0) {
50: post.tags.add(Tag.findOrCreateByName(tag));
51: }
52: }
53: // Validate
54: validation.valid(post);
I looked at Admin.java and Tag.java and compared them line for line with the samples and tests copy. The only difference is an inclusion of validation on aAdmin.java for what I imagine is some test scripts written later down the road..
Any ideas?
here is my admin...
package controllers;
import play.*;
import play.mvc.*;
import java.util.*;
import models.*;
#With(Secure.class)
public class Admin extends Controller {
#Before
static void setConnectedUser() {
if(Security.isConnected()) {
User user = User.find("byEmail", Security.connected()).first();
renderArgs.put("user", user.fullname);
}
}
public static void index() {
List<Post> posts = Post.find("author.email", Security.connected()).fetch();
render(posts);
}
public static void form(Long id) {
if(id != null) {
Post post = Post.findById(id);
render(post);
}
render();
}
public static void save(Long id, String title, String content, String tags) {
Post post;
if(id == null) {
// Create post
User author = User.find("byEmail", Security.connected()).first();
post = new Post(author, title, content);
} else {
// Retrieve post
post = Post.findById(id);
post.title = title;
post.content = content;
post.tags.clear();
}
//Set tags list
for(String tag : tags.split("\\s+")){
if(tag.trim().length() > 0) {
post.tags.add(Tag.findOrCreateByName(tag));
}
}
// Validate
validation.valid(post);
if(validation.hasErrors()) {
render("#form", post);
}
//Save
post.save();
index();
}
}
here is my tag.java
package models;
import java.util.*;
import javax.persistence.*;
import play.db.jpa.*;
import play.data.validation.*;
#Entity
public class Tag extends Model implements Comparable<Tag> {
#Required
public String name;
private Tag(String name) {
this.name = name;
}
public static Tag findOrCreateByName(String name) {
Tag tag = Tag.find("byName", name).first();
if(tag == null) {
tag = new Tag(name);
}
return tag;
}
public static List<Map> getCloud() {
List<Map> result = Tag.find(
"select new map(t.name as tag, count(p.id) as pound) from Post p join p.tags as t group by t.name"
).fetch();
return result;
}
public String toString() {
return name;
}
public int compareTo(Tag otherTag) {
return name.compareTo(otherTag.name);
}
}
In the form that calls the save() method you might be missing an input with name 'tags'. Something like:
<input id="tags" name="tags" type="text" value="" />
In the tutorial there is a template with:
<p>
#{field 'tags'}
<label>Enter some tags:</label>
<input type="text" size="50"
name="${field.name}" value="${post?.tags?.join(' ')}" />
#{/field}
</p>
Check that you have it.
Related
I have a textfield which uses autocomplete mixin on tapestry. The mixin is working fine as it is, but I am having a problem with tagging the values of list of names with duplicate values. Now I am wondering if I can somehow pass the id of the data on autocomplete upon selection.
Here is my code for pupulating the list.
List<String> onProvideCompletionsFromUserName(String partial) {
List<String> matches = new ArrayList<String>();
String partialUpper = partial.toUpperCase();
List<User> users = clientFinder.findUsers();
// int i = 0;
for (User user : users){
String name = NameUtil.toName(user.getFirstName(), user.getFamilyName());
if (name.toUpperCase().contains(partialUpper)) {
matches.add(name );
// if (i++ >= 5) {
// break;
// }
}
}
return matches;
}
Is there a way for me to pass the ID with the list like
(List onProvideCompletionsFromUserName)?
Has anyone encountered this problem as well ? Thanks for your response.
The only way I was able to do it was by extending the Autocomplete mixin with my own version, as the configure method in the mixin is marked protected. Here is my class. Note that I am firing my own event. You will have to give your own values for label, value and uid in the providecompletions handler. The zone parameter is the zone you want to update when the user clicks and item in the completion list.
Mixin:
import java.util.ArrayList;
import java.util.List;
import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.annotations.OnEvent;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.json.JSONLiteral;
import org.apache.tapestry5.json.JSONObject;
import org.got5.tapestry5.jquery.mixins.Autocomplete;
public class UserAutocomplete extends Autocomplete {
public static final String CHANGE_EVENT_NAME = "autocompleteUser";
#Inject
private ComponentResources resources;
#Parameter(defaultPrefix=BindingConstants.LITERAL)
private String zone;
#OnEvent(value = "provideCompletions")
public List<JSONObject> autoComplete(String query) {
List<JSONObject> strings = new ArrayList<JSONObject>();
if(query != null) {
for(User u : service.searchUsers(query.trim())) {
JSONObject so = new JSONObject();
String name = u.getName();
so.put("label", name);
so.put("value", name);
so.put("uid", u.getId());
strings.add(so);
}
}
return strings;
}
protected void configure(JSONObject config) {
config.put("url", resources.createEventLink("autocomplete").toURI());
String url = resources.createEventLink(CHANGE_EVENT_NAME).toURI();
config.put("options", new JSONObject().put("select", new JSONLiteral("function(e, d) {var zone = $('#" + zone + "'); if (!zone) { return; } "
+ "zone.tapestryZone('update', {url: '" + url + "'+'/'+d.item.uid});}")));
}
}
Page Template:
<t:textfield value="query" autocomplete="off" t:mixins="UserAutocomplete" t:zone="resultZone" />
Page Class:
...
#InjectComponent
private Zone resultZone;
#OnEvent(value = UserAutocomplete.CHANGE_EVENT_NAME)
void userChange(Integer id) {
User selectedUser = service.findUser(id);
renderer.addRender(resultZone);
}
My question is: how to give dynamic param to an url using <s:itarator> in struts2?
I have some data stored in a MySQL database, I put the data (Id and Name) in two ArrayList
package Model;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
public class Film {
private String id;
private String titolo;
private String trama;
private int i=0;
private ArrayList<String> puntate = new ArrayList<String>();
private ArrayList<Integer> idPuntate = new ArrayList<Integer>();
protected String DRIVER = "com.mysql.jdbc.Driver";
protected String url = "jdbc:mysql://localhost/SitoWeb";
protected String user = "root";
protected String psw = "";
private Connection con;
public String execute(){
Connessione();
Query();
return "success";
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public String getId() {return id;}
public void setId(String id) {this.id = id;}
public String getTitolo(){return titolo;}
public void setTitolo(String titolo) {this.titolo=titolo;}
public String getTrama(){return trama;}
public void Connessione(){
try{
con=null;
Class.forName(DRIVER);
con = DriverManager.getConnection(url,user,psw);
}catch(Exception e){}
}
public void Query(){
try {
PreparedStatement cmd = con.prepareStatement("SELECT Film.Titolo, Film.Trama, Episodi.Nome, Episodi.idEpisodio FROM Film INNER JOIN Episodi ON Film.Id = Episodi.id_Film WHERE id = ?");
cmd.setString(1, id);
ResultSet rs = cmd.executeQuery();
while(rs.next()){
titolo = rs.getString("titolo");
trama = rs.getString("trama");
idPuntate.add(i, rs.getInt("idEpisodio"));
puntate.add(i,rs.getString("Nome"));
i++;
}
rs.close();
cmd.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public ArrayList<Integer> getIdPuntate() {
return idPuntate;
}
public void setIdPuntate(ArrayList<Integer> idPuntate) {
this.idPuntate = idPuntate;
}
public ArrayList<String> getPuntate() {
return puntate;
}
public void setPuntate(ArrayList<String> puntate) {
this.puntate = puntate;
}
}
Now I want to show these data in my JSP page. I tried something like this:
<s:url action="episodi" var="episodi">
<s:param name="id"></s:param> // I don't know what to put here...
</s:url>
<ol>
<s:iterator value="puntate">
<li><s:property/></li>
</s:iterator>
</ol>
I don't know what to put in <s:param name="id"></s:param> because the param is an ArrayList. I tried to put <s:iterator value="idPuntate"> but it returns 0..
There are two problems:
If you are generating an url for each element of a Collection, then you need to put the url generation inside the iteration of that Collection, otherwise you will have always the same url:
<ol>
<s:iterator value="puntate">
<s:url action="episodi" var="episodio">
<s:param name="id">something here</s:param>
</s:url>
<li><s:property/></li>
</s:iterator>
</ol>
You have not used OOP, that would suggest to create a bean named Movie (or Film), with a List<Episode>, and Episode should be another bean with for example Long id, Integer season, Integer number, and String description.
Since you've used two different lists, one for the episode description and another one for the episode id, and assuming they're aligned (it is not guaranteed, so I strongly suggest you to refactor your code the OOP way), you can access the IDs by their index basing on the episode description index, during the iteration:
<ol>
<s:iterator value="puntate" status="ctr">
<s:url action="episodi" var="episodio">
<s:param name="id">
<s:property value="idPuntate[%{#ctr.index}]"/>
</s:param>
</s:url>
<li><s:property/></li>
</s:iterator>
</ol>
P.S: Film should be a bean declared in your action, not the action itself... otherwise it is not portable... what if you need the Film class in other actions ?
I've been working through the book "Play for Java", which is absolutely brilliant. I'm still fairly new to Java but I've been following the examples and I'm a bit stuck on Chapter 3. The code can be found here: Play for Java on GitHub.
The issue is that when I execute boundform.get(), the actual properties of the form don't seem to be making it into the "product" object. I've paused this in Eclipse's debugger and all of the values are correctly set at the line Form<Product> boundForm = productForm.bindFromRequest(); but then they just disappear by the time I get to product.save().
My controller, model, routes, and form are shown below. Please let me know if any additional information is needed.
Products.java (controller)
package controllers;
import models.Product;
import play.data.Form;
import play.mvc.Result;
import play.mvc.Controller;
import views.html.products.*;
import java.util.List;
public class Products extends Controller {
private static final Form<Product> productForm = Form.form(Product.class);
public static Result list() {
List<Product> products = Product.findAll();
return ok(list.render(products));
}
public static Result newProduct() {
return ok(details.render(productForm));
}
public static Result details(String ean) {
return TODO;
}
public static Result save() {
Form<Product> boundForm = productForm.bindFromRequest();
Product product = boundForm.get();
product.save();
return ok(String.format("Saved product %s", product));
}
}
Product.java (model)
package models;
import java.util.ArrayList;
import java.util.List;
public class Product {
public String ean;
public String name;
public String description;
public Product() {
}
public Product(String ean, String name, String description) {
this.ean = ean;
this.name = name;
this.description = description;
}
public String toString() {
return String.format("%s - %s", this.ean, this.name);
}
private static List<Product> products;
static {
products = new ArrayList<Product>();
products.add(new Product("1111111111111", "Paperclips 1",
"Paperclips description 1"));
products.add(new Product("2222222222222", "Paperclips 2",
"Paperclips description "));
products.add(new Product("3333333333333", "Paperclips 3",
"Paperclips description 3"));
products.add(new Product("4444444444444", "Paperclips 4",
"Paperclips description 4"));
products.add(new Product("5555555555555", "Paperclips 5",
"Paperclips description 5"));
}
public static List<Product> findAll() {
return new ArrayList<Product>(products);
}
public static Product findByEan(String ean) {
for (Product candidate : products) {
if (candidate.ean.equals(ean)) {
return candidate;
}
}
return null;
}
public static List<Product> findByName(String term) {
final List<Product> results = new ArrayList<Product>();
for (Product candidate : products) {
if (candidate.name.toLowerCase().contains(term.toLowerCase())) {
results.add(candidate);
}
}
return results;
}
public static boolean remove(Product product) {
return products.remove(product);
}
public void save() {
products.remove(findByEan(this.ean));
products.add(this);
}
}
Routes
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# Home page
GET / controllers.Application.index()
GET /products/ controllers.Products.list()
GET /products/new controllers.Products.newProduct()
GET /products/:ean controllers.Products.details(ean: String)
POST /products/ controllers.Products.save()
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.at(path="/public", file)
details.scala.html
#(productForm: Form[Product])
#import helper._
#import helper.twitterBootstrap._
#main("Product form") {
<h1>Product form</h1>
#helper.form(action = routes.Products.save()) {
<fieldset>
<legend>Product (#productForm("name").valueOr("New"))</legend>
#helper.inputText(productForm("ean"), '_label -> "EAN")
#helper.inputText(productForm("name"),'_label -> "Name")
#helper.textarea(productForm("description"), '_label -> "Description")
</fieldset>
<input type="submit" class="btn btn-primary" value="Save">
<a class="btn" href="#routes.Application.index()">Cancel</a>
}
}
I'm sure this is something painfully obvious. Thank you so much!
See the documentation's Handling Binding Failure section. Calling .get() on a Form isn't safe, because if there are validation errors it will return null. The preferred way is to check for errors first via hasErrors(), and then handle it from there.
if (boundform.hasErrors()) {
/* There are errors somewhere in the form,
* return it to the view and display them there,
* or do whatever else you need to handle the error.
*/
return badRequest(...);
} else {
// A valid `Product`, proceed as normal.
Product product = boundform.get();
return ok(....);
}
SBT cache issue. I ran activator clean and everything worked.
I started pasting entire files from the GitHub repo to see if I could narrow it down. That led to a new set of errors which brought me to this StackOverflow question which had the SBT cache suggestion in the thread.
I appreciate the suggestion and link to the error methods, LimbSoup. Will definitely be looking into those regardless and am sure I'll probably reference your answer in the future more than once!
Thanks so much, all.
I want to make a page which displays which Users of my website have the current "Role". Each users have a set of Roles, and a Role is affected to 0 ~ n users.
I'm displaying a basic list with Users's name, and a checkbox for each User. And then a submit button.
When the form is submitted, it will delete the Role from the selected Users.
Most of the time, it's working fine. But there is a problem if the DB' data changed between the page loading and the form submit.
If we are into this situation, it deletes the role from the wrong user!
I've tried different ways, and right now I'm using something like this :
import java.util.LinkedList;
import java.util.List;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.StatelessForm;
import org.apache.wicket.markup.html.list.AbstractItem;
import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
public class Test extends WebPage {
private static final long serialVersionUID = 1L;
/**
* An item used to link the checkbox to the Database Object
*/
public static class Item {
private boolean selected = false;
private int num;
public Item(final int num) { this.num = num; }
public final boolean isSelected() { return selected; }
public final int getNum() { return num; }
public final void setSelected(boolean selected) { this.selected = selected; }
public final void setNum(int num) { this.num = num; }
}
public Test(final PageParameters params) {
// This list will hold items we display
final List<Item> values = new LinkedList<Item>();
// Our stateless form
final StatelessForm<Void> form = new StatelessForm<Void>("form") {
private static final long serialVersionUID = 1L;
#Override
protected void onSubmit() {
// On submit, we check our items to see which ones are selected
String toDelete = "";
for (final Item it : values) {
if (it.selected) {
toDelete += it.getNum() + ", ";
}
}
System.out.println(toDelete);
}
};
add(form);
form.add(new Button("submit"));
// We retrieve our data from the Database
final List<Integer> things = getFromDatabase();
// We print them
final RepeatingView rp = new RepeatingView("li");
form.add(rp);
for (int num : things) {
final AbstractItem item = new AbstractItem(rp.newChildId());
rp.add(item);
// We create our holder
final Item it = new Item(num);
values.add(it);
item.add(new Label("name", num));
// We use the property of our holder as model
item.add(new CheckBox("checkbox", new PropertyModel<Boolean>(it, "selected")));
}
}
/**
* Simulate a fetch from the database by swapping our data at each requests
*/
public static int num = 0;
private static List<Integer> getFromDatabase() {
final List<Integer> list = new LinkedList<Integer>();
if (num++ %2 == 0) {
list.add(1);
list.add(2);
}
else {
list.add(2);
list.add(1);
}
return list;
}
}
and :
<body>
<form wicket:id="form">
<ul>
<li wicket:id="li">
<span wicket:id="name"></span>
<input type="checkbox" wicket:id="checkbox" />
</li>
</ul>
<input type="submit" valud="submit" wicket:id="submit" />
</form>
</body>
I'm simulating a changing DB by swapping values each time the page is requested.
And what happens is that if I select "1" and press submit, it says that it'll delete "2"!
I understand why, but I've no idea how to fix it.
The best way to fix it would be to have something like this:
<input type="checkbox" name="toDelete[]" value="<the_id>" />
<input type="checkbox" name="toDelete[]" value="<the_id>" />
etc.
But I've no idea how to do it with Wicket, and I can't find anything on the Internet =/
So yeah.. any examples / ideas?
i cannot use any bean value in my custom control.:
for instance:
this is my foo.taglib.xml file.
<facelet-taglib>
<namespace>http://www.penguenyuvasi.org/tags</namespace>
<tag>
<tag-name>test</tag-name>
<component>
<component-type>test.Data</component-type>
</component>
</tag>
</facelet-taglib>
faces-config.xml
<component>
<component-type>test.Data</component-type>
<component-class>test.Data</component-class>
</component>
test.Data class
package test;
import java.io.IOException;
import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;
public class Data extends UIComponentBase {
private Object msg;
#Override
public String getFamily() {
return "test.Data";
}
#Override
public void encodeBegin(FacesContext context) throws IOException {
super.encodeBegin(context);
context.getResponseWriter().write(msg.toString());
}
public void setMsg(Object msg) {
this.msg = msg;
}
}
Bean.java:
package test;
public class Bean {
private String temp = "vada";
public String getTemp() {
return temp;
}
}
test.xhtml (doesn't work)
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://www.penguenyuvasi.org/tags">
<py:test msg="#{bean.temp}" />
</html>
test.xhtml (works)
<py:test msg="#{bean.temp}" />
In your test.Data class, I suggest that you implement the getter for msg like that:
public String getMsg() {
if (msg != null) {
return msg;
}
ValueBinding vb = getValueBinding("msg");
if (vb != null) {
return (String) vb.getValue(getFacesContext());
}
return null;
}
And then, in your encodeBegin method:
...
context.getResponseWriter().write(getMsg());
...
This getter method is needed in order to evaluate the expression you may give to the msg attribute in your JSF page.
Edit, to use ValueExpression instead of the deprecated ValueBinding:
ValueExpression ve = getValueExpression("msg");
if (ve != null) {
return (String) ve.getValue(getFacesContext().getELContext());
}