user1361914
user1361914

Reputation: 411

creating dynamic html form

Trying to learn how to create a dynamic html form. The elements would be defined in the database. Once page is created, the post back data will be handled by controller to insert the data into the database.

I am using playframework 1.2.4

would appreciate any guideline/ helpful links

I can build the page if i know what the elements are and can pull data out for select list from a database table when i call render(param object) from my controller and access the object in my view.

My hack so far: Created a table with following columns

rid
HTMLElementType
ElementName
HTMLElementOptions [if the element type is select]
HTMLDefaultValue [default value for select like 'select a value from below']
HTMLElementEnabled

Created a model

package models;

import play.*;
import play.db.jpa.*;
import play.data.validation.*;

import javax.persistence.*;
import java.util.*;

@javax.persistence.Entity
@Table(name="mytable")
public class DynameForm extends Model{
     public String HTMLElementType;
     public String ElementName;
     public String HTMLElementOptions;
     public String HTMLDefaultValue;
     public String HTMLElementEnabled;
}

In my view i loop through checking if it a <select> and if so put in an empty option. But not sure if that is right way to go about. In addition in my view i also have to check if it is not then i have to put in <input type=> and build the complete tag

In addition how would I enforce validation on certain fields that are required like example last name/ ssn/ etc? I can alter my table to have a column IsRequired which could help me

Not sure what the right way even is to capture the data on post back

Upvotes: 4

Views: 1360

Answers (1)

Nasir
Nasir

Reputation: 2984

basically the problem is to generate an html form. You appear to have figured out your model. What you are missing is a view. I did something like the following once, to generate a model for a simpleDB model.

I provide a list of fields, and UI is generated based on the fields. I only had text fields, and only wanted 2 cases (visible and invisible) fields. Your usecase may require more complexity, so you can adapt as necessary.

dish.fields contains the fields with the relevant metadata. Any special things like, requires validation, or isRequired, you will have to provide that information to the view, so it can render the field in appropriate manner.

Simplest way to model it would be to begin with an HTML form, and start generalizing it one field at a time.

  #{list items:dish.fields, as:'f'}
    #{field 'f'}
    #{if f.display } 
    <div class="control-group">
        <label class="control-label"> &{f.name} </label>
        <div class="controls">
            <input type="text" class="input-xlarge" placeholder="&{f.name}" name="${dish.getFieldId(f)}" value="${dish.getValue(f)}" ></input>
        </div>
    </div>
    #{/if}
    #{else}
    <input type="hidden" class="input-xlarge" placeholder="&{f.name}" name="${dish.getFieldId(f)}" value="${dish.getValue(f)}" ></input>
    #{/else}
    #{/field}

    #{/list}    
    #{else}
    No fields
    #{/else}

I had to define my own fields, but you should be able to get the idea.

You will probably have to have a bunch of different input types for different use cases, so start with simple and generalize as you go on. You can have a look at the CRUD module implementation as well.

my DisplayAttribute class (metadata for the fields) looks something like the following. You can use it as a starting point.

public class DisplayAttribute {
    public Boolean display = Boolean.TRUE;
    public String type = "";
    public String  name;

    public DisplayAttribute(String name){
        this.name = name;
        this.display = Boolean.TRUE;
    }

    public DisplayAttribute(String name, Boolean display){
        this.name = name;
        this.display = display;
    }
... overridden equals and hash
}

edit How do the fields get rendered? Controller passes the meta data(DisplayAttribute) to the view, in this case, meta data only contains name of the field and wether it is displayable or not.

Model

Here model contains the fields to render, but you could just as easily retrieve these from a database. My model is generic because I realized I kept on doing the same things over and over again for multiple models.

I implement my own interface which gives me getFields method. I also maintain two maps, so that given an attribute, I can get its DisplayAttribute, and given a DisplayAttribute I get its name. I call methods of this model from the view when needed.

    public class GenericSimpleDBModel implements SimpleDBModel {

        public static AmazonSimpleDB sdb = null;
        private static final String bracketRemovalPattern = "(^.*?\\[|\\]\\s*$)";
        private Map<DisplayAttribute, Set<String>> data = new TreeMap<DisplayAttribute, Set<String>>(new UuidComparator());
        private Map<String, DisplayAttribute> attributeCache = new HashMap<String, DisplayAttribute>();
        protected final String DOMAIN_NAME;

        public GenericSimpleDBModel() {
            initialize(getFields());
            this.DOMAIN_NAME = "dishes";
        }

    protected void initialize(String[] fields) {
        data = new TreeMap<DisplayAttribute, Set<String>>(new UuidComparator());
        attributeCache = new HashMap<String, DisplayAttribute>();
        for (String f : fields) {
//            if (f.equals(getUUIDField()) || f.equals(getIntegrityField())) {
            if (f.endsWith("uuid") || f.endsWith("integrity")) {
                setValue(f, "", Boolean.FALSE);
            } else {
                setValue(f, "", Boolean.TRUE);
            }
        }
    }
   protected void initialize(Set<DisplayAttribute> fields) {
        data = new TreeMap<DisplayAttribute, Set<String>>(new UuidComparator());
        attributeCache = new HashMap<String, DisplayAttribute>();
        for (DisplayAttribute atr : fields) {
            setValue(atr.name, "");
        }
    }
... more methods
}

Upvotes: 2

Related Questions