listOf
機能(質問が最初に尋ねられた/回答されたときはありませんでした)を使用して、これがその方法です。これには、リストのタイプを表すフォームがフォームレットである 2 つのフォームが必要です。
data Thing = Thing { name: Text, properties: [(Text, Text)] }
thingForm :: Monad m => Maybe Thing -> Form Text m Thing
thingForm p = Thing
<$> "name" .: text (name <$> p)
<*> "properties" .: listOf propertyForm (properties <$> p)
propertyForm :: Monad m => Maybe (Text, Text) -> Form Text m (Text, Text)
propertyForm p = ( , )
<$> "name" .: text (fst <$> p)
<*> "value" .: text (snd <$> p)
シンプルなフォーム
アイテムの単純なリストがある場合、消化機能を備えたヘイストはこれに対していくつかのスプライスを定義しますが、特にフォームがテーブル内にある場合は、無効なマークアップになる可能性があります。
<label>Name <dfInputText ref="formname" /></label>
<fieldset>
<legend>Properties</legend>
<dfInputList ref="codes"><ul>
<dfListItem><li itemAttrs><dfLabel ref="name">Name <dfInputText ref="name" /></dfLabel>
<dfLabel ref="code">Value <dfInputText ref="value" required /></dfLabel>
<input type="button" name="remove" value="Remove" /></li></dfListItem>
</ul>
<input type="button" name="add" value="Add another property" /></dfInputList>
</fieldset>
jQuery 依存関係を持つフォームからの要素の追加と削除を制御するために、digestiveFunctors によって提供される JavaScriptがあります。jQuery の依存関係を避けるために自分で作成することになったので、提供されたスプライス (ボタン型要素の属性) を使用していませaddControl
んremoveControl
。
複雑なフォーム
OP のフォームは、ラベルが動的であり (たとえば、データベースから取得される)、複雑なテーブル レイアウトで使用する必要があるため、消化機能を備えたヘイストによって提供されるスプライスを利用できません。これは、2 つの追加タスクを実行する必要があることを意味します。
マークアップを手動で生成する
digestive-functors-heist スプライスによって生成されたマークアップをまだ見ていない場合は、フォームを正しく処理できるようにするために何を生成する必要があるかを正確に理解するために、最初にマークアップを確認することをお勧めします。
動的フォーム (たとえば、ユーザーがその場で新しいアイテムを追加または削除できるフォーム) の場合、非表示のインデックスフィールドが必要になります。
<input type='hidden' name='formname.fieldname.indices' value='0,1,2,3' />
- formname = 実行時にフォームに付けた名前
runForm
- fieldname = メイン フォームのリスト フィールドの名前 (サブフォームを使用している場合は、必要に応じて調整します)。この例では、「プロパティ」という名前になります。
- 値 = フォームの送信時に処理する必要があるサブフォームのインデックスを表す数字のコンマ区切りリスト
リストからアイテムの 1 つが削除されるか、新しいアイテムが追加された場合、このリストを調整する必要があります。そうしないと、新しいアイテムが完全に無視され、削除されたアイテムがリストに残ります。この手順は、OP のような静的フォームには不要です。
スプライスの書き方を既に知っていれば、フォームの残りの部分を生成するのは非常に簡単です。必要に応じてデータをチャンクアップし (groupBy、chunksOf など)、スプライス経由で送信します。
digestive-splices-heist によって生成されたマークアップを見てまだ判断できない場合は、サブフォームのインデックス値を各サブフォームのフィールドの一部として挿入する必要があります。サブフォームのリストの最初のフィールドの出力 HTML は次のようになります。
<input type='text' name='formname.properties.0.name' value='Foo' />
<input type='text' name='formname.properties.0.value' value='Bar' />
(ヒント: 0 から始まる無限リストと一緒にリストを圧縮してください)
エラー処理時にフォームからデータを引き出す
(このコードが実際に書かれたとおりにコンパイルできない場合は、前もってお詫びしますが、プロセスを示していることを願っています)
この部分は、他の部分ほど単純ではありません。このためには、消化機能の内部を掘り下げる必要があります。基本的に、データを取り戻して Thing に入力するために、digestive-functors-heist と同じ関数を使用します。必要な機能は次のlistSubViews
とおりです。
-- where `v` is the view returned by `runForm`
-- the return type will be `[View v]`, in our example `v` will be `Text`
viewList = listSubViews "properties" v
静的フォームの場合、これは、このリストをデータのリストと一緒に圧縮するのと同じくらい簡単です。
let x = zipWith (curry updatePropertyData) xs viewList
そして updatePropertyData 関数は、次の関数を使用してビューから情報を引き出してレコードを更新する必要がありますfileInputRead
。
updatePropertyData :: (Text, Text) -> View Text -> (Text, Text)
updatePropertyData x v =
let
-- pull the field information we want out of the subview
-- this is a `Maybe Text
val = fieldInputRead "value" v
in
-- update the tuple
maybe x ((fst x, )) val