10

PHP から Snap w/Heist へのサイトの移植に取り組んでいます。Digestive Functor をうまく使用するためにいくつかの単純なフォームを移植しましたが、今度はサブフォームの使用を必要とするトリッキーなフォームを実行する必要があります。

このアプリケーションは、小売店向けのチラシの作成を管理するため、実行する必要があるタスクの 1 つは、広告サイズを追加し、印刷されるチラシの物理的な寸法を定義することです。サイズは、ページの種類 (チラシの所有者が設定可能) とその方向 (管理者のみが制御可能) によって異なります。

PHPバージョンでフォームがどのように見えるか

このフォームは最低でも 3 つのセルを持つことが保証されており、おそらく 9 つのセル (上記の PHP バージョンの図のように) を持つことになりますが、理論的には無制限の数を持つことができます。

これまでのところ、寸法サブフォームについて得たものは次のとおりです。

data AdDimensions = AdDimensions
    { sizeId :: Int64
    , layoutId :: Int64
    , dimensions :: Maybe String
    }

adDimensionsForm :: Monad m => AdDimensions -> Form Text m AdDimensions
adDimensionsForm d = AdDimensions
    <$> "size_id" .: stringRead "Must be a number" (Just $ sizeId d)
    <*> "layout_id" .: stringRead "Must be a number" (Just $ layoutId d)
    <*> "dimensions" .: opionalString (dimensions d)

フォームの定義が正しくないように感じます (もしかしたら、ここで完全に間違った考えを持っているのでしょうか?)。 新しい広告サイズの size_id/layout_id のすべての可能な組み合わせのリストを取得するクエリを実行するときにデータベースから戻ってくると null になるため、 であるAdDimensions.dimensions必要がありますが、同様のクエリからは null にはなりません。Maybe String編集フォームの作成時に実行されます。フィールド自体は必須です (データベースでad_dimensions.dimensionsは に設定さnot nullれています)。

ここから先、親フォームにサブフォームのリストがあることを伝える場所や、Heist を使用してそれらをレンダリングする方法がわかりません。

4

2 に答える 2

5

私はかなり前に、このための特別なコンビネータを消化ファンクター 0.2 用に書きました。これは、フィールドを動的に追加および削除できるようにするJavaScript コードを含む、非常に機能の豊富なソリューションでした。そのコードは、Chris と私が formlets パッケージ用に行ったはるかに初期の実装に基づいていましたが、最終的に消化機能が取って代わりました。この関数は、ダイジェスティブ ファンクターが 0.3 で取得した新しい API で動作するように移植されたことはありません。

この問題は扱いにくく、微妙なコーナー ケースがいくつかあるため、コードを確認する時間をとることをお勧めします。Jasper は、現在のバージョンの消化機能関数へのコードの適切な移植をおそらく受け入れると思います。まだ誰もその仕事をしていないというだけです。

編集: これは、最新の消化機能のために現在行われています。listOf関数を参照してください。

于 2012-10-08T14:00:46.100 に答える
2

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 の依存関係を避けるために自分で作成することになったので、提供されたスプライス (ボタン型要素の属性) を使用していませaddControlremoveControl

複雑なフォーム

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
于 2013-07-23T20:10:57.203 に答える