[英]Retrieving a list of choices for Digestive Functors from the database (Snap/Heist)
[英]Digestive Functors with a variable number of subforms (Snap/Heist)
我正在將網站從PHP移植到Snap with Heist。 我已經將一些簡單的表單成功地移植到了消化函子上,但是現在我不得不做一些棘手的表單,這些表單需要使用子表單。
該應用程序管理零售商店的傳單制作,因此需要完成的任務之一是添加廣告尺寸並在打印的傳單上定義其物理尺寸。 大小會因頁面類型(由傳單所有者配置)及其方向(只能由管理員控制)而有所不同。
保證此表單至少具有3個單元格,最有可能具有9個單元格(如上圖所示,從PHP版本開始),但理論上可以有無限數量。
到目前為止,這是我對Dimensions子窗體的了解:
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)
表單定義感覺不太正確(也許我在這里完全錯了嗎?)。 AdDimensions.dimensions
應該是Maybe String
,因為在運行查詢以獲取新廣告尺寸的size_id / layout_id的所有可能組合的列表時從數據庫返回時,它將為null,但不為null從創建編輯表單時將運行的類似查詢中獲取。 該字段本身是必需的(在數據庫中ad_dimensions.dimensions
設置not null
)。
從這里開始,我不知道該去哪里告訴父表單它有一個子表單列表或如何使用Heist渲染它們。
我很早以前為此寫了一個特殊的combinator,用於digesting-functors-0.2。 這是一個功能非常齊全的解決方案 ,其中包括允許動態添加和刪除字段的javascript代碼 。 該代碼基於Chris和我為更早實現的Formlet軟件包所做的實現,而消化功能最終取代了它。 從來沒有將此功能移植到與消化功能在0.3中使用的新API一起使用。
這個問題很棘手,並且有一些微妙的極端情況,因此我建議您花一些時間看一下代碼。 我認為Jasper可能會在當前版本的消化功能中接受大量代碼。 只是還沒有人完成這項工作。
編輯:現在已經為最新的消化功能器完成了。 請參閱listOf函數。
使用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)
如果您有一個簡單的項目列表,則digesting-functors-heist為此定義了一些拼接,但是您可能會發現最終會得到無效的標記,尤其是在表格位於表格中的情況下。
<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>
有提供的JavaScript通過digestiveFunctors控制添加和刪除從具有一個jQuery依賴性的形式的元素 。 我最終寫了我自己的文章以避免jQuery依賴,這就是為什么我不使用提供的addControl
或removeControl
接頭(按鈕類型元素的屬性)的原因。
由於標簽是動態的(例如,它們來自數據庫), 並且因為我們希望在復雜的表布局中使用標簽,因此OP中的表格無法使用由摘要功能鍵提供的拼接。 這意味着我們必須執行另外兩個任務:
如果您沒有查看由摘要式功能鍵拼接生成的標記,則可能要先執行該操作,以使您對生成的內容有所了解,以便正確處理表單。
對於動態表單(例如,允許用戶即時添加或刪除新項目的表單),您將需要一個隱藏的索引字段:
<input type='hidden' name='formname.fieldname.indices' value='0,1,2,3' />
runForm
運行時為表單命名的runForm
當列表中的一項被刪除或添加了新項時,將需要調整此列表,否則新項將被完全忽略,並且刪除的項仍將存在於列表中。 對於靜態形式(如OP中的形式)而言,此步驟是不必要的。
如果您已經知道如何編寫接頭,則生成表格的其余部分應該很簡單。 適當地分塊數據(groupBy,chunksOf等),然后通過接頭發送。
如果您不能通過瀏覽digesting-splices-heist生成的標記來分辨,則需要插入子表單的索引值作為每個子表單的字段的一部分。 這是子表單列表的第一個字段的輸出HTML外觀:
<input type='text' name='formname.properties.0.name' value='Foo' />
<input type='text' name='formname.properties.0.value' value='Bar' />
(提示:將您的列表與從0開始的無限列表一起壓縮)
(如果此代碼實際上無法按照編寫的方式進行編譯,我事先表示歉意,但希望可以說明該過程)
這一部分不如其他部分簡單明了,因此您必須深入了解消化功能的內在特性。 基本上,我們將使用digesting-functors-heist所做的相同功能來取回數據並用它填充Thing。 我們需要的功能是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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.