這章節會大量翻譯原始網站的英文資訊,僅部分內容會因個人理解稍微修改與調整,對原始內容有興趣的,可以前往原始網站查閱:
https://docs.getodk.org/form-logic/
ODK Collect 支援多種動態表單行為,本文涵蓋了如何在 XLSForm 定義中指定這些行為。
變數是引用之前問題的回答值,在 XLSForm 中使用變數時,將問題名稱放(question-name)在大括號中,並在前面加上$符號:
${question-name}
變數可以用在 [label]、[hint] 和 [repeat_count] 欄位,以及任何接受表達式的欄位中。
表達式: https://docs.getodk.org/form-logic/#expressions
type | name | label |
---|---|---|
text | your_name | 請問如何稱呼? |
note | hello_name | 嗨! ${your_name} 您好 |
您也可以引用目前問題或目前問題的上層群組(parent group)或重複項目(repeat):
符號 | Explanation | Example | Notes |
---|---|---|---|
. | 目前問題的數值 | . >= 18 | 用於約束(constraints) |
.. | 目前問題的上層項目 | position(..) | 與「position()」一起使用來取得上層重複實例的索引。 |
XLSForm 中的 「${}」表示法是一種方便的快捷方式,用來引用特定欄位。當 XLSForm 被轉換時,「${}」引用會擴展為 XPath 路徑,這些路徑描述了表單中欄位的位置。
在 XLSForm 中,使用 XPath 表示法是有利的,尤其是在重複或資料集的情境下。「${}」 和 XPath 表示法可以自由混合使用。
XPath 的一種思考方式是,它將表單或數據集視為您電腦上的一系列資料夾和文件。問題就像文件,而群組(Group)和重複項目(Repeats)則像資料夾,因為它們可以包含其他元素。路徑元素之間用 「/」 分隔。想像一個表單,其中有一個名為 「outer」 的群組,該群組包含另一個名為 「inner」 的群組,「inner」 群組中包含一個名為 「q1」 的問題。到 「q1」 的絕對路徑是 /data/outer/inner/q1
。
上述示例中的 「data」 是表單根的名稱。這個根名稱預設為 「data」,但可以在 XLSForm 設定表中添加一個名稱欄來修改,並在其下方指定一個值。不過這種修改需求很少見。路徑開頭的 「/」 表示這是一個絕對路徑。
XPath 路徑也可以是相對的。比如,假設上面的示例中 「q1」 有一個相關性表達式,該表達式引用了 「outer」 群組中的一個名為 「age」 的問題。我們可以使用絕對表達式來引用它:「/data/outer/age」,也可以寫成相對表達式:「../../age」。
相對表達式中的 「../..」 表示從當前位置 「/data/outer/inner/q1」 向上兩層。第一個 「..」 上升一層到 「/data/outer/inner」,然後第二個 「..」 再上升一層到 「/data/outer/」。我們想讀取 「outer」 群組中的一個問題,因此添加該問題的名稱來得到 「../../age」。
ODK 工具支持的 XPath 子集是按照 ODK XForms 規範(https://getodk.github.io/xforms-spec/#xpath-paths )來描述的。
在重複項目與資料集中,XPath 路徑可以引用多個節點(multiple nodes),這稱為節點集 (nodeset)。XPath 謂詞是用方括號([])括起來的 True/False(布林值)表達式,這些表達式會過濾它們所引用的節點集。當您為選擇定義選項過濾器時,該表達式會作為 XPath 謂詞來過濾選項。
您也可以撰寫自己的謂詞表達式。例如,假設有一個表單,包含一個名為 「people」 的重複項目,內部有一個名為 「age」 的問題。表達式 「/data/people[age < 18]」 會評估出一個節點集,該節點集中包含所有 「age」 問題值小於 18 的 「people」 實例。
您可以在謂詞之後添加更多路徑步驟。例如: 「/data/people[age < 18]/pet_count」 會評估為一個節點集,該節點集包含所有 「people」 重複項中年齡小於 18 的實例的寵物數量。節點集可以傳遞給像 「sum()」 這樣的函數,或者其他接受節點集參數的函數。
有時,表單可能會使用群組來組織重複項中的問題部分。這些群組必須在謂詞中考慮進去。如果 「age」 問題嵌套在一個名為 「inner」 的群組中,則謂詞表達式需要是 「inner/age < 18」。此外,如果 「pet_count」 問題嵌套在一個名為 「details」 的群組中,完整的表達式應為 「/data/people[inner/age < 18]/details/pet_count」。
XPath 謂詞也是引用數據集中特定值的方法。您可以在引用數據集中的值這一部分學到更多相關內容。
數據集:https://docs.getodk.org/form-datasets/
引用數據集:https://docs.getodk.org/form-datasets/#referencing-values-in-datasets
當表單定義包含重複項時,對應已填寫的表單將擁有 0 個或更多的重複項實例。使用之前提到的檔案和資料夾類比,每個重複實例就像一個資料夾,而這些資料夾都使用重複項的名稱。重複項實例通過它們的索引(第一個、第二個,依此類推)來區分。
在重複項內撰寫表達式時,使用調查員當前正在填寫的重複項實例的位置可能會很有幫助。這可以通過使用「position()」函數來實現。一個有用的上下文是,當您想先收集一個人員或物品的名單,然後對每一個進行額外問題提問時。如 「position()」函數中的示例所示,您可以使用第一個重複項來收集名單,然後在第二個重複項中根據它們在第一個重複項中的位置來引用這些項目。
關於「position()」函數: https://docs.getodk.org/form-operators-functions/#position
「position()」函數的另一個用途是訪問前一個重複項實例。在「重複項中的動態預設值」(https://docs.getodk.org/form-logic/#dynamic-defaults-repeats) 部分有這方面的示例。
XPath 路徑在重複項外部引用部分或所有重複項實例時也非常有用。XPath 表達式特別適合用來過濾重複項實例,例如從重複項中收集的數據提供總結信息:
type | name | label | calculation |
---|---|---|---|
begin repeat | people | Person | |
Int | age | Age | |
Int | pet_count | How many pets does this person have? | |
end repeat | people | ||
Int | total_pets | sum(${people}[age < 18]/pet_count) | |
Note | total_note | Total pets owned by children: ${total_pets} |
在路徑表達式 「${people}[age < 18]/pet_count」 中,「${people}」 使用 「${}」表記法來引用所有重複項實例。您也可以將其擴展為 XPath 路徑 「/data/people」。有關詳細信息,請參見 XPath 謂詞 (https://docs.getodk.org/form-logic/#xpath-predicates-for-filtering)部分。在這個例子中,「total_pets」 值會顯示給用戶。它可以用於許多不同的上下文,例如,如果在社區中有多於一個兒童擁有寵物,那麼可以用它來定義某個群組的相關性(https://docs.getodk.org/form-logic/#relevants),這樣只有在符合條件時才需要填寫該部分問題。
表達式會在填寫表單時動態進行評估。它可以包含 XPath 函數、運算符、之前回答的值,以及(在某些情況下)目前正在回答的值。
表達式範例
${bill_amount} * 0.18
將先前的值 bill_amount
乘以 18%,用於計算合適的小費。
concat(${first_name}, ' ', ${last_name})
將兩個之前的回答(first_name
和 last_name
)之間加上一個空格,連接成一個字串。
${age} >= 18
根據 age
的值,評估為 True
或 False
。
round(${bill_amount} * ${tip_percent} * 0.01, 2)
根據之前輸入的兩個值計算小費金額,然後將結果四捨五入到小數點後兩位。
表達式可以使用在以下地方:
要評估複雜的表達式,請使用計算行( calculate row)。將要評估的表達式放入計算列(calculation column)中。然後,您可以使用計算行的名稱(name)來引用計算結果。
表達式不能用於標籤(label )和提示列(hint ),因此如果您想將計算的值顯示給用戶,必須先使用計算(calculation)行,然後再使用變數於文字(Note)行呈現。
Type | name | label | calculation |
---|---|---|---|
decimal | bill_amount | Bill amount: | |
calculate | tip_18 | round((${bill_amount} * 0.18),2) | |
calculate | tip_18_total | ${bill_amount} + ${tip_18} | |
Note | tip_18_note | Bill: $${bill_amount} | |
Tip (18%): $${tip_18} | |||
Total: $${tip_18_total} |
警告 建議僅將上次儲存的值(last-saved) 用作預設值(default)。引用上次儲存的值可以作為任何表達式的一部分,在允許表達式的地方使用,但這可能會在提交編輯時導致意外結果,因為上次儲存的值可能已經改變。
此功能不適用於加密表單。
您可以參考此表單定義的最後保存記錄中的值:
${last-saved#question-name}
這在多個連續記錄輸入相同值時非常有用。例如,對於一系列家庭,輸入相同的區域。
XLSForm 顯示使用上次儲存的值作為動態預設值
type | name | label | default |
---|---|---|---|
text | street | Street | ${last-saved#street} |
從上次儲存的值中提取的值。這通常是最近建立的記錄,但也可能是之前存在的記錄,該記錄已被編輯並保存。對於某個表單定義首次保存的記錄,任何字段的最後保存值將為空。
任何類型的問題都可以根據上次儲存的值設置其預設值。
警告
最後保存的功能僅複製文字答案值,而不會複製二進制附件,因此對於二進制問題來說效果不佳。文件名將被複製,但實際文件將無法在 Collect 中使用。
每個表達式在調查員填寫表單的過程中會不斷重新評估。這是一個重要的思維模型,能解釋某些意外行為。更具體地說,當以下情況發生時,表達式會被重新評估:
random()
或 now()
。它們所表示的值可能會在上述條件發生時改變。未回答的數字問題為 nil。也就是說,它們沒有值。當在數學運算符或函數中使用引用空值的變數時,該變數會被視為非數值(Not a Number,NaN)。空值不會被轉換為零。包括 NaN 的計算結果也將是 NaN,這可能不是您想要或預期的行為。
要將空值轉換為零,可以使用「coalesce()」 函數或 `「if()」函數。