Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

プレゼン、ボランティアコーチします!

勉強会でスピーカーをやりたいけど、プレゼンが初めて、苦手という方に無償でコーチします!資料レビューや録画リハへアドバイスします。Twitter@jyukutyoまでメンションでもDMでも。

私はデブサミやJJUG CCCなど200人規模で登壇経験ありです。海外での登壇も短いながらあり。デブサミ2017では公募スピーカー1位でした!

Introducing Apache Wicketの超意訳(11)

http://www.theserverside.com/tt/articles/article.tss?l=IntroducingApacheWicket

Next, let's look at the EditContact page.
This is where we'll introduce the form to create a new contact as well as edit an existing contact.
We'll start with the Java code:

次にEditContactページを見てみましょう。
すでにあるコンタクトを編集することはもちろん、新しいコンタクトを作成するためのフォームをここに導入します。
Javaコードから始めましょう。

public class EditContact extends BasePage {

    public EditContact() {
        setModel(new CompoundPropertyModel(new LoadableDetachableModel() {
            protected Object load() {
                return new Contact();
            }
        }));
        init();
    }

    public EditContact(final Long contactId) {
        setModel(new CompoundPropertyModel(new LoadableDetachableModel() {
            protected Object load() {
                return WicketApplication.get().getContactDao().get(contactId);
            }
        }));
        init();
    }

    private void init() {
        add(new FeedbackPanel("feedback"));
        add(new ContactForm("form", getModel()));
    }

    private class ContactForm extends Form {

        public ContactForm(String id, IModel m) {
            super(id, m);

            TextField firstName = new TextField("firstName");
            firstName.setRequired(true);
            firstName.add(StringValidator.maximumLength(15));
            add(firstName);

            TextField lastName = new TextField("lastName");
            lastName.setRequired(true);
            lastName.add(StringValidator.maximumLength(20));
            add(lastName);

            TextField email = new TextField("email");
            email.add(StringValidator.maximumLength(150));
            email.add(EmailAddressValidator.getInstance());
            add(email);

            TextArea notes = new TextArea("notes");
            notes.add(StringValidator.maximumLength(500));
            add(notes);

            DropDownChoice group = new DropDownChoice("group");
            group.setChoices(new AbstractReadOnlyModel() {
                public Object getObject() {
                    List<String> l = new ArrayList<String>(3);
                    l.add("Friend");
                    l.add("Co-Worker");
                    l.add("Nemesis");
                    return l;
                }
            });
            add(group);

            add(new Button("save") {
                public void onSubmit() {
                    Contact c = (Contact) getForm().getModelObject();
                    WicketApplication.get().getContactDao().save(c);
                    setResponsePage(ListContacts.class);
                }
            });
            add(new Button("cancel") {
                public void onSubmit() {
                    setResponsePage(ListContacts.class);
                }
            }.setDefaultFormProcessing(false));
        }
    }
}

The EditContact class has two constructors supporting the two use cases for the class.
The first constructor doesn't take any arguments and is used to create new instances of Contact.
We're still using a LoadableDetachableModel to return a new Contact even though we're not getting it from the database to ensure the CompoundPropertyModel doesn't serialize the Contact when the page is stored in the PageMap.
This is also a good place to set any defaults on newly created objects, like a default group.


The second constructor takes the contact id, used to load the desired Contact for editing.
Once the constructor populates the model, the init() method is called.


The init() method simply adds a FeedbackPanel and the form object.
FeedbackPanels are Wicket components designed to relay messages back to the user.
For instance, if the user doesn't enter a valid first name, the error message is displayed in the panel.
Like other panels, we need to reflect it in the markup:

EditContactクラスはこのクラスへの2つのユースケースをサポートするようにコンストラクタが2つあります。
1つめのコンストラクタは引数をまったく取らず、Contactの新しいインスタンスを生成するために使います。
たとえPageMapにページを保存する際にCompoundPropertyModelがContactをシリアライズできないという確証があり、データベースからContactを取得していないとしても、やはりLoadableDetachableModelを新しいContactを返すために使っています。
これはまたデフォルトのグループのような新しく生成したオブジェクトに対してあらゆるデフォルト値をセットする絶好の場所です。


2つめのコンストラクタはコンタクトIDを引数に取ります。編集対象のContactをロードするために使います。
FeedbackPanelはユーザにメッセージを伝えるよう設計されたWicketコンポーネントです。
たとえば、もしユーザが名前を正しく入力しなかったら、パネルにエラーメッセージを表示します。
ほかのパネルのように、マークアップにそれを反映する必要があります。

<span wicket:id="feedback"></span>

Any validation errors are presented in the body of the SPAN element.
You can choose to style the feedback, but that will be the subject of a future article.


Next, the form class is defined and populated with components.
Let's look at the TextField for the Contact's first name:

あらゆるバリデーションエラーはSPAN要素のボディに表示します。
フィードバックのスタイルを選べますが、それは今後の記事の話題でしょう。


次に、フォームクラスを定義し、コンポーネントに投入します。
Contactの名前に対応するTextFieldを見てみましょう。

TextField firstName = new TextField("firstName");
firstName.setRequired(true);
firstName.add(StringValidator.maximumLength(15));
add(firstName);

Since we used a CompoundPropertyModel as the model, the TextField will populate itself with the value stored in the Contact's firstName property.
Next we set the field to be required and have a maximum length of 15 characters.
Finally, the component is added to the parent form.
This is repeated for the various form components, sometimes with different validators, but the core pattern is the same.
Where this differs is the DropDownChoice.


The DropDownChoice component is used to display the HTML Select element.
In our example, the choices available in the select list are set using the setChoices(...) method:

モデルとしてCompoundPropertyModelを使っているので、ContactのfirstNameプロパティになる値を保持してTextFieldは自身を投入します。
次に必要なフィールドをセットし、最大長を15文字にします。
最後に、コンポーネントを親フォームに追加します。
これをさまざまなフォームコンポーネントに繰り返します。時には異なるバリデータがあるときもありますが、核となるパターンは同じです。
この違いがあるのはDropDownChoiceです。


DropDownChoiceコンポーネントはHTMLのSelect要素を表示するために使います。
例ではselectリストでの選択肢はsetChoices(...)メソッドを使ってセットします。

DropDownChoice group = new DropDownChoice("group");
group.setChoices(new AbstractReadOnlyModel() {
    public Object getObject() {
        List<String> l = new ArrayList<String>(3);
        l.add("Friend");
        l.add("Co-Worker");
        l.add("Nemesis");
        return l;
    }
});

The AbstractReadOnlyModel, another implementation of IModel, returns a list of Strings used to create options for the select list.
An approximation of the generated HTML is shown below:

AbstractReadOnlyModelはIModelのもう1つの実装ですが、Stringのリストを返し、それがselectリストのオプションとなります。
生成するHTMLはおよそ下のようなものです。

<select>
    <option value="Friend">Friend</option>
    <option value="Co-Worker">Co-Worker</option>
    <option value="Nemesis">Nemesis</option>
</select>

We're using simple Strings in this example, but the DropDownChoice also supports rich objects with the help of the IChoiceRenderer.
Suppose groups were represented as an object:

この例では単純なStringを使っていますが、DropDownChoiceはIChoiceRendererの助けを借りてリッチなオブジェクトもサポートしています。
グループがオブジェクトとして表現されると仮定しましょう。

public class Group {
    private String name;

    public Group(String name) {
        setName(name);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}  

In this case, our DropDownChoice component looks like:

この場合、DropDownChoiceコンポーネントはこのようになります。

DropDownChoice group = new DropDownChoice("group");
group.setChoices(new AbstractReadOnlyModel() {
    public Object getObject() {
        List<Group> l = new ArrayList<Group>(3);
        l.add(new Group("Friend"));
        l.add(new Group("Co-Worker"));
        l.add(new Group("Nemesis"));
        return l;
    }
});
group.setChoiceRenderer(new IChoiceRenderer() {
    public Object getDisplayValue(Object o) {
        Group g = (Group) o;
        return g.getName();
    }
    public String getIdValue(Object o) {
        Group g = (Group) o;
        return g.getName();
    }
});

The IChoiceRenderer allows the developer to determine how the displayed value and and ID values are created for the option tags.
If you've used some other web frameworks and fought with select lists, you'll find this method to be much easier to use.

IChoiceRendererによって開発者はoptionタグに表示する値とIDの値を生成する方法を決定できます。
もしほかのWebフレームワークを使ってselectリストと悪戦苦闘したことがあるなら、このメソッドはより簡単に使えると気付くでしょう。