Disable Elm form submission without no-op message - html

I have an Elm app with an HTML form (containing an input field and a submit button) that I would like to disable while the input is not valid. I've managed to do that by binding form submission to a no-op message when the input is invalid:
type Msg
= Input String
| Submit
| Noop
viewForm : Model -> Html Msg
viewForm = form [ onSubmit (if model.valid then Submit else Noop) ]
[ input [ onInput Input ] []
, button [ disabled (not model.valid) ] [ "Submit" ]
]
update then does nothing on a Noop message. The button is also disabled, but this is secondary, since I also care about form submission by hitting Enter from the text input. Note that it does not work to skip the onSubmit handler, because then hitting Enter will reload the page.
This feels a bit messy and inefficient, so my questions are:
What is the idiomatic way to achieve this in Elm?
How much more expensive is this use of a no-op message to a no-op Javascript event handler.

First of all, I have to say that your implementation is alright, there's no harm in sending Noop message, that's why it exists.
The best scenario would be to remove the listener entirely(if possible) and disable the button visually. This actually might have a performance drawback if model.valid changes super-frequently.
I'm using a helper for conditionally adding stuff to a list:
appendIf : Bool -> a -> List a -> List a
appendIf flag value list =
if flag == True then
list ++ [ value ]
else
list
So you could use it like that:
view model =
button
([ type_ "" submit ] |> appendIf model.valid (onClick Submit))
[]

Related

Elm onInput event handler not working on contenteditable element

I want to update my model everytime someone modifies an editable table cell.
The onInput function seems to be working well on input elements, but on a td element with contenteditable="true", the handler function never gets called.
Why?
How should I detect content change in an editable table cell then?
Please see lines 74 and 78 in the following piece of code
(I just took the "buttons" example of the Elm guide and added my stuff to it to have something minimal but runnable):
module Main exposing (..)
-- Press buttons to increment and decrement a counter.
--
-- Read how it works:
-- https://guide.elm-lang.org/architecture/buttons.html
--
import Browser
import Html exposing (Html, button, div, text, table, tbody, tr, td, input)
import Html.Events exposing (onClick, onInput)
import Html.Attributes exposing (contenteditable)
-- MAIN
main =
Browser.sandbox { init = init, update = update, view = view }
-- MODEL
type alias Model = Int
init : Model
init =
0
-- UPDATE
type Msg
= Increment
| Decrement
update : Msg -> Model -> Model
update msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
testOnInputHandler : String -> Msg
testOnInputHandler str =
let
log = Debug.log "in testOnInputHandler" "here"
in
Increment
-- VIEW
view : Model -> Html Msg
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (String.fromInt model) ]
, button [ onClick Increment ] [ text "+" ]
, table []
[ tbody []
[ tr []
[ td [ contenteditable True, onInput testOnInputHandler] [ text "editable!" ]
]
]
]
, input [ onInput testOnInputHandler ] []
]
onInput will get the string from target.value which does not exist for contenteditable input events.
You may want to grab innerText for example instead:
onContentEditableInput : (String -> msg) -> Attribute msg
onContentEditableInput tagger =
Html.Events.stopPropagationOn "input"
(Json.map (\x -> ( x, True )) (Json.map tagger innerText))
innerText : Json.Decoder String
innerText =
Json.at ["target", "innerText"] Json.string

In Elm, how can I detect if focus will be lost from a group of elements?

Suppose I have a form with a number of components. I'd like to detect a los of focus from the group. So, focus from 1 input to another on the same form should be ignored. How can I achieve this?
First, we want to be able to tag each focusable element within the group with some attribute, so when we switch elements we'll know if we're in the same group or not. This can be achieved with data attributes.
groupIdAttribute groupId =
Html.Attributes.attribute "data-group-id" groupId
Next, we need to decode the event payload on an onBlur event to see if the target is different from the relatedTarget (that which will get the focus). And report the change. (note that here we refer to data-group-id via the path "dataset", "groupId")
decodeGroupIdChanged msg =
Json.Decode.oneOf
[ Json.Decode.map2
(\a b ->
if a /= b then
Just a
else
Nothing
)
(Json.Decode.at [ "target", "dataset", "groupId" ] Json.Decode.string)
(Json.Decode.at [ "relatedTarget", "dataset", "groupId" ] Json.Decode.string)
, Json.Decode.at [ "target", "dataset", "groupId" ] Json.Decode.string
|> Json.Decode.andThen (\a -> Json.Decode.succeed (Just a))
]
|> Json.Decode.andThen
(\maybeChanged ->
case maybeChanged of
Just a ->
Json.Decode.succeed (msg a)
Nothing ->
Json.Decode.fail "no change"
)
Now we can create an onGroupLoss listener:
onGroupFocusLoss msg =
Html.Events.on "blur" (decodeGroupIdChanged msg)
And rig it up like so:
input [onGroupFocusLoss GroupFocusLoss, groupIdAttribute "a"]
Here is an example (note that it is built with elm-ui so there's a little extra code.)
https://ellie-app.com/3nkBCXJqjQTa1

Angular2 Reactive forms, FormControl default value in pristine status

I have created a form which allows the user to add additional text-inputs by clicking a button. The FormControls behind these inputs are stored in a FormArray inside of a FormGroup.
I want to provide a default value for these inputs, that is going to be submitted if they are pristine. If the user changes the value of the input, which changes it to dirty, I do not want the default value to be submitted or displayed.
I currently am displaying the inputs like this, as the placeholder attribute does exactly what I want, displaying the default name, only if the input has not been changed.
<div
formArrayName="names"
*ngFor="let server of names.controls; let i = index; trackBy:trackByFn">
<span>{{ i + 1 }}</span>
<input
type="text"
formControlName="{{i}}"
placeholder="{{defaultName}}">
</div>
To validate the names I have created the following validation function:
export function validateServerName(form: FormGroup): ValidationErrors | null {
const names: string[] = form.value[CREATE_FORM_KEY_NAMES];
for (const name of names) {
if (name.trim() === '') {
return {
invalidName: true
};
}
}
return null;
}
Here I am having trouble figuring out if the element is dirty or pristine, as form.value[key] only returns a string array, not an array of the FormControls.
I am looking for either an easier way to do what I am trying to achieve, or a way to validate the form properly.
you can check the control status using
if touched is true then its dirty
this.form.get('controlname').touched
and for pristine you can check like
this.form.get('controlname').pristine
UPDATE
for form array it will be something like
let val = this.user.get('<FormArray>') as FormArray;
console.log(val.at(index));
you can now use pristine and touched on this variable

How to get only pure value from MySQL

I need value from MySQL send into PLC via OPC UA (in NODE-RED). Everything works ok, but I don't know how to get pure value without descriptions of array etc.
I use this code:
SELECT `user_info` FROM `users` WHERE `user_name` LIKE 'Lukas'
The answer is:
array[1]
0: object
user_info: "6"
If I send it to PLC as STRING the value in PLC is:
[object Object]
Can I edit the code somehow? I need answer only:
6
Thank you
The answer is array[1] 0: object user_info: "6"
I assume you've copied that out of the Debug window which shows you the exact structure of the payload you've received.
That is saying, the payload is an array with a single element. That element is an object with a user_info property of value 6.
In other words:
[
{
"user_info": "6"
}
]
In which case, to access the value you would use:
msg.payload[0].user_info
For example, a Function node to pull that value out and put it into the payload would be:
msg.payload = msg.payload[0].user_info;
return msg;
Or you could use a Change node to set the value of msg.payload to the value of msg.payload[0].user_info.

ClojureScript - get checkbox element value

I have the following code (from om.next) creating a checkbox input. The creation works fine, but I don't know how to get the event value, as I don't see the value somewhere in the logs when I click on it (there is a lot of data though) :
(dom/input #js {:type "checkbox"
:onClick (fn [e] (js/console.log e)) ;; how do I get the current true/false value from `e` ?
})
I get in the logs (abbreviated) :
SyntheticMouseEvent {dispatchConfig: Object, dispatchMarker:
".0.0.1.$[cardpath]=1[om_tutorial=1B_UI_Exercises]=1[exercise-3].0.$-184795562.1.0",
nativeEvent: MouseEvent, target: input, currentTarget: input…}
Note : the code is from there.
(.- target e) returns you the element,
generally you want the .-value from an element, but for a checkbox you want .-checked instead... so something like
(.. e -target -checked)