Skip to content

Send submitting elements value along with post #390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions jquery.pjax.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,16 +117,22 @@ function handleClick(event, container, options) {
function handleSubmit(event, container, options) {
options = optionsFor(container, options)

var form = event.currentTarget
var form = event.currentTarget,
$form = $(form)

if (form.tagName.toUpperCase() !== 'FORM')
throw "$.pjax.submit requires a form element"

var data = $form.serializeArray()

if (submitButton = $form.data(submitButtonAttr))
data.push(submitButton)

var defaults = {
type: form.method.toUpperCase(),
url: form.action,
data: $(form).serializeArray(),
container: $(form).attr('data-pjax'),
data: data,
container: $form.attr('data-pjax'),
target: form
}

Expand All @@ -135,6 +141,28 @@ function handleSubmit(event, container, options) {
event.preventDefault()
}

var submitClickSelectors = 'form[data-pjax] input[type=submit],\
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this selector works.. Do all pjax forms necessarily have a data-pjax attribute?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Readme infers as much (https://github.com/defunkt/jquery-pjax#pjaxsubmit), but there is nothing cas iron. I think it is should be 'opt-in' functionality though. Do you agree?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to capture the current submit element globally, no matter if a pjax-form is handled atm or not. I see no other way.

Any other ideas @mislav @josh ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to do this. I just think that the form functionality as a whole should be opt-in.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or possibly opt-out? form:not(:pjax-ignore)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are no perf implications I would enable it by default, because then pjax-forms would behave like usual forms, which is what devs would expect in the first place IMO

form[data-pjax] button[type=submit],\
form[data-pjax] button:not([type])',
submitButtonAttr = 'pjax-submit-button-value'

// Private: sends value of submitting element along form post
//
// event - "click" jQuery.Event
//
// Returns nothing.
function handleSubmitClick(event) {
var $submit = $(this),
$form = $submit.closest('form')

if (name = $submit.attr('name')) {
$form.data(submitButtonAttr, { name: name, value: $submit.val() })
}
else {
$form.data(submitButtonAttr, null)
}
}

// Loads a URL with ajax, puts the response body inside a container,
// then pushState()'s the loaded URL.
//
Expand Down Expand Up @@ -798,7 +826,10 @@ function enable() {
maxCacheLength: 20,
version: findVersion
}

$(window).on('popstate.pjax', onPjaxPopstate)

$(document).on('click', submitClickSelectors, handleSubmitClick)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't you better use a submit event.. Click events are sent a lot more often and this could impact performance

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@staabm My understanding was that the submit event can only be bound to a form.

"It can only be attached to

elements." - http://api.jquery.com/submit/

Click events will always result in a submission, unless the default function of the button has been overridden.

Perhaps there is call for an ignored class, that can be applied to buttons you intend to override?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me say it in other words... Can't you just extend the submit handling which pjax already does instead of adding another listener?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@staabm if you can suggest a better approach I'd genuinely be happy to see it. Started down the same thought process as you initially.

However, there is no cross browser solution (that I have found) that allows you to locate the submitting element within the submit event. My understanding is that only Gecko and Trident provide this data.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The submitted button value is only required when pjaxing a form, right? Noone would expect it when using pjax on a link, wouldn't they?

So, adjust handleSubmit and pass the value of a submit button if one was clicked and otherwise submit the value of the fist submit button of the form, in case it was submitted using ENTER key.

You also won't need this hidden field and instead pass the value alongside other data using jquery's ajax() with the data option...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now all the submit stuff is completely opt in. So I don't think any "submit button" click handlers should be initial installed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@josh The click handler is only added to buttons within [data-pjax] forms.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$.pjax.enable() today does not install any click or submit handlers nor make any assumptions on what selectors the elements must use.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a suggestion, we could add a configuration setting for form. The default value would be null (where no binding takes place) or form[data-pjax], depending on your feeling on 'opt-in vs. opt-out'. The setting would be injected into submitClickSelectors, for the benefit of $.pjax.enable().

This would have the advantage of allowing the user to prevent the binding by passing null.

I'm not suggesting that this has any implication for the $.pjax.submit()function, it could remain as it is. All it means is that the user controls the relationship between the form they chose to target with $.pjax.submit() and the form buttons that are automatically bound (or not) with the form configuration setting.

var pjaxContainer = '#pjax-container',
  formSelector = 'form[data-pjax]';

$(document)
  .pjax('a', pjaxContainer, { form: formSelector })
  .on('submit', formSelector, function(event) {
    $.pjax.submit(event, pjaxContainer);
  });

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$.pjax.enable() today does not [...] make any assumptions on what selectors the elements must use.

Right; I keep forgetting what's core behavior and what's not. The default unobtrusiveness of pjax makes this feature tricky to implement.

}

// Disable pushState behavior.
Expand All @@ -822,6 +853,8 @@ function disable() {
$.pjax.reload = function() { window.location.reload() }

$(window).off('popstate.pjax', onPjaxPopstate)

$(document).off('click', submitClickSelectors, handleSubmitClick)
}


Expand Down
6 changes: 5 additions & 1 deletion test/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def title(str)


get '/' do
erb :qunit
erb :qunit, :layout => false
end

get '/jquery.pjax.js' do
Expand Down Expand Up @@ -86,3 +86,7 @@ def title(str)
get '/:page.html' do
erb :"#{params[:page]}", :layout => !pjax?
end

post '/form_post.html' do
erb :form_post, :layout => !pjax?
end
97 changes: 97 additions & 0 deletions test/unit/form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
if ($.support.pjax) {
module("handleSubmit", {
setup: function() {
var self = this
stop()
window.iframeLoad = function(frame) {
self.frame = frame
window.iframeLoad = $.noop
start()
}
$("#qunit-fixture").append("<iframe src='basic_form.html'>")
},
teardown: function() {
delete window.iframeLoad
}
})

asyncTest("Form POST records submitting element's value", function() {
var frame = this.frame

frame.$("#main")
.on("pjax:end", function() {
equal(frame.$("#submit").text(), "submit value")
start()
})

frame.$("[name='submit']").click()
})

module("handleSubmit (multiple submit buttons)", {
setup: function() {
var self = this
stop()
window.iframeLoad = function(frame) {
self.frame = frame
window.iframeLoad = $.noop
start()
}
$("#qunit-fixture").append("<iframe src='form.html'>")
},
teardown: function() {
delete window.iframeLoad
}
})

asyncTest("Form POST records submitting element's value", function() {
var frame = this.frame

frame.$("#main")
.on("pjax:end", function() {
equal(frame.$("#submit").text(), "submit value")
start()
})

frame.$("input[name='submit']").click()
})

asyncTest("Form POST records submitting element's value, when input is a button", function() {
var frame = this.frame

frame.$("#main")
.on("pjax:end", function() {
equal(frame.$("#submit").text(), "submit value")
start()
})

frame.$("button[name='submit']").click()
})

module("handleSubmit (multiple forms)", {
setup: function() {
var self = this
stop()
window.iframeLoad = function(frame) {
self.frame = frame
window.iframeLoad = $.noop
start()
}
$("#qunit-fixture").append("<iframe src='multiple_forms.html'>")
},
teardown: function() {
delete window.iframeLoad
}
})

asyncTest("Form POST records submitting element's value", function() {
var frame = this.frame

frame.$("#main")
.on("pjax:end", function() {
equal(frame.$("#submit").text(), "submit value")
start()
})

frame.$("#form2 [name='submit']").click()
})
}
18 changes: 18 additions & 0 deletions test/views/basic_form.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<%= title 'Form' %>

<form action="/form_post.html" method="post" data-pjax>
<input name="test" type="text" />
<input name="test1" type="text" />
<input name="submit" type="submit" value="submit value" />
</form>

<script type="text/javascript">
$("#main")
.pjax("a")
.on('submit', 'form[data-pjax]', function(evt) {
$.pjax
.submit(evt, $("#main"));
})

window.parent.iframeLoad(window)
</script>
21 changes: 21 additions & 0 deletions test/views/form.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<%= title 'Form' %>

<form action="/form_post.html" method="post" data-pjax>
<input name="test" type="text" />
<input name="test1" type="text" />
<input name="submit3" type="submit" value="submit value 3" />
<input name="submit" type="submit" value="submit value" />
<input name="submit2" type="submit" value="submit value 2" />
<button name="submit" type="submit" value="submit value">Submit Button</button>
</form>

<script type="text/javascript">
$("#main")
.pjax("a")
.on('submit', 'form[data-pjax]', function(evt) {
$.pjax
.submit(evt, $("#main"));
})

window.parent.iframeLoad(window)
</script>
1 change: 1 addition & 0 deletions test/views/form_post.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p id="submit"><%= params[:submit] %></p>
28 changes: 28 additions & 0 deletions test/views/multiple_forms.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<%= title 'Form' %>

<form action="/form_post.html" method="post" data-pjax>
<input name="test" type="text" />
<input name="test1" type="text" />
<input name="submit3" type="submit" value="submit value 3" />
<input name="submit" type="submit" value="submit value form 1" />
<input name="submit2" type="submit" value="submit value 2" />
</form>

<form id="form2" action="/form_post.html" method="post" data-pjax>
<input name="test" type="text" />
<input name="test1" type="text" />
<input name="submit3" type="submit" value="submit value 3" />
<input name="submit" type="submit" value="submit value" />
<input name="submit2" type="submit" value="submit value 2" />
</form>

<script type="text/javascript">
$("#main")
.pjax("a")
.on('submit', 'form[data-pjax]', function(evt) {
$.pjax
.submit(evt, $("#main"));
})

window.parent.iframeLoad(window)
</script>
1 change: 1 addition & 0 deletions test/views/qunit.erb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<script type="text/javascript" src="/unit/pjax.js"></script>
<script type="text/javascript" src="/unit/fn_pjax.js"></script>
<script type="text/javascript" src="/unit/pjax_fallback.js"></script>
<script type="text/javascript" src="/unit/form.js"></script>
</head>
<body>
<div id="qunit"></div>
Expand Down