実行時にデータベースからフォームを生成する動的フォームを作成したいのですが、実行時に提供されるフィールドを使用して (JPA を使用して) データベース サーバーにデータ ストレージ テーブルを作成したいと考えています。私は自分の能力ですべてを試しましたが、現時点では助けが必要です. 私の問題がどこにあり、正確に何を達成したいかを示すために、できるだけ説明します。
ここで StackOverflow を検索したところ、primefaces で動的フォームを作成するには、Primefaces-Extensions DynaForm を使用する必要があることがわかりました。primefaces-extensions ショーケースの例から葉を借りて、以下のように正しく実装することができました。
ダイナフォームコントローラー
@PostConstruct
protected void initialize() {
model = new DynaFormModel();
// add rows, labels and editable controls
// set relationship between label and editable controls to support outputLabel with "for" attribute
// 1. row
DynaFormRow row = model.createRegularRow();
DynaFormLabel label11 = row.addLabel("Author");
DynaFormControl control12 = row.addControl(new BookProperty("OurAuthor", true), "input");
label11.setForControl(control12);
DynaFormLabel label13 = row.addLabel("ISBN");
DynaFormControl control14 = row.addControl(new BookProperty("ISBN", true), "input");
label13.setForControl(control14);
// 2. row
row = model.createRegularRow();
DynaFormLabel label21 = row.addLabel("Title");
DynaFormControl control22 = row.addControl(new BookProperty("Title", false), "input", 3, 1);
label21.setForControl(control22);
// 3. row
row = model.createRegularRow();
DynaFormLabel label31 = row.addLabel("Publisher");
DynaFormControl control32 = row.addControl(new BookProperty("Publisher", false), "input");
label31.setForControl(control32);
DynaFormLabel label33 = row.addLabel("Published on");
DynaFormControl control34 = row.addControl(new BookProperty("Published on", false), "calendar");
label33.setForControl(control34);
// 4. row
row = model.createRegularRow();
DynaFormLabel label41 = row.addLabel("Language");
DynaFormControl control42 = row.addControl(new BookProperty("Language", false), "select");
label41.setForControl(control42);
DynaFormLabel label43 = row.addLabel("Description", 1, 2);
DynaFormControl control44 = row.addControl(new BookProperty("Description", false), "textarea", 1, 2);
label43.setForControl(control44);
// 5. row
row = model.createRegularRow();
DynaFormLabel label51 = row.addLabel("Rating");
DynaFormControl control52 = row.addControl(new BookProperty("Rating", 3, true), "rating");
label51.setForControl(control52);
}
public DynaFormModel getModel() {
return model;
}
public List<BookProperty> getBookProperties() {
if (model == null) {
return null;
}
List<BookProperty> bookProperties = new ArrayList<BookProperty>();
for (DynaFormControl dynaFormControl : model.getControls()) {
bookProperties.add((BookProperty) dynaFormControl.getData());
}
return bookProperties;
}
public String submitForm() {
FacesMessage.Severity sev = FacesContext.getCurrentInstance().getMaximumSeverity();
boolean hasErrors = (sev != null && (FacesMessage.SEVERITY_ERROR.compareTo(sev) >= 0));
RequestContext requestContext = RequestContext.getCurrentInstance();
requestContext.addCallbackParam("isValid", !hasErrors);
return null;
}
DynaForm .xhtml 定義
<h:form id="mainForm">
<h:panelGroup id="dynaFormGroup">
<p:messages id="messages" showSummary="true"/>
<pe:dynaForm id="dynaForm" value="#{dynaFormController.model}" var="data">
<pe:dynaFormControl type="input" for="txt">
<p:inputText id="txt" value="#{data.value}" required="#{data.required}" />
</pe:dynaFormControl>
<pe:dynaFormControl type="calendar" for="cal" styleClass="calendar">
<p:calendar id="cal" value="#{data.value}" required="#{data.required}" showOn="button"/>
</pe:dynaFormControl>
<pe:dynaFormControl type="select" for="sel" styleClass="select">
<p:selectOneMenu id="sel" value="#{data.value}" required="#{data.required}">
<f:selectItems value="#{dynaFormController.languages}"/>
</p:selectOneMenu>
</pe:dynaFormControl>
<pe:dynaFormControl type="textarea" for="tarea">
<p:inputTextarea id="tarea" value="#{data.value}" required="#{data.required}" autoResize="false"/>
</pe:dynaFormControl>
<pe:dynaFormControl type="rating" for="rat">
<p:rating id="rat" value="#{data.value}" required="#{data.required}"/>
</pe:dynaFormControl>
<f:facet name="buttonBar">
<p:commandButton value="Submit" action="#{dynaFormController.submitForm}"
process="dynaForm" update=":mainForm:dynaFormGroup :mainForm:inputValues"
oncomplete="handleComplete(xhr, status, args)"/>
<p:commandButton type="reset" value="Reset" style="margin-left: 5px;"/>
</f:facet>
</pe:dynaForm>
</h:panelGroup>
<p:dialog header="Input values" widgetVar="inputValuesWidget">
<p:dataList id="inputValues" value="#{dynaFormController.bookProperties}" var="bookProperty"
style="margin:10px;">
<h:outputText value="#{bookProperty.name}: #{bookProperty.formattedValue}"
style="margin-right: 10px;"/>
</p:dataList>
</p:dialog>
<h:outputScript id="dynaFormScript" target="body">
/* <![CDATA[ */
function handleComplete(xhr, status, args) {
if(args && args.isValid) {
PF('inputValuesWidget').show();
} else {
PF('inputValuesWidget').hide();
}
}
/* ]]> */
</h:outputScript>
<h:outputStylesheet id="dynaFormCSS">
/* note: trick with colspan is needed for IE8 */
.pe-dynaform-cell input,
.pe-dynaform-cell textarea,
.pe-dynaform-cell[colspan="1"] input,
.pe-dynaform-cell[colspan="1"] textarea {
width: 150px;
}
/* note: trick with colspan is needed for IE8 */
.pe-dynaform-cell.calendar input,
.pe-dynaform-cell[colspan="1"].calendar input {
width: 120px;
}
.pe-dynaform-cell.select .ui-selectonemenu {
width: 157px !important;
}
.pe-dynaform-cell.select .ui-selectonemenu .ui-selectonemenu-label {
width: 130px !important;
}
</h:outputStylesheet>
</h:form>
私はこれから変換しています: . DynaFormController を見ると、すべての DynaForm 定義が行われている場所です。たとえば、フォームに新しいフィールドを追加する場合、.xhtml を変更する必要はありませんが、DynaFormController に移動して定義する必要があります。
私が望んでいることと私がやろうとしたことは、フォーム定義をデータベースに保存することです。これにより、DynaFormCOntroller で新しいフィールドを定義する代わりに、実行時にデータベースにフィールドとその定義を追加し、DynaFormController が選択しますそれは自動的に。つまり、DynaFormController は実行時にデータベースから DynaForm を構築します。
DynaForm フィールドとその定義を格納するデータベース テーブルがあります。
ここから DynaForm 定義をキャプチャし
ます。任意の数のフィールドとその定義を作成でき、それらはデータベース テーブルに格納されます。以下に示すように。
注: 追加した rowNumber フィールドはレイアウト用です。以下の生成されたフォームを見ると、firstName と secondName が同じ行にあることがわかります。上記のフォーム定義と比較すると、firstName と secondName の両方が rowNumber 4 にあることがわかります。
すべての DynaForm 定義がデータベースに格納された後、データベースから以下のフォームを生成します。
データベースから生成された DynaForm
上記のフォームで [送信] をクリックすると、データベースにデータを格納するテーブルが作成されます。
データベースから DynaForm を生成する DynaFormController を次に示します。
@ManagedBean
@ViewScoped
public class DynaFormBacking implements Serializable{
private @Inject
DynaFormAttributeFacade dynaFormAttributeFacade;
private List<DynaFormAttribute> dynaFormAttributeList;
private DynaFormModel model;
private @Inject TblPersonFacade personFacade;
/**
* Creates a new instance of DynaFormBacking
*/
@PostConstruct
public void init(){
model = new DynaFormModel();
DynaFormRow row;
DynaFormLabel label;
DynaFormControl control;
int i = 0; //loop incrementer
//Number of form attributes.
int listSize = getDynaFormAttributeList().size();
for (; i < listSize;) {
//Get form attributes that belong to the same row
List<DynaFormAttribute> rowDynaFormList = dynaFormAttributeFacade.getEntityRowsGivenColumnValue(new DynaFormAttribute(), "rowNumber", getDynaFormAttributeList().get(i).getRowNumber());
//create a new row
row = model.createRegularRow();
//place labels and controls on the newly created row. If we have multiple fields on the same row, this loop will place them
for (int r = 0; r < rowDynaFormList.size(); r++) {
//place label and control on the row (This is for a single form attribute)
label = row.addLabel(rowDynaFormList.get(r).getLabelName(), rowDynaFormList.get(r).getLabelColspan(), rowDynaFormList.get(r).getLabelRowspan());
control = row.addControl(new Person(false), rowDynaFormList.get(r).getControlName(), rowDynaFormList.get(r).getControlColspan(), rowDynaFormList.get(r).getControlRowspan());
label.setForControl(control);
//increment to keep track of the number of form attributes
i++;
}
}
}
public DynaFormBacking() {
}
public DynaFormModel getModel() {
return model;
}
//This list picks the form fields and their definitions from the database
public List<DynaFormAttribute> getDynaFormAttributeList() {
dynaFormAttributeList = dynaFormAttributeFacade.findAll();
return dynaFormAttributeList;
}
public String submitForm() {
FacesMessage.Severity sev = FacesContext.getCurrentInstance().getMaximumSeverity();
boolean hasErrors = (sev != null && (FacesMessage.SEVERITY_ERROR.compareTo(sev) >= 0));
RequestContext requestContext = RequestContext.getCurrentInstance();
requestContext.addCallbackParam("isValid", !hasErrors);
TblPerson person = new TblPerson();
person.setFirstName((String) getPersonList().get(0).getFormattedValue());
person.setSecondName((String) getPersonList().get(1).getFormattedValue());
person.setSex((String) getPersonList().get(2).getFormattedValue());
person.setDateOfBirth( (Date) getPersonList().get(3).getFormattedValue());
person.setPhone((String) getPersonList().get(4).getFormattedValue());
personFacade.create(person);
return null;
}
public List<Person> getPersonList() {
if (model == null) {
return null;
}
List<Person> personList = new ArrayList<Person>();
for (DynaFormControl dynaFormControl : model.getControls()) {
personList.add((Person) dynaFormControl.getData());
}
return personList;
}
}
上記のコントローラーで使用した getENtityRowsGivenColumnValue メソッドの実装を次に示します。
/**
* This method returns Entity rows of data specific to the given column
* value - return rows when given a column name and the column value
*/
public List<T> getEntityRowsGivenColumnValue(T entity, String columnName, Object columnValue) {
List<T> resultList;
javax.persistence.criteria.CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
javax.persistence.criteria.CriteriaQuery<T> cq = cb.createQuery(entityClass);
javax.persistence.criteria.Root<T> rt = cq.from(entityClass);
cq.select(rt).where(cb.equal(rt.get(columnName), columnValue));
javax.persistence.TypedQuery<T> q = getEntityManager().createQuery(cq);
try {
resultList = q.getResultList();
} catch (NoResultException ex) {
resultList = null;
}
return resultList;
}
上記のコントローラーで使用した Person クラスは次のとおりです。
@ManagedBean
@RequestScoped //Must be request scoped. otherwise, it won't work
public class Person implements Serializable{
private static final long serialVersionUID = 20120521L;
private Object value;
private boolean required;
/**
* Creates a new instance of Person
*/
public Person() {
}
public Person(boolean required) {
this.required = required;
}
public Person(Object value, boolean required) {
this.value = value;
this.required = required;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public boolean isRequired() {
return required;
}
public void setRequired(boolean required) {
this.required = required;
}
public Object getFormattedValue() {
if (value instanceof Date) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
return simpleDateFormat.format(value);
}
return value;
}
}
上記のコントローラーで使用した TblPerson エンティティ クラスは次のとおりです。
public class TblPerson implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id")
private Integer id;
@Size(max = 45)
@Column(name = "firstName")
private String firstName;
@Size(max = 45)
@Column(name = "secondName")
private String secondName;
@Size(max = 10)
@Column(name = "sex")
private String sex;
@Column(name = "dateOfBirth")
@Temporal(TemporalType.DATE)
private Date dateOfBirth;
// @Pattern(regexp="^\\(?(\\d{3})\\)?[- ]?(\\d{3})[- ]?(\\d{4})$", message="Invalid phone/fax format, should be as xxx-xxx-xxxx")//if the field contains phone or fax number consider using this annotation to enforce field validation
@Size(max = 45)
@Column(name = "phone")
private String phone;
public TblPerson() {
}
public TblPerson(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getSecondName() {
return secondName;
}
public void setSecondName(String secondName) {
this.secondName = secondName;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(Date dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof TblPerson)) {
return false;
}
TblPerson other = (TblPerson) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "ijmis.model.TblPerson[ id=" + id + " ]";
}
}
これは、コントローラーで使用した DynaFormAttribute エンティティ クラスです。
public class DynaFormAttribute implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id")
private Integer id;
@Size(max = 45)
@Column(name = "labelName")
private String labelName;
@Column(name = "labelColspan")
private Integer labelColspan;
@Column(name = "labelRowspan")
private Integer labelRowspan;
@Size(max = 45)
@Column(name = "controlName")
private String controlName;
@Column(name = "controlColspan")
private Integer controlColspan;
@Column(name = "controlRowspan")
private Integer controlRowspan;
@Column(name = "rowNumber")
private Integer rowNumber;
public DynaFormAttribute() {
}
public DynaFormAttribute(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLabelName() {
return labelName;
}
public void setLabelName(String labelName) {
this.labelName = labelName;
}
public Integer getLabelColspan() {
return labelColspan;
}
public void setLabelColspan(Integer labelColspan) {
this.labelColspan = labelColspan;
}
public Integer getLabelRowspan() {
return labelRowspan;
}
public void setLabelRowspan(Integer labelRowspan) {
this.labelRowspan = labelRowspan;
}
public String getControlName() {
return controlName;
}
public void setControlName(String controlName) {
this.controlName = controlName;
}
public Integer getControlColspan() {
return controlColspan;
}
public void setControlColspan(Integer controlColspan) {
this.controlColspan = controlColspan;
}
public Integer getControlRowspan() {
return controlRowspan;
}
public void setControlRowspan(Integer controlRowspan) {
this.controlRowspan = controlRowspan;
}
public Integer getRowNumber() {
return rowNumber;
}
public void setRowNumber(Integer rowNumber) {
this.rowNumber = rowNumber;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof DynaFormAttribute)) {
return false;
}
DynaFormAttribute other = (DynaFormAttribute) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "ijmis.model.DynaFormAttribute[ id=" + id + " ]";
}
}
これは私が助けを必要とするところです:これをすべて読んだ後、あなたは正確に何を尋ねようとしているのかを自分自身に尋ねているかもしれません. これは私が本当に助けが必要な場所です:
1. データ ストレージ:実行時にすべてのフォーム フィールドを生成しますが、データ ストレージ テーブルは静的に作成されます。逆に、実行時にフォーム フィールドを作成している場合でも、データ ストレージ テーブルにあるフィールドのみを作成するのが論理的です。データの整合性や制約を危険にさらすことなく、実行時にデータ ストレージ テーブルを動的に作成し、実行時に定義したフィールドを含むデータベース テーブルを作成するにはどうすればよいですか?たとえば、アプリケーションを初めてデプロイするときは、フォーム定義を作成してデータベースに保存し、データベースから動的フォームを生成しますが、実行時にフィールドのデータを保存するデータベース テーブルも作成します。 . これは、アプリケーションを初めてセットアップするときに一度実行する必要があります。(この質問の一部は、実行時にエンティティを作成し、それを db サーバーで実行して、実行時に実際のテーブルを作成するにはどうすればよいでしょうか?)
2.この実装では、ユーザーから新しいデータをキャプチャして、指定されたとおりに保存することしかできません。フィールド値をデータベースに送信する前に、フィールド値に対して何らかのロジックを実行したい場合はどうすればよいですか? また、フォームの読み込み時に、フォームの一部の値を自動的に提供する必要がある場合もあります。たとえば、従業員番号を生成し、新しいレコードがキャプチャされているときに読み取り専用フィールドとして表示したい場合があります。私の実装では、どうすればそれを達成できますか?
これは非常に長い質問ですが、あなたの親切でよく考えられた貢献に感謝します。あなたの貢献が大いに役立つことを約束します。