Dynamically create and submit a form - html

I have some data in my model which I want to put into a request body and send it with a POST request. The problem is I want the browser to navigate to the request URL because the route returns a new HTML page. Normally this would be done by putting the data in a form and then using JS to submit it.
How can I do this with Elm?

Edit - Updated based on your comment clarification
As I understand your question, you want to be able to construct a POST request behind the scenes but let the browser post it so that the browser is redirected to the actual page it is posted, leaving your Elm app behind.
You could build up the form in Elm but keep it hidden, then when you want to trigger it, pass the form ID to a port which performs the form submission.
type alias Model =
{ foo : String }
view model =
div []
[ Html.form
[ id "my-form"
, action "https://requestb.in/1crya751"
, method "POST"
, style [ ( "display", "none" ) ]
]
[ input [ type_ "text", name "foo", value model.foo ] []
]
, div []
[ button [ onClick SubmitForm ] [ text "Submit" ] ]
]
type Msg
= SubmitForm
update msg model =
case msg of
SubmitForm ->
model ! [ submitForm "my-form" ]
port submitForm : String -> Cmd msg
Your javascript could look something like this (albeit with some error handling in case the id doesn't exist):
var app = Elm.Main.fullscreen()
app.ports.submitForm.subscribe(function(formId) {
document.getElementById(formId).submit();
});
Here is a working example of this on ellie-app.com. It posts the form to requestb.in so you can see what has been posted by going here.
The reason I'm recommending a hidden form instead of trying to use the standard Http packages is because it sounds like you want the browser to be redirected to whatever form you are posting to. You couldn't really achieve the same thing by using the Http packages.

Related

Using Laravel, is there a way to run validation on one ajax call with data for multiple models?

Assuming one were to post multiple data sets of one model at the time through JSON, it is possible to insert these using Eloquent's Model::create() function. However in my case I'll also need to validate this data.
The Validator only takes a Request object as input, and as far as I've seen I can't create a new Request instance with only one model.
Assuming this would be the input data (JSON), and index is the value for the browser to know what data belongs to an what item (as they have no unique ID assigned at the point of creation)
[
{
"index" : 1,
"name" : "Item 1",
"value" : "Some description"
},
{
"index" : 2,
"name" : "Item 2",
"value" : "Something to describe item 2"
},
(and so on)
]
Every object in the root array needs to be ran through the same validator. The rules of it are defined in Model::$rules (public static array).
Would there be a way to run the validator against every item, and possibly capture the errors per item?
You can utilize Validator for manual validation:
...
use Validator;
...
$validator = Validator::make(
json_decode($data, true), // where $data contains your JSON data string
[
// List your rules here using wildcard syntax.
'*.index' => 'required|integer',
'*.name' => 'required|min:2',
...
],
[
// Array of messages for validation errors.
...
],
[
// Array of attribute titles for validation errors.
...
]
);
if ($validator->fails()) {
// Validation failed.
// $validator->errors() will return MessageBag with what went wrong.
...
}
You can read more about validating arrays here.

Elm Html and view : Model -> Html Msg

I'm brand new to Elm and I'm struggling to see what is wrong here..
view : Model -> Html Msg
view model = div [] [ button [ onClick NewStrategy ] [ text "-" ] ]
complains that..
The type annotation for `view` says it always returns:
Html (Msg)
But the returned value (shown above) is a:
Html (String -> Msg)
It looks no different to http://elm-lang.org/examples/buttons to me. One div with a button inside with an onClick and some text.
What am I missing here?
You hadn't listed the source code for Msg but the error you've listed seems to indicate that the NewStrategy constructor takes a single string parameter. If you gave it a string value, it should work.
Example:
view model = div [] [ button [ onClick (NewStrategy "submit") ] [ text "submit" ] ]

Modify HTTP Request value in Chrome automatically

Is there a way or an app to always change the value of a POST Request to a specific URL in Chrome?
Try chrome.webRequest. Specifically, chrome.webRequest.onBeforeRequest.addListener
You would provide the string ["blocking"] as an attribute for the opt_extraInfoSpec parameter, and as a return value provide an object of type BlockingResponse which specifies what changes you want to make to the request.
Also, to get the body of the POST request, opt_extraInfoSpec also needs to contain the string "requestBody"
Your code would look something like this:
chrome.webRequest.onBeforeRequest.addListener( function(details){
//
if(details.method == "POST")
var new_url = "http://stackoverflow.com/my_new_url";
return {redirectUrl: new_url};
}, ({urls: ["http://*/*", "https://*/*"] }), ["blocking", "requestBody"]);
Dcoumentation at https://developer.chrome.com/extensions/webRequest
EDIT: The code you would place in the background page only.

In Aikau. How to get the search query parameters inside a custom widget in a dialog?

I am using Alfresco 5.1.e
In the search page "/share/page/dp/ws/faceted-search". I have included a button "AlfDynamicPayloadButton".
When the button is clicked it opens a dialog with "ALF_CREATE_DIALOG_REQUEST" and inside of it my custom widget.
I need the current search parameters into that widget to create a special visualisation.
My code in the file "faceted-search.get.js":
var myWidget = {
name : "alfresco/buttons/AlfDynamicPayloadButton",
config : {
label : "My Button",
useHash : true,
hashDataMapping : {
"hashFragmentParameterName" : "buttonPayloadProperty"
},
publishPayloadSubscriptions : [ {
topic : "ALF_SEARCH_REQUEST"
}],
publishTopic : "ALF_CREATE_DIALOG_REQUEST",
publishPayloadType : "PROCESS",
publishPayloadModifiers : [ "processDataBindings" ],
publishPayload : {
dialogTitle : "My Title",
widgetsContent : [ {
name : "myPackage/Example",
config : {
width : 400,
height : 500
// other configurations
}
}]
}
}
};
var widget = widgetUtils.findObject(model.jsonModel.widgets, "id",
"FCTSRCH_TOP_MENU_BAR");
widget.config.widgets.push(myWidget);
My Widget:
define(
[
"dojo/_base/declare",
"dijit/_WidgetBase",
"alfresco/core/Core",
"dijit/_TemplatedMixin",
"dojo/_base/lang",
"dojo/text!./html/Example.html"
],
function(declare, _Widget, Core, _Templated, lang, template) {
[ _Widget, Core, _Templated ],{
templateString : template,
i18nRequirements : [ {
i18nFile : "./i18n/Example.properties"
} ],
cssRequirements : [ {
cssFile : "./css/Example.css"
} ],
constructor : function example__constructor() {
// the widget is created each time the button is pressed
this.alfSubscribe("ALF_SEARCH_REQUEST",
lang.hitch(this, this.upgradeSearchParameter));
this.inherited(arguments);
},
upgradeSearchParameter:
function example__upgradeSearchParameter(args){
// this line never run
this.searchParameter = args;
}
});
});
So far I have tried:
Subscribe inside the widget to ALF_SEARCH_REQUEST. The problem with that is the widget haven't been created when topic is published.
Include "alfresco/services/SearchService" has dependency of my widget. I can access with that to some information of the query like the "sort", "site", "sortAscending", etc, but not the "term".
Include "alfresco/search/AlfSearchList" has dependency of my widget. I have "searchTerm", but it is always empty "".
Using "publishPayloadSubscriptions" in my button. All the information of the parameters of the query are inside of the dialog, but it not exists option to populate my widget with that information
publishPayloadSubscriptions : [ {
topic : "ALF_SEARCH_REQUEST"
}],
Is there some way to get all the parameters of the last query in my custom widget?
OK, now that you've added the model I think I might be able to provide a better answer...
It looks like you've just copied the example from the JSDoc. Where you've set the hashDataMapping configuration you can actually reconfigure this to get the search term.
Each time you search, the search text is set on the URL as the searchTerm hash parameter. This means that with useHash configured to be true you can configure the AlfDynamicPayloadButton like this:
{
name: "alfresco/buttons/AlfDynamicPayloadButton",
config : {
label : "My Button",
useHash : true,
hashDataMapping : {
searchTerm: "widgetsContent.0.config.searchTerm"
},
publishPayloadSubscriptions: [],
publishTopic: "ALF_CREATE_DIALOG_REQUEST",
publishPayload: {
dialogTitle: "My Title",
widgetsContent: [
{
name: "myPackage/Example",
config: {
width : 400,
height : 500
// other configurations
}
}
]
}
}
};
The key thing here is that you are mapping the searchTerm URL hash value to the widgetsContent.0.config.searchTerm property of your publishPayload. This means that your custom widget within your dialog will be assigned the last search term that was used (to an attribute also called searchTerm that your widget can reference).
I think you're on the right path with subscribing to the ALF_SEARCH_REQUEST topic. If you're finding that the initial ALF_SEARCH_REQUEST topic (on loading the page) is being published before your AlfDynamicPayloadButton has registered its subscription then you might want to consider upgrading to a more recent Aikau release.
We fixed an issue in the 1.0.68 release to ensure that no publications are fired until all widgets have completed loading on the page (this was actually to address the scenario where there are multiple Surf components on the page which shouldn't be the case on the faceted search page!). We work around this problem by having a shared singleton PubQueue that doesn't release any publications until after all widgets and services have been created.
I suppose this could depends where you're creating your AlfDynamicPayloadButton - for example, if it's in the search results then it will be created after the ALF_SEARCH_REQUEST topic has been published.
Have you checked the DebugLog to ensure that the subscription in your button is being setup correctly (for example that there are no scoping issues?).
Have you verified that a fixed payload (with hard-coded data) will result in the displaying the dialog as you require? Have you verified that the button is not successfully subscribing to the topic but just not building the payload as required.
Could you also update your question to show the configuration for your AlfDynamicPayloadButton as this might help me figure out what the problem is.

How to extract the results of Http Requests in Elm

Using Elm's html package it is possible to make http requests:
https://api.github.com/users/nytimes/repos
These are all the New York Times repos on Github. Basically there are two items I'd want from the Github response, the id and name
[ { "id": 5803599, "name": "backbone.stickit" , ... },
{ "id": 21172032, "name": "collectd-rabbitmq" , ... },
{ "id": 698445, "name": "document-viewer" , ... }, ... ]
The Elm type for Http.get requires a Json Decoder object
> Http.get
<function> : Json.Decode.Decoder a -> String -> Task.Task Http.Error a
I don't know how to open lists yet. So I put the decoder Json.Decode.string and at least the types matched, but I had no idea what to do with the task object.
> tsk = Http.get (Json.Decode.list Json.Decode.string) url
{ tag = "AndThen", task = { tag = "Catch", task = { tag = "Async", asyncFunction = <function> }, callback = <function> }, callback = <function> }
: Task.Task Http.Error (List String)
> Task.toResult tsk
{ tag = "Catch", task = { tag = "AndThen", task = { tag = "AndThen", task = { tag = "Catch", task = { tag = "Async", asyncFunction = <function> }, callback = <function> }, callback = <function> }, callback = <function> }, callback = <function> }
: Task.Task a (Result.Result Http.Error (List String))
I just want an Elm object of the repo names so I can display in some div elements, but I can't even get the data out.
Can someone slowly walk me through how to write the decoder and how to get the data out with Elm?
Update for Elm 0.17:
I have updated the complete gist of this answer to work with Elm 0.17. You can see the full source code here. It will run on http://elm-lang.org/try.
A number of language and API changes were made in 0.17 that make some of the following recommendations obsolete. You can read about the 0.17 upgrade plan here.
I will leave the original answer for 0.16 untouched below, but you can compare the final gists to see a list of what has changed. I believe the newer 0.17 version is cleaner and easier to understand.
Original Answer for Elm 0.16:
It looks like you're using the Elm REPL. As noted here, you're not going to be able to execute tasks in the REPL. We'll get to more on why in a bit. Instead, let's create an actual Elm project.
I'm assuming you've downloaded the standard Elm tools.
You'll first need to create a project folder and open it up in a terminal.
A common way to get started on an Elm project is to use the StartApp. Let's use that as a starting point. You first need to use the Elm package manager command line tool to install the required packages. Run the following in a terminal in your project root:
elm package install -y evancz/elm-html
elm package install -y evancz/elm-effects
elm package install -y evancz/elm-http
elm package install -y evancz/start-app
Now, create a file at the project root called Main.elm. Here is some boilerplate StartApp code to get you started. I won't go into explaining the details here since this question is specifically about Tasks. You can learn more by going through the Elm Architecture Tutorial. For now, copy this into Main.elm.
import Html exposing (..)
import Html.Events exposing (..)
import Html.Attributes exposing (..)
import Html.Attributes exposing (..)
import Http
import StartApp
import Task exposing (Task)
import Effects exposing (Effects, Never)
import Json.Decode as Json exposing ((:=))
type Action
= NoOp
type alias Model =
{ message : String }
app = StartApp.start
{ init = init
, update = update
, view = view
, inputs = [ ]
}
main = app.html
port tasks : Signal (Task.Task Effects.Never ())
port tasks = app.tasks
init =
({ message = "Hello, Elm!" }, Effects.none)
update action model =
case action of
NoOp ->
(model, Effects.none)
view : Signal.Address Action -> Model -> Html
view address model =
div []
[ div [] [ text model.message ]
]
You can now run this code using elm-reactor. Go to the terminal in your project folder and enter
elm reactor
This will run a web server on port 8000 by default, and you can pull up http://localhost:8000 in your browser, then navigate to Main.elm to see the "Hello, Elm" example.
The end goal here is to create a button which, when clicked, pulls in the list of nytimes repositories and lists the IDs and names of each. Let's first create that button. We'll do so by using the standard html generation functions. Update the view function with something like this:
view address model =
div []
[ div [] [ text model.message ]
, button [] [ text "Click to load nytimes repositories" ]
]
On its own, the button click does nothing. We need to create an Action that is then handled by the update function. The action the button is initiating is to fetch data from the Github endpoint. Action now becomes:
type Action
= NoOp
| FetchData
And we can now stub out the handling of this action in the update function like so. For now, let's change the message to show that the button click was handled:
update action model =
case action of
NoOp ->
(model, Effects.none)
FetchData ->
({ model | message = "Initiating data fetch!" }, Effects.none)
Lastly, we have to cause button clicks to trigger that new action. This is done using the onClick function, which generates a click event handler for that button. The button html generation line now looks like this:
button [ onClick address FetchData ] [ text "Click to load nytimes repositories" ]
Great! Now the message should be updated when you click it. Let's move onto Tasks.
As I mentioned earlier, the REPL does not (yet) support the invoking of tasks. This may seem counterintuitive if you're coming from an imperative language like Javascript, where when you write code that says "go fetch data from this url," it immediately creates an HTTP request. In a purely functional language like Elm, you do things a little differently. When you create a Task in Elm, you're really just indicating your intentions, creating a sort of "package" that you can hand off to the runtime in order to do something that causes side effects; in this case, contact the outside world and pull data down from a URL.
Let's go ahead and create a task that fetches the data from the url. First, we're going to need a type inside Elm to represent the shape of the data we care about. You indicated that you just wanted the id and name fields.
type alias RepoInfo =
{ id : Int
, name : String
}
As a note about type construction inside Elm, let's stop for a minute and talk about how we create RepoInfo instances. Since there are two fields, you can construct a RepoInfo in one of two ways. The following two statements are equivalent:
-- This creates a record using record syntax construction
{ id = 123, name = "example" }
-- This creates an equivalent record using RepoInfo as a constructor with two args
RepoInfo 123 "example"
That second was of constructing the instance will become more important when we talk about Json decoding.
Let's also add a list of these to the model. We'll have to change the init function as well to start off with an empty list.
type alias Model =
{ message : String
, repos : List RepoInfo
}
init =
let
model =
{ message = "Hello, Elm!"
, repos = []
}
in
(model, Effects.none)
Since the data from the URL comes back in JSON format, We'll need a Json Decoder to translate the raw JSON into our type-safe Elm class. Create the following decoder.
repoInfoDecoder : Json.Decoder RepoInfo
repoInfoDecoder =
Json.object2
RepoInfo
("id" := Json.int)
("name" := Json.string)
Let's pick that apart. A decoder is what maps the raw JSON to the shape of the type to which we're mapping. In this case, our type is a simple record alias with two fields. Remember that I mentioned a few steps ago that we can create a RepoInfo instance by using RepoInfo as a function that takes two parameters? That's why we're using Json.object2 to create the decoder. The first arg to object is a function that takes two arguments itself, and that's why we're passing in RepoInfo. It is equivalent to a function with arity two.
The remaining arguments spell out the shape of the type. Since our RepoInfo model lists id first and name second, that's the order in which the decoder expects the arguments to be.
We'll need another decoder to decode a list of RepoInfo instances.
repoInfoListDecoder : Json.Decoder (List RepoInfo)
repoInfoListDecoder =
Json.list repoInfoDecoder
Now that we have a model and decoder, we can create a function that returns the task for fetching the data. Remember, this isn't actually fetching any data, it's merely creating a function which we can hand off to the runtime later.
fetchData : Task Http.Error (List RepoInfo)
fetchData =
Http.get repoInfoListDecoder "https://api.github.com/users/nytimes/repos"
There are a number of ways of handling the variety of errors that can occur. Let's choose Task.toResult, which maps the result of the request to a Result type. It will make things easier on us in a bit, and is sufficient for this example. Let's change that fetchData signature to this:
fetchData : Task x (Result Http.Error (List RepoInfo))
fetchData =
Http.get repoInfoListDecoder "https://api.github.com/users/nytimes/repos"
|> Task.toResult
Note that I'm using x in my type annotation for the error value of Task. That's just because, by mapping to a Result, I'll never have to care about an error from the task.
Now, we're going to need some actions to handle the two possible results: An HTTP error or a successful result. Update Action with this:
type Action
= NoOp
| FetchData
| ErrorOccurred String
| DataFetched (List RepoInfo)
Your update function should now set those values on the model.
update action model =
case action of
NoOp ->
(model, Effects.none)
FetchData ->
({ model | message = "Initiating data fetch!" }, Effects.none)
ErrorOccurred errorMessage ->
({ model | message = "Oops! An error occurred: " ++ errorMessage }, Effects.none)
DataFetched repos ->
({ model | repos = repos, message = "The data has been fetched!" }, Effects.none)
Now, we need a way to map the Result task to one of these new actions. Since I don't want to get bogged down in error handling, I'm just going to use toString to change the error object into a string for debugging purposes
httpResultToAction : Result Http.Error (List RepoInfo) -> Action
httpResultToAction result =
case result of
Ok repos ->
DataFetched repos
Err err ->
ErrorOccurred (toString err)
That gives us a way to map a never-failing task to an Action. However, StartApp deals with Effects, which is a thin layer over Tasks (as well as a few other things). We'll need one more piece before we can tie it all together, and that's a way to map the never-failing HTTP task to an Effects of our type Action.
fetchDataAsEffects : Effects Action
fetchDataAsEffects =
fetchData
|> Task.map httpResultToAction
|> Effects.task
You may have noticed I called this thing, "never failing." That was confusing to me at first so let me try to explain. When we create a task, we're guaranteed a result, but it a success or failure. In order to make Elm apps as robust as possible, we in essence remove the possibility of failure (by which I mainly mean, an unhandled Javascript exception), by explicitly handling every case. That's why we've gone through the trouble of mapping first to a Result and then to our Action, which explicitly handles error messages. To say it never fails is not to say that HTTP problems can't happen, it's to say that we're handling every possible outcome, and errors are mapped to "successes" by mapping them to a valid action.
Before our final step, let's make sure our view can show the list of repositories.
view : Signal.Address Action -> Model -> Html
view address model =
let
showRepo repo =
li []
[ text ("Repository ID: " ++ (toString repo.id) ++ "; ")
, text ("Repository Name: " ++ repo.name)
]
in
div []
[ div [] [ text model.message ]
, button [ onClick address FetchData ] [ text "Click to load nytimes repositories" ]
, ul [] (List.map showRepo model.repos)
]
Lastly, the piece that ties this all together is to make the FetchData case of our update function return the Effect which initiates our task. Update the case statement like this:
FetchData ->
({ model | message = "Initiating data fetch!" }, fetchDataAsEffects)
That's it! You can now run elm reactor and click the button to fetch the list of repositories. If you want to test out the error handling, you can just mangle the URL for the Http.get request to see what happens.
I've posted the entire working example of this as a gist. If you don't want to run it locally, you can see the final result by pasting that code into http://elm-lang.org/try.
I've tried to be very explicit and concise about each step along the way. In a typical Elm app, a lot of these steps will be condensed down to a few lines, and more idiomatic shorthand will be used. I've tried to spare you those hurdles by making things as small and explicit as possible. I hope this helps!