Input field not submitting anything - React - html

I'm working on a project where the front end is powered by React. I'm not even sure if this is relevant (maybe the problem lies in another obvious place), but since I've been able to make everything work without React so far, it would be fair to guess that it's my relationship with React that is resulting in this error.
In my React template (?) I added this most simple piece of code, that is rendered inside a form:
if (this.state.indexColumns['0'].show === true) {
cols.splice(xKey + idxMod, 0, (
<DataTableStaticCell key={'index-' + k}>
{content}
</DataTableStaticCell>
<input type="hidden" name="order_id" value={indexId} />
));
} else {
cols.splice(xKey + idxMod + 1, 0, (
<input type="hidden" name="order_id" value={indexId} />
));
}
When I check using developer tools in browsers, everything is rendered correctly and the field is there, it has the correct value, is located inside a form like it's supposed to be.
Rendered:
<input type="hidden" name="order_id" value="1" data-reactid=".0.2.0.0.1.0.0.$2.2.0.$2.$0.$0.$0.$rows.0.0.2.0.0.1.$0.1">
Now, whenever I post my form, no data is submitted from this field at all. I use Chrome developer tools to catch the form send request (any better methods?) and it does not appear there.
I've tried everything I could think of so far: visible field, not-prefixed-value field, different types of input, different names and such... Some stupid idiotic mistake is ruining my life at the moment, been stuck with this for two days now. I can't even believe that myself.
Appreciate your help :)
EDIT: Form handling:
getFormData: function (validate) {
var invalid = false;
if (validate) {
invalid |= !this.state.form.validate();
if (invalid) {
logger.warn(this.state.form.errors().asData());
}
}
var formData = _.extend({}, this.state.form.cleanedData);
var dataTableKeys = this.findLayoutKeysByType('data_table');
dataTableKeys.forEach(function (tableKey) {
var ref = this.refs['dataTable' + tableKey];
}.bind(this));
return [!invalid, formData];
},
EDIT 2: I should add that <DataTableStaticCell> was already in place before I started working on the project and also contains an <input> field, which is rendered like this for example:
<input type="text" value="" data-reactid=".0.2.0.0.1.0.0.$2.2.0.$2.$0.$0.$0.$rows.0.0.2.0.0.1.$0.$0.1.0">
That is the main reason I started with this approach. Are there any fundamental differences between my data flow and with the example presented later?

Related

HTML components using Typescript, Knockout and Require - HTML is not rendered?

I am trying to learn how component driven development works and I have followed the documents here https://knockoutjs.com/documentation/component-custom-elements.html (Including the many nested links relevant to this topic) however although the TS/JS files are loaded without error the HTML component is never rendered.
Here you can see the the typescript is loaded correctly by RequireJS:
Login-User typescript loaded in browser
And here is the HTML component in the webpage:
HTML component
This is the content within the HTML 'template':
<div class="panel">
<label>Username:</label>
<input type="text" data-bind="Value: $component.Username()" />
<br />
<label>Password:</label>
<input type="text" data-bind="Value: $component.Password()" />
<br />
<label>Valid:</label>
<input type="text" data-bind="Value: $component.ValidUser()" />
</div>
Here is the registration of the HTML template:
const componentName = "Login-User";
ko.components.unregister(componentName);
ko.components.register(componentName, {
viewModel: LoginViewModel,
template: { require: `text!/Views/Components/${componentName}.html` }
});
I do not get any errors in the console but the constructor in the TS file is never hit when adding breakpoints to debug which suggests the me that there was no attempt to actually render the HTML component at all?
I have checked all file paths are correct and deleted and re-compiled the TS files to generate JS files to ensure everything is up to date, I assume I have not properly configured require in some way and so the HTML component is never actually registered however due to no errors being logged I am a bit stuck for where to go next! As I said previously I have read the documentation on Knockout and also for RequireJS however when searching google for issue when implementing a HTML component I seem to only get results for Angular.
Any advice on how to determine the issue would be greatly appreciated, even better if there is any documentation/guides on how to use Knockout/Require/Typescript/HTML Components together which someone could point me at that would be great!
I think I have provided everything need but if not let me know.
Thanks,
Danny
Okay after a couple more hours of trial and error I figured out I had a few problems, for anyone else having this issue try the below resolutions:
I was not calling ko.applybindings();
import * as ko from "knockout";
export default class LoginViewModel {
Username: KnockoutObservable<string>;
Password: KnockoutObservable<string>;
ValidUser: KnockoutComputed<boolean>;
constructor(username: string, password: string) {
this.Username = ko.observable(username);
this.Password = ko.observable(password);
this.computedMethods();
}
private computedMethods(): void {
this.ValidUser = ko.pureComputed(() => {
return this.Username() === "Danny" && this.Password() === "pasword";
});
}
}
const componentName = "login-user";
ko.components.register(componentName, {
viewModel: LoginViewModel,
template: { require: `text!Scripts/Typescript/${componentName}.html` }
});
ko.applyBindings(); << This is important as it actually binds the custom element i.e login-user params="username: 'Danny', password: 'none'"></login-user> and without it nothing will be rendered on the page
After correcting this issue I then got a 404 in console when trying to load the custom element and although the filepath was correct I found that the best way to resolve this was to have the custom element in the same folder as it's TS counter-part:
Before:
template: { require: `text!/Views/Components/${componentName}.html` }
After:
template: { require: `text!Scripts/Typescript/${componentName}.html` }
Google searches recommended installing the following nuget packages although I think only require.text is actually required, if it doesn't resolve your issue it's worth a shot..:
Require Packages
I initially had my components named using camel case i.e Login-User.ts and Login-User.html, I read somewhere that they should be lower case to be valid html 'Tags' and both the .ts and .html files should be named exactly the same
Hope this helps anyone else having issues.

How I use the data from Angular on Node JS? And how can I make a page load the information about a certain "data"?

I'm working on a project that I need from login, to compare the information at the form with the database. And later, after doing the validation, I need to load the information of a login in another page (I have no idea how).
(I tried to find some tutorials, but all of them use Express, that I'm not allowed to)
Now my code:
HTML (I think this part is OK, cause I could save the information in $scope.u)
<form ng-controller = "login" ng-submit="submit(user)">
<label>Login:</label>
<input type="text" ng-model="user.login" required>
<label>Senha:</label>
<input type="password" ng-model="user.pwd" required>
<label><input type="checkbox"> Lembre-me</label>
<button type="submit" class="btn btn-default">Login</button>
<p>{{user.login}}</p>
<p>{{user.pwd}}</p>
<p>LOGIN:{{user.login}}</p>
<p>SENHA:{{user.pwd}}</p>
</form>
Angular (I'm not sure if I understood the idea of $http.post, so I don't know if I can send the info of $scope.u to Nodejs)
app.controller('login',function($scope,$http){
$scope.u = {};
$scope.submit = function(user) {
$scope.u = angular.copy(user);
console.log($scope.u);
};
$http.post('/servico/login', $scope.u).success(function(data, status) {
console.log('Data posted successfully');
});
});
Node (If I could use the information of $scope.u, my problem would be finished there, but I don't know how I can load the information in another page)
The button Login should compare the values from the form and them, maybe, use to send to the other page.
function login(request,response){
var queryString = 'SELECT uLogin,uSenha FROM usuarios';
connection.query(queryString,function(err,rows){
});
}
I hope I've been clear with my doubt.
Thanks for your help.

How do I reset a form including removing all validation errors?

I have an Angular form. The fields are validated using the ng-pattern attribute. I also have a reset button. I'm using the Ui.Utils Event Binder to handle the reset event like so:
<form name="searchForm" id="searchForm" ui-event="{reset: 'reset(searchForm)'}" ng-submit="search()">
<div>
<label>
Area Code
<input type="tel" name="areaCode" ng-model="areaCode" ng-pattern="/^([0-9]{3})?$/">
</label>
<div ng-messages="searchForm.areaCode.$error">
<div class="error" ng-message="pattern">The area code must be three digits</div>
</div>
</div>
<div>
<label>
Phone Number
<input type="tel" name="phoneNumber" ng-model="phoneNumber" ng-pattern="/^([0-9]{7})?$/">
</label>
<div ng-messages="searchForm.phoneNumber.$error">
<div class="error" ng-message="pattern">The phone number must be seven digits</div>
</div>
</div>
<br>
<div>
<button type="reset">Reset</button>
<button type="submit" ng-disabled="searchForm.$invalid">Search</button>
</div>
</form>
As you can see, when the form is reset it calls the reset method on the $scope. Here's what the entire controller looks like:
angular.module('app').controller('mainController', function($scope) {
$scope.resetCount = 0;
$scope.reset = function(form) {
form.$setPristine();
form.$setUntouched();
$scope.resetCount++;
};
$scope.search = function() {
alert('Searching');
};
});
I'm calling form.$setPristine() and form.$setUntouched, following the advice from another question here on Stack Overflow. The only reason I added the counter was to prove that the code is being called (which it is).
The problem is that even after reseting the form, the validation messages don't go away. You can see the full code on Plunker. Here's a screenshot showing that the errors don't go away:
I started with the comment from #Brett and built upon it. I actually have multiple forms and each form has many fields (more than just the two shown). So I wanted a general solution.
I noticed that the Angular form object has a property for each control (input, select, textarea, etc) as well as some other Angular properties. Each of the Angular properties, though, begins with a dollar sign ($). So I ended up doing this (including the comment for the benefit of other programmers):
$scope.reset = function(form) {
// Each control (input, select, textarea, etc) gets added as a property of the form.
// The form has other built-in properties as well. However it's easy to filter those out,
// because the Angular team has chosen to prefix each one with a dollar sign.
// So, we just avoid those properties that begin with a dollar sign.
let controlNames = Object.keys(form).filter(key => key.indexOf('$') !== 0);
// Set each control back to undefined. This is the only way to clear validation messages.
// Calling `form.$setPristine()` won't do it (even though you wish it would).
for (let name of controlNames) {
let control = form[name];
control.$setViewValue(undefined);
}
form.$setPristine();
form.$setUntouched();
};
$scope.search = {areaCode: xxxx, phoneNumber: yyyy}
Structure all models in your form in one place like above, so you can clear it like this:
$scope.search = angular.copy({});
After that you can just call this for reset the validation:
$scope.search_form.$setPristine();
$scope.search_form.$setUntouched();
$scope.search_form.$rollbackViewValue();
There doesn't seem to be an easy way to reset the $errors in angular. The best way would probably be to reload the current page to start with a new form. Alternatively you have to remove all $error manually with this script:
form.$setPristine(true);
form.$setUntouched(true);
// iterate over all from properties
angular.forEach(form, function(ctrl, name) {
// ignore angular fields and functions
if (name.indexOf('$') != 0) {
// iterate over all $errors for each field
angular.forEach(ctrl.$error, function(value, name) {
// reset validity
ctrl.$setValidity(name, null);
});
}
});
$scope.resetCount++;
You can add a validation flag and show or hide errors according to its value with ng-if or ng-show in your HTML. The form has a $valid flag you can send to your controller.
ng-if will remove or recreate the element to the DOM, while ng-show will add it but won't show it (depending on the flag value).
EDIT: As pointed by Michael, if form is disabled, the way I pointed won't work because the form is never submitted. Updated the code accordingly.
HTML
<form name="searchForm" id="searchForm" ui-event="{reset: 'reset(searchForm)'}" ng-submit="search()">
<div>
<label>
Area Code
<input type="tel" name="areaCode" ng-model="areaCode" ng-pattern="/^([0-9]{3})?$/">
</label>
<div ng-messages="searchForm.areaCode.$error">
<div class="error" ng-message="pattern" ng-if="searchForm.areaCode.$dirty">The area code must be three digits</div>
</div>
</div>
<div>
<label>
Phone Number
<input type="tel" name="phoneNumber" ng-model="phoneNumber" ng-pattern="/^([0-9]{7})?$/">
</label>
<div ng-messages="searchForm.phoneNumber.$error">
<div class="error" ng-message="pattern" ng-if="searchForm.phoneNumber.$dirty">The phone number must be seven digits</div>
</div>
</div>
<br>
<div>
<button type="reset">Reset</button>
<button type="submit" ng-disabled="searchForm.$invalid">Search</button>
</div>
</form>
JS
$scope.search = function() {
alert('Searching');
};
$scope.reset = function(form) {
form.$setPristine();
form.$setUntouched();
$scope.resetCount++;
};
Codepen with working solution: http://codepen.io/anon/pen/zGPZoB
It looks like I got to do the right behavior at reset. Unfortunately, using the standard reset failed. I also do not include the library ui-event. So my code is a little different from yours, but it does what you need.
<form name="searchForm" id="searchForm" ng-submit="search()">
pristine = {{searchForm.$pristine}} valid ={{searchForm.$valid}}
<div>
<label>
Area Code
<input type="tel" required name="areaCode" ng-model="obj.areaCode" ng-pattern="/^([0-9]{3})?$/" ng-model-options="{ allowInvalid: true }">
</label>
<div ng-messages="searchForm.areaCode.$error">
<div class="error" ng-message="pattern">The area code must be three digits</div>
<div class="error" ng-message="required">The area code is required</div>
</div>
</div>
<div>
<label>
Phone Number
<input type="tel" required name="phoneNumber" ng-model="obj.phoneNumber" ng-pattern="/^([0-9]{7})?$/" ng-model-options="{ allowInvalid: true }">
</label>
<div ng-messages="searchForm.phoneNumber.$error">
<div class="error" ng-message="pattern">The phone number must be seven digits</div>
<div class="error" ng-message="required">The phone number is required</div>
</div>
</div>
<br>
<div>
<button ng-click="reset(searchForm)" type="reset">Reset</button>
<button type="submit" ng-disabled="searchForm.$invalid">Search</button>
</div>
</form>
And JS:
$scope.resetCount = 0;
$scope.obj = {};
$scope.reset = function(form_) {
$scope.resetCount++;
$scope.obj = {};
form_.$setPristine();
form_.$setUntouched();
console.log($scope.resetCount);
};
$scope.search = function() {
alert('Searching');
};
Live example on jsfiddle.
Note the directive ng-model-options="{allowinvalid: true}". Use it necessarily, or until the entry field will not be valid, the model value is not recorded. Therefore, the reset will not operate.
P.S. Put value (areaCode, phoneNumber) on the object simplifies purification.
Following worked for me
let form = this.$scope.myForm;
let controlNames = Object.keys(form).filter(key => key.indexOf('$') !== 0);
for (let name of controlNames) {
let control = form [name];
control.$error = {};
}
In Short: to get rid of ng-messages errors you need to clear out the $error object for each form item.
further to #battmanz 's answer, but without using any ES6 syntax to support older browsers.
$scope.resetForm = function (form) {
try {
var controlNames = Object.keys(form).filter(function (key) { return key.indexOf('$') !== 0 });
console.log(controlNames);
for (var x = 0; x < controlNames.length; x++) {
form[controlNames[x]].$setViewValue(undefined);
}
form.$setPristine();
form.$setUntouched();
} catch (e) {
console.log('Error in Reset');
console.log(e);
}
};
I had the same problem and tried to do battmanz solution (accepted answer).
I'm pretty sure his answer is really good, but however for me it wasn't working.
I am using ng-model to bind data, and angular material library for the inputs and ng-message directives for error message , so maybe what I will say will be useful only for people using the same configuration.
I took a lot of look at the formController object in javascript, in fact there is a lot of $ angular function as battmanz noted, and there is in addition, your fields names, which are object with some functions in its fields.
So what is clearing your form ?
Usually I see a form as a json object, and all the fields are binded to a key of this json object.
//lets call here this json vm.form
vm.form = {};
//you should have something as ng-model = "vm.form.name" in your view
So at first to clear the form I just did callback of submiting form :
vm.form = {};
And as explained in this question, ng-messages won't disappear with that, that's really bad.
When I used battmanz solution as he wrote it, the messages didn't appear anymore, but the fields were not empty anymore after submiting, even if I wrote
vm.form = {};
And I found out it was normal, because using his solution actually remove the model binding from the form, because it sets all the fields to undefined.
So the text was still in the view because somehow there wan't any binding anymore and it decided to stay in the HTML.
So what did I do ?
Actually I just clear the field (setting the binding to {}), and used just
form.$setPristine();
form.$setUntouched();
Actually it seems logical, since the binding is still here, the values in the form are now empty, and angular ng-messages directive is triggering only if the form is not untouched, so I think it's normal after all.
Final (very simple) code is that :
function reset(form) {
form.$setPristine();
form.$setUntouched();
};
A big problem I encountered with that :
Only once, the callback seems to have fucked up somewhere, and somehow the fields weren't empty (it was like I didn't click on the submit button).
When I clicked again, the date sent was empty. That even more weird because my submit button is supposed to be disabled when a required field is not filled with the good pattern, and empty is certainly not a good one.
I don't know if my way of doing is the best or even correct, if you have any critic/suggestion or any though about the problem I encountered, please let me know, I always love to step up in angularJS.
Hope this will help someone and sorry for the bad english.
You can pass your loginForm object into the function ng-click="userCtrl.login(loginForm)
and in the function call
this.login = function (loginForm){
loginForm.$setPristine();
loginForm.$setUntouched();
}
So none of the answers were completely working for me. Esp, clearing the view value, so I combined all the answers clearing view value, clearing errors and clearing the selection with j query(provided the fields are input and name same as model name)
var modelNames = Object.keys($scope.form).filter(key => key.indexOf('$') !== 0);
modelNames.forEach(function(name){
var model = $scope.form[name];
model.$setViewValue(undefined);
jq('input[name='+name+']').val('');
angular.forEach(model.$error, function(value, name) {
// reset validity
model.$setValidity(name, null);
});
});
$scope.form.$setPristine();
$scope.form.$setUntouched();

IE8/Firefox Behavioral Difference

I'm working on login page written as a JSP. It's pretty simple but behaves differently in IE8 and Firefox (big surprise there). I have not tested this is other browsers yet.
I could probably hack a fix in with some Javascript but I was looking for more information about the behavior, before I just implement a workaround, with hopes of a cleaner fix and avoiding this problem in the future.
The server-side generated HTML snippet in question is this:
<form name="member_session_info" method="post" autocomplete="off" action="/membersession" onsubmit="return validate_form()" >
<input type="hidden" id="memberSessionAction" name="memberSessionAction" value="" />
<input type="hidden" name="homePage" value="/2009/aas/aas_home.jsp" />
<input type="hidden" name="memberLandingPage" value="/2009/aas/aas_member_landing.jsp" />
<input type="hidden" name="memberProfilePage" value="/2009/aas/aas_member_profile.jsp" />
<input type="hidden" name="passwordRecoveryPage" value="/2009/aas/aas_password_recovery.jsp" />
<input id="uname" class="text xsmall right" name="username" value="USERNAME" onclick="checkClickUser()" onKeyPress="checkKeyPress(event, 'login', sendProfile)" style="width: 220px;" type="text">
<input id="pass" class="text xsmall right" name="password" value="PASSWORD" onclick="checkClickPassword()" onKeyPress="checkKeyPress(event, 'login', sendProfile)" style="width: 220px;" type="password">
FORGOT PASSWORD
</form>
and the Javascript that backs it up is:
<script type="text/javascript" language="JavaScript">
function validatePost(code, doAlert)
{
postValid = true;
return postValid;
}
function sendProfile(action)
{
document.getElementById("memberSessionAction").value = action;
document.member_session_info.submit();
return false;
}
function initializePage()
{
}
function validate_form()
{
return false;
}
function checkClickUser()
{
var username;
username = document.getElementById("uname").value;
if (username == "USERNAME") {
// Clear the field since it's the first time
document.getElementById("uname").value = "";
}
return true;
}
function checkClickPassword()
{
var username;
username = document.getElementById("pass").value;
if (username == "PASSWORD") {
// Clear the field since it's the first time
document.getElementById("pass").value = "";
}
return true;
}
function checkKeyPress(event, object, func)
{
var keycode;
if (window.event) keycode = window.event.keyCode;
else if (event) keycode = (event.which) ? event.which : event.keyCode;
else return true;
if ((keycode == 13)) // check for return
{
func(object);
return true;
}
return true;
}
</script>
The basic symptom is this:
If you use tab to navigate from the username field to the password field in the form, the password is correctly highlighted and cleared in FF, but not in IE8. In IE8, tabbing to the password field moves the cursor to the very beginning of the password box, leaving the default value (PASSWORD) in place, and not clearing it.
Any idea on why this occurs? Is this a known bug or inherent flaw of IE8 that I will just have to hack around, or can I just add a wee bit of code somewhere to handle IE8 more correctly?
If the problem isn't clear from my description I can attempt to elucidate or just throw up a screenshot/video clip, or upload a static copy of the HTML somewhere. (If the last one, I could use a recommendation of a good site or service to do this since the actual site is still in dev and not availabile to the web yet.) Thanks!
Edit: Changing the onclick property to onfocus fixed that problem, but brought another one to light (see my comment #David). Could this be related to the way that checkKeyPress is written? It's a function I borrowed from elsewhere in the site. In particular I'm wondering if changing its return statements could be the fix. Maybe it shouldn't return true/false/anything at all?
Edit 2: I removed the checkKeyPress method entirely to see if that was causing the problem, and it changed nothing.
The full source is here. The div that focus randomly jumps to is the one between the two "global nav" comments, at the very top of the body. Still no idea why it's happening. To see if the focus was somehow just getting reset, I added another div above the one that focus is jumping to randomly, expecting focus to start jumping to the new div instead. It didn't. It still switches focus to the div with the image in it. I am utterly befuggled.
What if you put the check in onfocus instead of onclick? Tabbing to a field technically isn't a click anyways.
Since the lost focus seems to happen every 6000 milliseconds, I'd point the blame somewhere at expandone()/contractall() in /js/qm_scripts.js.
Your login form is in the "dropmsg0" div, causing it to be briefly hidden and redisplayed every 6 seconds. The textboxes lose focus in IE8 when hidden. I'd either rename the div to exclude if from the ticker, or modify the ticker script to not run when there's only one dropmsg div.

When submitting a GET form, the query string is removed from the action URL

Consider this form:
<form action="http://www.blabla.com?a=1&b=2" method="GET">
<input type="hidden" name="c" value="3" />
</form>
When submitting this GET form, the parameters a and b are disappearing.
Is there a reason for that?
Is there a way of avoiding this behaviour?
Isn't that what hidden parameters are for to start with...?
<form action="http://www.example.com" method="GET">
<input type="hidden" name="a" value="1" />
<input type="hidden" name="b" value="2" />
<input type="hidden" name="c" value="3" />
<input type="submit" />
</form>
I wouldn't count on any browser retaining any existing query string in the action URL.
As the specifications (RFC1866, page 46; HTML 4.x section 17.13.3) state:
If the method is "get" and the action is an HTTP URI, the user agent takes the value of action, appends a `?' to it, then appends the form data set, encoded using the "application/x-www-form-urlencoded" content type.
Maybe one could percent-encode the action-URL to embed the question mark and the parameters, and then cross one's fingers to hope all browsers would leave that URL as it (and validate that the server understands it too). But I'd never rely on that.
By the way: it's not different for non-hidden form fields. For POST the action URL could hold a query string though.
In HTML5, this is per-spec behaviour.
See Association of controls and forms - Form submission algorithm.
Look at "4.10.22.3 Form submission algorithm", step 17. In the case of a GET form to an http/s URI with a query string:
Let destination be a new URL that is equal to the action except that
its <query> component is replaced by query (adding a U+003F QUESTION
MARK character (?) if appropriate).
So, your browser will trash the existing "?..." part of your URI and replace it with a new one based on your form.
In HTML 4.01, the spec produces invalid URIs - most browsers didn't actually do this though...
See Forms - Processing form data, step four - the URI will have a ? appended, even if it already contains one.
What you can do is using a simple foreach on the table containing the GET information. For example in PHP :
foreach ($_GET as $key => $value) {
$key = htmlspecialchars($key);
$value = htmlspecialchars($value);
echo "<input type='hidden' name='$key' value='$value'/>";
}
As the GET values are coming from the user, we should escape them before printing on screen.
You should include the two items (a and b) as hidden input elements as well as C.
I had a very similar problem where for the form action, I had something like:
<form action="http://www.example.com/?q=content/something" method="GET">
<input type="submit" value="Go away..." />
</form>
The button would get the user to the site, but the query info disappeared so the user landed on the home page rather than the desired content page. The solution in my case was to find out how to code the URL without the query that would get the user to the desired page. In this case my target was a Drupal site, so as it turned out /content/something also worked. I also could have used a node number (i.e. /node/123).
If you need workaround, as this form can be placed in 3rd party systems, you can use Apache mod_rewrite like this:
RewriteRule ^dummy.link$ index.php?a=1&b=2 [QSA,L]
then your new form will look like this:
<form ... action="http:/www.blabla.com/dummy.link" method="GET">
<input type="hidden" name="c" value="3" />
</form>
and Apache will append 3rd parameter to query
When the original query has array, for php:
foreach (explode("\n", http_build_query($query, '', "\n")) as $keyValue) {
[$key, $value] = explode('=', $keyValue, 2);
$key = htmlspecialchars(urldecode($key), ENT_COMPAT | ENT_HTML5);
$value = htmlspecialchars(urldecode($value), ENT_COMPAT | ENT_HTML5);
echo '<input type="hidden" name="' . $key . '" value="' . $value . '"' . "/>\n";
}
To answer your first question yes the browser does that and the reason is
that the browser does not care about existing parameters in the action URL
so it removes them completely
and to prevent this from happening use this JavaScript function that I wrote
using jQuery in:
function addQueryStringAsHidden(form){
if (form.attr("action") === undefined){
throw "form does not have action attribute"
}
let url = form.attr("action");
if (url.includes("?") === false) return false;
let index = url.indexOf("?");
let action = url.slice(0, index)
let params = url.slice(index);
url = new URLSearchParams(params);
for (param of url.keys()){
let paramValue = url.get(param);
let attrObject = {"type":"hidden", "name":param, "value":paramValue};
let hidden = $("<input>").attr(attrObject);
form.append(hidden);
}
form.attr("action", action)
}
My observation
when method is GET and form is submitted, hidden input element was sent as query parmater. Old params in action url were wiped out. So basically in this case, form data is replacing query string in action url
When method is POST, and form is submitted, Query parameters in action url were intact (req.query) and input element data was sent as form data (req.body)
So short story long, if you want to pass query params as well as form data, use method attribute as "POST"
This is in response to the above post by Efx:
If the URL already contains the var you want to change, then it is added yet again as a hidden field.
Here is a modification of that code as to prevent duplicating vars in the URL:
foreach ($_GET as $key => $value) {
if ($key != "my_key") {
echo("<input type='hidden' name='$key' value='$value'/>");
}
}
Your construction is illegal. You cannot include parameters in the action value of a form. What happens if you try this is going to depend on quirks of the browser. I wouldn't be surprised if it worked with one browser and not another. Even if it appeared to work, I would not rely on it, because the next version of the browser might change the behavior.
"But lets say I have parameters in query string and in hidden inputs, what can I do?" What you can do is fix the error. Not to be snide, but this is a little like asking, "But lets say my URL uses percent signs instead of slashes, what can I do?" The only possible answer is, you can fix the URL.
I usually write something like this:
foreach($_GET as $key=>$content){
echo "<input type='hidden' name='$key' value='$content'/>";
}
This is working, but don't forget to sanitize your inputs against XSS attacks!
<form ... action="http:/www.blabla.com?a=1&b=2" method ="POST">
<input type="hidden" name="c" value="3" />
</form>
change the request method to' POST' instead of 'GET'.