Automatically focus input element after creation in purescript-halogen - purescript

I'm using purescript-halogen to build a spreadsheet-like table (similar to Handsontable). If you double-click a cell, an html input element is rendered as a child of the respective table cell (and no such element is rendered for all the other cells).
This works really well with halogen, except that I don't know how to automatically set the focus to the newly created input element.
I tried the autofocus attribute, but this only works for the first cell that is double-clicked. The JavaScript way to do it is by calling the focus() method on the new element, but I don't know how to call it after the DOM has been updated in halogen. Any ideas?

Ok, here is how I did it using Phil's Initializer hint:
Write a JavaScript function that actually focuses the element.
exports.setFocusImpl = function(elemId) {
return function() {
document.getElementById(elemId).focus();
};
};
FFI it.
foreign import data FOCUS :: !
foreign import setFocusImpl :: forall e. Fn1 String (Eff (focus :: FOCUS | e) Unit)
setFocus :: forall e. String -> Eff (focus :: FOCUS | e) Unit
setFocus = runFn1 setFocusImpl
And then use the setFocus function in the initializer.
H.input
[ A.id_ "inputField"
, A.Initializer do
liftEff $ setFocus "inputField"
pure DoNothing
] [ ]
Note that I'm using an old version of halogen where the signature is still the old one (definition of Initializer in 30e8b2c7).

Related

Where is the documentation to write an event handler for input text box?

Originally I wanted to know:
How do I write a handler for this?
type state = string;
type action = | ChangeName(string)
let reducer = (_state, action) => switch action { | ChangeName(text) => text }
[#react.component]
let make = () => {
let (state, dispatch) = React.usefReducer(reducer, "");
/* What is the parameter type and how do I decode it? */
let onChange = ??? => dispatch(ChangeText(????));
<input value=state onChange/>
}
Specifically, what is the parameter type for the punned onChange handler and how do I decode it?
Every reference I come across is for JS, which I'm having difficulty translating to Re.
EDIT
The answer I found by scraping github:
let onChange = event => dispatch(ChangeName(ReactEvent.Form.target(event)##value));
Say I'd like to use another JSX element, where's the documentation? OR, is their a supposition that people coming to this from elsewhere have knowledge apriori? (I'm mostly comfortable with 'c').
You can get the types of all the DOM attributes from https://github.com/rescript-lang/rescript-react/blob/v0.10.1/src/ReactDOM.res
This file contains bindings to ReScript-React's subset of DOM attributes. It has:
onChange: ReactEvent.Form.t => unit
ReactEvent.Form module is declared at https://github.com/rescript-lang/rescript-react/blob/v0.10.1/src/ReactEvent.resi#L168
When looking for anything specific to ReScript-React, search that repo.
Looks like you have the correct code to handle the event now. Btw, you have in some places the variant constructor ChangeName and in others ChangeText, I assume the correct one is one of those. The compiler will of course catch this too :-)

Unify record types with overlapping fields

I have the following code:
workWithImportantField :: forall fields. { importantField :: Int | fields } -> Input
workWithImportantField = ...
workWithImportantField $
maybe { importantField: 1 } identity (Just { importantField: 1, fieldIDontCareAbout: "whatever" })
This doesn't compile because the first record doesn't have the fieldIDontCareAbout. However, I'm perfectly fine if it unifies into forall fields. { importantField :: Int | fields } so it gets passed into workWithImportantField. How would I go about doing that?
I've tried adding type annotations to various places (first record, second record, the whole expression) without success. I can always replace identity with unsafeCoerce, but I would like a type-safe solution. I can also manually pick out the fields I need by replacing identity with \{ importantField } -> { importantField }, but that doesn't look nice.
Replacing identity with pick found at Record.Extra "throws away" fieldIDontCareAbout from the second record so the types unify.

Why does this function application generate a runtime error in purescript?

I have the following PureScript snippets; note parseXMLFromString is partially applied:
parseXMLFromString ∷ String → DOMParser → Effect Document
parseXMLFromString s d =
parseFromString "application/xml" s d
parseNoteDoc :: DOMParser -> Effect Document
parseNoteDoc = parseXMLFromString TD.noteXml
note <- parseNoteDoc domParser
The following code is generated:
// Generated by purs version 0.12.4
"use strict";
var Effect_Console = require("../Effect.Console/index.js");
var Test_Data = require("../Test.Data/index.js");
var Web_DOM_DOMParser = require("../Web.DOM.DOMParser/index.js");
var parseNoteDoc = Web_DOM_DOMParser.parseXMLFromString(Test_Data.noteXml);
var main = function __do() {
var v = Web_DOM_DOMParser.makeDOMParser();
var v1 = parseNoteDoc(v)();
return Effect_Console.log("TODO: You should add some tests.")();
};
module.exports = {
parseNoteDoc: parseNoteDoc,
main: main
};
The line var v1 = parseNoteDoc(v)(); gives the error TypeError: parseNoteDoc(...) is not a function.
I'm not sure where the extra () is coming from on parseNoteDoc but that is the issue. When I manually remove the () in the generated source, it works works as expected.
Update: Added the code to reproduce this on this branch. After the usual formalities, npm run testbrowser and open dist/index.html in a browser.
TL;DR: your FFI code is incorrect, you need to add an extra function().
Longer explanation:
The extra empty parens come from Effect.
This is how effectful computations are modeled in PureScript: an effectful computation is not a value, but a "promise" of a value that you can evaluate and get the value as a result. A "promise" of a value may be modeled as a function that returns a value, and this is exactly how it's modeled in PureScript.
For example, this:
a :: Effect Unit
is compiled to JavaScript as:
function a() { return {}; }
and similarly, this:
f :: String -> Effect Unit
is compiled to JavaScript as:
function f(s) { return function() { return {}; } }
So it takes a string as a parameter, and then returns Effect Unit, which is itself a parameterless function in JS.
In your FFI module, however, you are defining parseFromString as:
exports.parseFromString = function (documentType) {
return function (sourceString) {
return function (domParser) {
return domParser.parseFromString(sourceString, documentType);
};
};
};
Which would be equivalent to parseFromString :: String -> String -> DOMParser -> Document - i.e. it takes three parameters, one by one, and returns a parsed document.
But on the PureScript side you're defining it as parseFromString :: String -> String -> DOMParser -> Effect Document - which means that it should take three parameters, one by one, and then return an Effect Document - which should be, as described above, a parameterless function. And it is exactly this extra parameterless call that fails when you try to evaluate that Effect Unit, which in reality is not an Effect at all, but a Document.
So, in order to fix your FFI, you just need to insert an extra parameterless function, which will model the returned Effect:
exports.parseFromString = function (documentType) {
return function (sourceString) {
return function (domParser) {
return function() {
return domParser.parseFromString(sourceString, documentType);
}
};
};
};
(it is interesting to note that makeDOMParser :: Effect DOMParser is correctly modeled in your FFI module as a parameterless function)
But there is a better way
These pyramids of nested functions in JS do look quite ugly, you have to agree. So it's no surprise that there is an app for that - EffectFn1, runEffectFn1, and friends. These are wrappers that "translate" JavaScript-style functions (i.e. taking all parameters at once) into PureScript-style curried effectful functions (i.e. taking parameters one by one and returning effects).
You can declare your JS side as a normal JS function, then import it into PureScript as EffectFnX, and call it using runEffectFnX where needed:
// JavaScript:
exports.parseFromString = function (documentType, sourceString, domParser) {
return domParser.parseFromString(sourceString, documentType);
};
-- PureScript:
foreign import parseFromString ∷ EffectFn3 String String DOMParser Document
parseHTMLFromString ∷ String → DOMParser → Effect Document
parseHTMLFromString s d =
runEffectFn3 parseFromString "text/html" s d
P.S. People who purchased EffectFn1 also liked Fn1 and friends - same thing, but for pure (non-effectful) functions.

How do I combine effectful event handlers and custom EventUpdates in purescript-halogen?

In my custom Halogen/Purescript project I follow the pattern from the AJAX Example where I split my actions up into pure Inputs and effectful Requests.
I want to change my event handler to use the preventDefault behavior, but don't understand what consequences for the type of the UI function this entails.
I made the same changes to the AJAX Example by changing the event handler the following way:
Before:
H.button [ A.classes [B.btn, B.btnPrimary]
, A.disabled busy
, A.onclick (\_ -> pure (handler code))
] [ H.text "Compile" ]
After:
H.a [ A.classes [B.btn, B.btnPrimary]
, A.href "#compile"
, A.disabled busy
, A.onclick (\_ -> E.preventDefault $> pure (handler code))
] [ H.text "Compile" ]
(Full diff available here)
I end up with this type error:
Cannot unify type
Example.Ajax.Input
with type
Halogen.HTML.Events.Monad.Event Halogen.HalogenEffects<(http ::
Example.Ajax.HTTP | u32519)> Example.Ajax.Input
At this point, I'm a bit lost whether I would need to adjust the type signature of the UI function or I apply the preventDefault modifier the wrong way.
The type of $> looks like:
($>) :: forall a. EventHandler a -> b -> EventHandler b
The type of pure looks like:
pure :: forall a. a -> EventHandler a
So the problem is by using both together, you're making a type which looks like this:
EventHandler a -> EventHandler b -> EventHandler (EventHandler b)
But you don't want that, you just want an EventHandler b, where b is the E.Event type of handler code.
The best solution is to just not use pure:
E.preventDefault $> handler code
If sometime you do have two EventHandler values you want to use together like this, the function to use is *> instead of $>.

Lift: How to bind a Hyperlink to a serverside method

I am new to Lift, I want to use hyperlink instead of submit button. I am able to bind my submit button with a server-side method use CSS Selector. for example:
def render = {
// define some variables to put our values into
// process the form
def process() {
do something....
}
}
"type=submit" #> SHtml.onSubmitUnit(process)
}
I want to use hyperlink to submit my form instead of submit button. How can I bind hyperlink with process()(server-side) method.
Thanks,
Puneet
In this instance you would probably want to use SHtml.ajaxCall and supply the form information as the JsonContext (i.e. not bound with CSS selectors):
def ajaxCall (jsCalcValue: JsExp, jsContext: JsContext, func: (String) ⇒ JsCmd) : (String, JsExp)
Alternativly you could use SHtml.a:
def a (func: () ⇒ JsObj, jsonContext: JsonContext, body: NodeSeq, attrs: ElemAttr*) : Elem
Failing that you should look at the available methods in SHtml (jsonForm would be another one to look at) and see which one best fits your use case. I would encourage you to pick up a copy of Lift in Action which discusses how the function binding works, as I think you have miss-understood it in relation to the request/response cycle.