Javascript 如何在Elm中创建有状态、模块化、自包含的web组件?

Javascript 如何在Elm中创建有状态、模块化、自包含的web组件?,javascript,elm,Javascript,Elm,假设您想要创建一个有3个按钮的UI。单击其中一个按钮时,其他按钮将被释放。在JavaScript中,您可以编写: var元素=[“Foo”、“Bar”、“Tot”].map(函数(名称){ var元素=document.getElementById(名称); element.onclick=function(){ elements.map(函数(元素){ element.className='button'; }); element.className='按钮已选定'; }; 返回元素; })

假设您想要创建一个有3个按钮的UI。单击其中一个按钮时,其他按钮将被释放。在JavaScript中,您可以编写:

var元素=[“Foo”、“Bar”、“Tot”].map(函数(名称){
var元素=document.getElementById(名称);
element.onclick=function(){
elements.map(函数(元素){
element.className='button';
});
element.className='按钮已选定';
};
返回元素;
});
。按钮{
边框:1px纯黑;
光标:指针;
保证金:4倍;
填充:4px;
}
.选定{
背景色:#DDDDDD;
}

福
酒吧
托特

这是同一事物的另一个版本:)


Elm可以满足这些需求中的每一个,使您的组件具有状态、模块化、自包含和纯粹的特性。以下是Elm中使用的示例(请原谅内联样式):

import StartApp.Simple exposing(开始)
导入Html(Html、div、span、text)
导入Html.Attributes(id、类、样式)
导入Html.Events(onClick)
类型别名模型=
{元素:列表字符串
,选中:可能是字符串
}
init:模型
初始化=
{元素=[“Foo”,“Bar”,“Tot”]
,selected=Nothing
}
类型动作
=选择字符串
更新:操作->模型->模型
更新动作模型=
案例诉讼
选择s->
{model | selected=Just s}
视图:Signal.Address操作->模型->Html
视图地址模型=
让
btn文本=
跨度
[id txt
,按钮样式
,onClick地址[]
只是s->
如果s==txt,则
[(“背景色”和“#DDDDDD”)]
其他的
[]
在里面

div[]我刚刚看到Chad的答案,当时我正在写我的答案。这个答案也使用了,但是在Html中使用了原始的类名,并且有一个“更强大的”模型。更强模型的优点在于,你可以看到你在问题中提到的三个位。id名称和实际按钮之间的隐式耦合也较少。但它会给你留下一些重复的名称,你可能想要,也可能不想要。这取决于你想要这种耦合的程度

import StartApp.Simple as StartApp
import Html as H exposing (Html)
import Html.Attributes as HA
import Html.Events as HE

type alias Model =
  { foo : Bool
  , bar : Bool
  , tot : Bool
  }

type Action
  = Foo
  | Bar
  | Tot

model : Model
model =
  { foo = False
  , bar = False
  , tot = False
  }

update : Action -> Model -> Model
update clicked _ =
  case clicked of
    Foo -> { model | foo = True }
    Bar -> { model | bar = True }
    Tot -> { model | tot = True }

view : Signal.Address Action -> Model -> Html
view addr { foo, bar, tot } =
  [ foo, bar, tot ]
  |> List.map2 (viewButton addr) buttons
  |> H.div []

buttons : List (String, Action)
buttons =
  [ ("Foo", Foo)
  , ("Bar", Bar)
  , ("Tot", Tot)
  ]

viewButton : Signal.Address Action -> (String, Action) -> Bool -> Html
viewButton addr (id, action) selected =
  H.span
    [ HA.id id
    , HA.classList
      [ ("button", True)
      , ("selected", selected)
      ]
    , HE.onClick addr action
    ]
    [ H.text id
    ]

buttonStyle =

main =
  StartApp.start
    { model = model
    , view = view
    , update = update
    }

正如devdave所建议的,嵌套是我发现的模块化组件的唯一方法

我已经实现了一个类似的示例,您可以在这里看到:

其思想是子函数公开函数以获取其自身模型的属性。这些函数反过来可以为子组件调用更多嵌套函数

查看本回购协议的
Readme.md
,了解代码示例:

抱歉,但这些答案都没有解释最重要的一点:这是如何模块化的?即,你如何能够将应用程序包含在另一个应用程序中?需要何种状态泵送?抱歉,但这些答案都没有解释最重要的一点:这是如何模块化的?即,你如何能够将应用程序包含在另一个应用程序中?什么状态指示泵送是必要的?@Viclib-看一看。示例1构建了一个简单的组件,然后以下示例以模块化的方式构建该组件。我发布的示例也可以这样做,但教程更详细。Elm体系结构教程上的示例不是模块化的。使用计数器需要手动将计数器的状态存储在其自己的模型上。经过足够的层之后,站点的顶级模型将成为一个巨大的平面ADT,并提及每个子模型的每个模式。想象一下:“data MySiteAdt=……state_of the_button_inside_the_other_div_inside_the_menu:IsSelected…”。这不是模块化的工作方式。父应用程序不需要有许多内部应用程序模型规范的副本。诚然,教程中描述的模块化会冒泡,因此您必须在所有父应用程序中考虑子状态。但这是使用StartApp。如果您放弃StartApp,直接使用端口,您可以实现ve更细粒度的模块化你说直接进入端口是什么意思?抱歉,但这些答案都没有解释最重要的一点:这是如何模块化的?即,你如何能够将应用程序包含在另一个应用程序中?需要什么样的状态泵送?你使用嵌套模块化Elm应用程序。因此,你有一个顶级的应用程序组件a使用更新和查看函数。然后,您可以通过在顶层模型记录中为其状态提供一个条目、一个ChildAction类型、一个更新函数中的条目(将ChildAction操作发送到子对象的更新方法)以及一个Signal.forwardTo标记子对象中的操作,将“子模块”嵌套在该子模块中。老实说,它的目前有很多重复的样板文件可以让它工作。我正在寻找一种方法来抽象它,但还没有发现任何优雅的东西。在Elm体系结构中,每个组件都依赖于它的直接子组件,但既不依赖于它的父组件,也不依赖于它的间接子组件。这就是可重用性:你可以在任何其他组件中使用任何组件,耦合将仅限于与子组件通信的父级。要实现模块化,您可能意味着,您必须提取组件中的通用功能,以便有通用的init update view函数,这些函数记录表示组件的各种功能。
import StartApp.Simple as StartApp
import Html as H exposing (Html)
import Html.Attributes as HA
import Html.Events as HE

type alias Model =
  { foo : Bool
  , bar : Bool
  , tot : Bool
  }

type Action
  = Foo
  | Bar
  | Tot

model : Model
model =
  { foo = False
  , bar = False
  , tot = False
  }

update : Action -> Model -> Model
update clicked _ =
  case clicked of
    Foo -> { model | foo = True }
    Bar -> { model | bar = True }
    Tot -> { model | tot = True }

view : Signal.Address Action -> Model -> Html
view addr { foo, bar, tot } =
  [ foo, bar, tot ]
  |> List.map2 (viewButton addr) buttons
  |> H.div []

buttons : List (String, Action)
buttons =
  [ ("Foo", Foo)
  , ("Bar", Bar)
  , ("Tot", Tot)
  ]

viewButton : Signal.Address Action -> (String, Action) -> Bool -> Html
viewButton addr (id, action) selected =
  H.span
    [ HA.id id
    , HA.classList
      [ ("button", True)
      , ("selected", selected)
      ]
    , HE.onClick addr action
    ]
    [ H.text id
    ]

buttonStyle =

main =
  StartApp.start
    { model = model
    , view = view
    , update = update
    }