9

I'm trying to use the automatic binding feature of Play, without success. I'm developing in Java, on Eclipse 4.4 Luna.

Here is my form :

<h2>Create a new user</h2>
<form action="@routes.Backend.createUser()" method="post">
    First Name
    <input type="text" name="firstName" />
    Last Name
    <input type="text" name="lastName" />
    E-mail
    <input type="email" name="email" />
    PIN
    <input type="number" name="pin" />
    Status
    <input type="text" name="status" />
    Is guest?
    <input type="checkbox" name="isGuest" />

    <input type="submit" value="Create user" />
</form>

Here is my class "Users":

@Entity
public class Users extends Model {

// Database columns
@Id
public int userId;

public String firstName;
public String lastName;
public String email;
public int pin;
public String status;
public boolean isGuest;

}

And here is my controller:

public class Backend extends Controller {

  public static Result createUser() {
    Form<Users> form = Form.form(Users.class).bindFromRequest();
    if (form.hasErrors()) {
        // doSomething()
    } else {
        Users u = form.get();
        u.save();
    }

    // TESTING
    // Checking the content of the request
    DynamicForm requestData = Form.form().bindFromRequest();
    String firstName = requestData.get("firstName");
    String lastName = requestData.get("lastName");
    // Printing the content works, I am able to see the correct values
    System.out.println(firstName); // Bob
    System.out.println(lastName); // Smith
    // This somehow doesn't work...
    System.out.println(u.firstName); // NULL
    System.out.println(u.lastName); // NULL
    System.out.println(u.userId); // Correctly generated
    // END OF TESTING

    return redirect(routes.Backend.allUsers());
  }
}

I wonder why the automatic binding of values doesn't work. I have made sure that the fields name in my form correspond to the attributes names in the class, and this should be enough for the form binding to work, right?

I am using Eclipse Luna, and I turned off automatic project build (I do it manually from the console). I know that sometimes Eclipse can cause issues because of that auto-build feature. Note: This was the way to go, but I didn't clean the project using the activator command, as user Dmitri suggested. Also, you only have to do this once, as long as you don't turn on the automatic build feature in Eclipse.

I have tried restarting Eclipse and the application several times, without success...

EDIT: I tried using only String attributes for my Users class, since the requestData.get(String s) method returns a String. But still no success...

EDIT 2: I'm going to bind the values manually... If anyone have an idea, please post :)

EDIT 3: I've updated my code to follow the rules mentioned in the answer below

EDIT 4: I can't get autobinding working only when using my Postgresql 9.3 database. When I use in-memory database, everything works smoothly. Also, since there was no JDBC driver for Java 8 and postgresql 9.3, I'm using an older version of the driver (actually the driver is on PGSQL's website, but I couldn't get it working with Play). I will have to check what happens with another DB, then I'll report back here!

EDIT 5: I tried to create my custom data binder like this:

        Formatters.register(User.class, new Formatters.SimpleFormatter<User>() {
        @Override
        public User parse(String arg0, Locale arg1) throws ParseException {
            User u = new Model.Finder<Integer, User>(Integer.class, User.class).byId(Integer.parseInt(arg0));
            return u;
        }
        @Override
        public String print(User arg0, Locale arg1) {
            return "User : " + arg0.firstName;
        }
    });

... but it didn't work!

EDIT 6: User Dmitri has found a working solution: you have to compile the project outside of Eclipse. It seems that there is some incompatibilities between Eclipse's compilator and Play! Framework's compilator...

Danish Ashfaq
  • 446
  • 4
  • 11
  • Are you getting any error ? – blackbishop Aug 18 '14 at 15:00
  • Absolutely no errors... I know that the rest is working, since I can manually bind the values and create an instance of my Users object then save it and retrieve it, but this auto-binding feature is definitely broken for me... – Danish Ashfaq Aug 18 '14 at 15:54

6 Answers6

12

I have been struggling with exactly the same problem: bindFromRequest returned nulls for "name" field. I did exactly the same what a guy in this Play for Java introduction video did: youtube.com/watch?v=bLrmnjPQsZc . But still no luck. I've been working on Windows 7 with JDK 1.8. IDE: Eclipse 4.4.0. And I run activator through cygwin.

This is what solved the problem for me:

  1. In Eclipse: Project -> Build Automatically - > turn off
  2. In cygwin: ./activator clean; ./activator compile; ./activator run;

After this, bindFromRequest binds name correctly and puts it into the database.

Dmitri Lihhatsov
  • 404
  • 4
  • 14
  • I have tried to create my custom data binder, but it still didn't work, so basically I'm still struggling with the issue... I was about to go back to plain SQL for all my requests! But now I'm going to try this right away and report here! – Danish Ashfaq Sep 05 '14 at 07:36
  • Unbelievable, it worked! So this was Eclipse related... right? Can you please explain what actually happened? Thank you so much! :) – Danish Ashfaq Sep 05 '14 at 07:45
  • No problem :) I suspect it has to do with ebean enhancements which occur after compilation. Eclipse sees new changes and compiles again, thus messing the code. – Dmitri Lihhatsov Sep 05 '14 at 19:51
  • That's very likely. IntelliJ IDEA users haven't reported that kind of problem... I don't know if a bug report has been filed, who to report the bug to (Eclipse or Play?) and if it's even worth it to file one. Anyway, thank you very much for your help, I wouldn't have found out myself! – Danish Ashfaq Sep 07 '14 at 11:49
  • ...Wow...mind blowing.. So the issue with Eclipse IDE – MADHAIYAN M Nov 05 '14 at 07:24
5

Create getters/setters for your data model. It has solved my problem.

abbas
  • 6,453
  • 2
  • 40
  • 36
  • This is actually the correct answer. The error occurs because the scala compiler adds default getters and setters which are not being created when compiling it with eclipse. – Tom Sep 25 '16 at 09:15
  • Even today, I believe this is the correct answer, had exactly the same problem. Creating getters and setters solves the issue. – Ilhicas Jan 23 '17 at 12:44
2

In your code you have :

Users u = Form.form(Users.class).bindFromRequest().get(); 

Try with this instead :

Users user = new Users();
Form <Users> u = Form.form(Users.class).fill(user).bindFromRequest();

EDIT :

May be the problem is the input types you're using. Try to generate your form like this :

@form(routes.Backend.createUser()) {

            <label>First Name:</label> @inputText(userForm("first_name")) <br />
            <label>Last Name:</label> @inputText(userForm("first_name")) <br />
            <label>Email:</label> @inputText(userForm("email")) <br />
            <label>Pin:</label> @inputText(userForm("pin")) <br />
            <label>Status:</label> @inputText(userForm("status")) <br />
            <label>Is Guest:</label> @checkbox(userForm("is_guest")) <br />
            <input type="submit" value="Create user" />
    }

Then in User Entity : try to change all columns type to String

@Entity
public class Users extends Model {

// Database columns
@Id
public int user_id;

public String first_name;
public String last_name;
public String email;
public String pin;
public String status;
public String is_guest;

}

In your controller :

public class Backend extends Controller {

  public static Result createUser() {
    Form <Users> userForm = Form.form(Users.class).bindFromRequest();
    Users user = userForm .get();
    user.save();
}
}  
blackbishop
  • 30,945
  • 11
  • 55
  • 76
  • I use the ".get()" method at the end, which is supposed to return an object of type "Users". This is exactly what is done in the Play! Framework for Java Developers introduction video : https://www.youtube.com/watch?v=bLrmnjPQsZc – Danish Ashfaq Aug 18 '14 at 14:54
  • What is "userForm" in your code? I checked on play's documentation but there is nothing on it... I just know that it's a parameter passed to the template, like @(userForm: Form[Users]), but what is it exactly? – Danish Ashfaq Aug 19 '14 at 07:41
  • please see this link : https://www.playframework.com/documentation/2.2.x/ScalaForms – blackbishop Aug 19 '14 at 07:51
  • Ok, so I assumed it was just a simple Form userForm = Form.form(Users.class); that I passed to my template. And I still cannot get the auto-binding working... The generated form is fine, everything is OK when I manually check the values. – Danish Ashfaq Aug 19 '14 at 07:51
  • here's a good [post](http://stackoverflow.com/questions/13688668/form-values-are-null-in-post-request-in-play-framework#answer-13691975) – blackbishop Aug 19 '14 at 07:56
  • Thanks for the info. I was able to get autobinding working on another test project, following exactly the instructions on the video tutorial. I think that my issue might be related to the fact that I have a more complex database than the simpler one used in the example. I guess I'm going to bind the values manually for the moment, and I'll look into it later. Thanks for your help :) – Danish Ashfaq Aug 19 '14 at 09:01
1

There is absolutely no link between the binding and your database. Do not follow @blackbishop's advice telling you to change all the fields of your model to String. That's a very bad idea, if there are different types, there is a reason...

Moreover, Ebean (supposing you're using it) or JPA generate database column types according to your Java properties type. Why would you store a 0 or a 1 in a varchar column ?

Follow these rules :

  1. Use a singular name for your models (User instead of Users)
  2. Always use camel case for your properties, no underscores (firstName instead of first_name, lastName instead of last_name...)
  3. Check errors before getting the value after binding

That should give you this :

public static Result createUser() {
    Form<User> form = Form.form(User.class).bindFromRequest();
    if (form.hasErrors()) {
        // Do what you have to do (i.e. : redirect to the form with a flash message)
    }
    User u = form.get();
    u.save();
    return redirect(routes.Backend.allUsers());
}

By the way, in your testing lines, the user_id is correctly generated because you have no validation rules and this data comes from the database, not the form.

c4k
  • 4,270
  • 4
  • 40
  • 65
  • I've always used these rules, but for this project, if I name my model "User", the database name will be "user", which is a reserved SQL name... Anyway, I'll try using camelCase and report back. Thanks! – Danish Ashfaq Aug 20 '14 at 10:36
  • I tried with camelCase... without success. I really have no idea what's happening, I still get the same result :/ – Danish Ashfaq Aug 20 '14 at 11:06
  • What's really bothering me is that if I print the content of the form (form.toString()), I get this : `Form(of=class models.system.Users, data={firstName=Bob, lastName=Smith, pin=567, isGuest=on, email=bob@smith.com, status=active}, value=Some(models.system.Users@5d482b), errors={})`, which clearly shows that the form.get() method doesn't work for me... – Danish Ashfaq Aug 20 '14 at 11:42
  • For `user` being a reserved word in MySQL, simply use `@Table(name = "users")` in your model class. What is inserted in the database after you submit the form ? – c4k Aug 20 '14 at 14:09
  • Yeah, I'll use the @Table annotation, it's a better solution. After I submit the form, "null" is inserted. I might have a lead: check my edit! – Danish Ashfaq Aug 20 '14 at 15:26
1

I solved this by Adding Setters and Getters. If you have Entity/Model class you should add setters and getters. If you have FormData classes add setters and getters for it as well.

So when you call

Form<YourFormData> formData =       Form.form(YourFormData.class).bindFromRequest(); 
 YourFormData formData = formData.get(); 

Your formData will now have all the values set. Hope this helps!

Shirish Singh
  • 797
  • 7
  • 17
0

I know this post has an accepted answer but wanted to post my experience with this issue.

I had the same issue and performed all the steps mentioned in the answer above i.e. Eclipse build automatically and then clean and compile through the activator. However, this did not work. I tried many different ways and even created a project from scratch without creating any Eclipse dependencies. Still, it did not work.

Then, I looked at my model and decided to try by changing the case of my property names, and then voila! It worked!

Here is what my model looked like BEFORE:

public class Test {
 public String FirstName;
 public String LastName;}

Now, I changed it to look like this:

public class Test {
  public String firstName;
  public String lastName;}

Just wanted to point this out since it is not obvious and I am coming from .Net

dotnetster
  • 1,601
  • 1
  • 16
  • 19