Elm 0.19.1:能否在初始模型解码器中发起HTTP GET请求?
解决方案:分阶段初始化 + 异步补全数据
核心思路是把组件初始化拆成两个阶段:先完成基础查询条件的解码(仅拿到id列表),再通过异步HTTP请求补全对应的名称数据,最后更新模型完成渲染。具体步骤如下:
1. 调整模型结构,拆分状态
首先修改你的Model,把联系人相关的状态拆分成三个部分:已解码的id列表、id到名称的映射字典,以及加载状态:
type LoadingStatus = Loading | Loaded | LoadFailed String type alias Model = { -- 其他查询条件字段 contactIds : List String , contactNameMap : Dict String String , contactLoadingStatus : LoadingStatus -- 其他模型字段 }
2. 初始解码器仅解析id列表
保持初始解码器只负责解析后端返回的id列表,不处理名称(毕竟后端没返回):
import Json.Decode as D initialDecoder : D.Decoder Model initialDecoder = D.map3 Model -- 其他字段的解码逻辑 (D.field "contactIds" (D.list D.string)) (D.succeed Dict.empty) -- 初始名称字典为空 (D.succeed Loading) -- 初始状态设为加载中
3. 初始化时触发批量查询请求
在init函数中,解码完成基础模型后,根据contactIds生成批量查询名称的Cmd。假设后端有一个接口可以接收id列表,返回对应的(id, name)数组:
import Http import Json.Encode as E init : Flags -> ( Model, Cmd Msg ) init flags = case D.decodeValue initialDecoder flags.queryJson of Ok initialModel -> ( initialModel , if List.isEmpty initialModel.contactIds then Cmd.none else getContactNames initialModel.contactIds ) Err decodeError -> -- 处理解码错误,比如返回错误状态的模型 ( { contactIds = [], contactNameMap = Dict.empty, contactLoadingStatus = LoadFailed (D.errorToString decodeError) } , Cmd.none ) -- 批量获取联系人名称的HTTP请求函数 getContactNames : List String -> Cmd Msg getContactNames ids = Http.post { url = "/api/contacts/names" , body = Http.jsonBody (E.list E.string ids) , expect = Http.expectJson ContactNamesLoaded contactNamesDecoder } -- 后端返回数据的解码器:解析成(id, name)列表 contactNamesDecoder : D.Decoder (List (String, String)) contactNamesDecoder = D.list (D.map2 Tuple.pair (D.field "id" D.string) (D.field "name" D.string))
4. 在Update中处理请求结果
定义对应的Msg,在update函数中处理请求成功/失败的情况,更新模型的名称字典和加载状态:
type Msg = -- 其他Msg ContactNamesLoaded (Result Http.Error (List (String, String))) update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of ContactNamesLoaded result -> case result of Ok namePairs -> ( { model | contactNameMap = Dict.fromList namePairs , contactLoadingStatus = Loaded } , Cmd.none ) Err httpError -> ( { model | contactLoadingStatus = LoadFailed (Http.errorToString httpError) } , Cmd.none ) -- 其他Msg处理逻辑
5. 视图层根据状态渲染
在视图中,根据加载状态展示不同内容:加载中显示提示,加载完成后通过id从字典中取名称,失败则显示错误:
view : Model -> Html Msg view model = div [] -- 其他查询条件视图 [ case model.contactLoadingStatus of Loading -> text "加载联系人名称中..." LoadFailed err -> text ("加载失败:" ++ err) Loaded -> div [] (List.map (\id -> div [] [ text (Dict.get id model.contactNameMap |> Maybe.withDefault id) ] ) model.contactIds) ]
这样既避开了解码器中嵌套HTTP请求的不合理性,也解决了初始模型未解码完成就发请求的问题——因为请求是在初始化完成(基础模型解码成功后)才触发的,完全符合Elm的架构规则。
内容的提问来源于stack exchange,提问作者TBatist
相关产品推荐
相关产品推荐

