Submit a Form to Custom URL in Laravel 4 - forms

I have a simple form which allows the User to enter from_date and to_date.Lets say a user enters 2014-09-01 and 2014-09-10 respectively.
How can I get this form submit to a URL ../from_date/2014-09-01/to_date/2014-09-10

You cannot do that but if you need to do something like this, you need to submit to standard class Controller and then resubmit it using redirection:
public function resubmit() {
Redirect::to('/from_date/'.Input::get('from_date').'/to_date/'.Input::get('to_date'))->withInput();
}
But to be honest I don't know why you try to do that. Usually you post data to static url and display content using dynamic urls with pretty urls.

To change the URL once the page has already loaded, you will need to use javascript, as you won't be able to do this using Laravel/PHP because you need access to the dates, which are only selected after the page has loaded.
If your form is similar to:
<form id="dateForm" onsubmit="return submitDateForm()">
<input type="text" id="from_date" name="from_date">
<input type="text" id="to_date" name="to_date">
</form>
Assuming you are using POST for the form submission, and have already imported jQuery, then insert into your view (or a separate .js file which you import) the following jQuery:
function submitDateForm() {
var from_date = $( '#from_date' ).val();
var to_date = $( '#to_date' ).val();
//Send the request
var request = $.post("../from_date/" + from_date + "/to_date/" + to_date);
//prevent the form from submitting itself normally
return false;
}

Related

Method override for expressJS not working for PUT hidden method HTML FORM

Using express and mongoose/mongo to create a todo app. Domain model: Authors have reminders.
In my app.js:
var bodyParser = require('body-parser')
var methodOverride = require('method-override')
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:true}))
app.use(methodOverride('_method'))
my form using handlebars in views/authors/edit.hbs:
<h2>Edit {{name}}</h2>
<form action="/authors/{{_id}}" method="post">
<input type="hidden" name="_method" value="put">
<label>Name:</label>
<input type="text" name="name">
<input type="submit">
</form>
My routing for the edit view and put request to update author:
app.get("/authors/:id/edit", function(req, res){
AuthorModel.findById(req.params.id, function(err, docs){
res.render("authors/edit", docs)
})
})
app.put("/authors/:id", function(req, res){
console.log("updating")
AuthorModel.findById(req.params.id, function(err, docs){
docs.name = req.body.name
docs.save(function(err){
if(!err){
res.redirect("authors/" + req.params.id)
}
})
})
})
When I inspect the elements in the dev tools I see everything I need to see, but when I go to submit, gives me error CANNOT POST. Create functionality works fine, so my first thought is the method override isn't working. If I change put to POST, everything works great.
If it helps, link to the repo that contains this app
Judging by the documentation, there are two common ways to override the method:
using a header;
using a query string parameter;
You're using the "old" method of specifying a form variable, which you can still use, but this requires custom logic on the server side.
It seems to me that the easiest way for you to fix this is to use the query string parameter:
<form action="/authors/{{_id}}?_method=put" method="post">

Why do i not get through to my Meteor.method on the server?

I'm trying to add a url to the logged in users collection. My final goal at least is to be able to add a field e.g {profilePicture: 'http://randompic.com/123.png'}
What i've tried so far is:
<template name="profile">
<h1>My Profile</h1>
<form class="form-inline"action="">
<label for="url"></label>
<input class="input input-large" type="url" name="url" placeholder="URL for you image..." id="url">
<button class="btn btn-success submit">Update profile picture</button>
</form>
</template>
When the user will press the Update profile picture -button i send it to this helper function:
Template.profile.events({
'click .submit': function (evt, tmpl) {
var userid = Meteor.userId();
var url = tmpl.find('#url').value;
var options = {_id: userid, profilePicture: url};
Meteor.call('addToProfile', options);
}
});
I have tried to alert out option._id and options.profilePicture and i have that data availble.
Now when i pass it along to my server.js file i get no output of my alert:
Meteor.methods({
'addToProfile': function(options) {
//Not even this alert will show..
alert(options._id); Edit: console.log() works on the server thought.
}
})
So that is my first issue.
The second problem (to come) is that i don't know how to update/add to the users collection with this profilePicture data. Would really appreciate if someone could contribute with a small example of that part.
Based on the comments everything seems to be functioning as expected. It appears that you are just trying to update some user data on the client. Since you have the insecure package removed you need to validate the updates on the server (that the client requests), this is how you would do that:
// only applies to client requests
Meteor.users.allow({
// validation for user inserts
insert: function ( userId, doc ) {
// perform any checks on data that you want to perform, like checking to see if the current user is an admin.
// this check just requests that a user be logged in
return userId;
}
,
// is your validation for user updates
update: function ( userId, doc, fields, modifier ) {
// again, perform any validation you want. A typical check is to make sure they didn't add any extra fields.
// this makes sure a user is logged in and that they are only attempting to update themself
return userId === doc._id;
}
});
There are some more thorough examples in the docs. Now you can just call update like you normally would and rely on the server to perform any validation!

Symfony2, Twig, Routes, Create valid url from twig with slug

question is how to create such route as hpp://localhost/search/{searchString}
I played around but...
Simple form is (twig)
<form class="form-search" action="{{ path ('site_search') }}" method="get">
<input type="text" name="string" value="{% trans %}Search{% endtrans %}..."/>
</form>
Route is
site_search:
pattern: /search/{string}
defaults: { _controller: MainSiteBundle:Search:default, string: null }
methods: [GET]
Now i get such url http://local.site/search?string=some but i want get http://local.site/search/some
Or what is the best solution for _GET form url ?
That's the way html works .. form method get will append ?inputname=inputvalue and post will send a post request to the given action url. ( but not append something to the action like /inputvalue )
you could change the form action dynamically with javascript or use a RewriteRule in your webserver configuration in order to achieve what you're looking for.
A url rewrite might be the better option in my oppinion. Users directly accessing /search/term will not be redirected, users submitting the form will and therefore see the correct url in their browsers.
Why not just use method="post" and inside your action redirect the user to /search/term.
public function searchAction($string = null, Request $request)
{
if ( !($request->get('string') && !($string) ) {
// ... render your form here
// return form
}
// ... perform search here
// return search results
}
That's how search is usually implemented with a form. Post the search-term get redirected to the results.

How to keep changed form content when leaving and going back to HTTPS page? (works with HTTP)

Enter/change something in a textarea
Before submitting the form, leave the page (e.g. by clicking browser's back button)
Go back to the edit page (e.g. by clicking the forward button)
Expected result: the content entered in the textarea should still be there
Actual result:
with HTTPS: all changes are gone (bad!)
with HTTP: the changes are still there (good!)
Why is this happening when using HTTPS? How can I prevent this? Is the browser or the website responsible?
You can consider the following solutions:
The autocomplete Attribute (HTML5)
This seems unrelated since autocomplete tells the browser to complete fields with the values based on earlier user input which were "submitted" with the form. But in my tests I saw that; after filling out the form without submitting; when I hit the forward (history) button and hit back again; form fields were auto-filled if I set autocomplete="on" and all were cleared when set to "off".
So; (if targeting HTML5 users) you can use this attribute to "cache" your form data. (Works on all major browsers, except Opera).
<form action="/update" method="post" autocomplete="on">
Email: <input type="text" id="email" /><br />
Username: <input type="text" id="uname" /><br />
Password: <input type="password" id="pwd" autocomplete="off"/><br />
<input type="submit" />
</form>
Notice that you can set the auto-complete feature off for a specific field (password in this case) when the rest of the form controls are on.
MSDN Remarks:
If the autocomplete attribute is missing, the field will default to an 'on' state if element has no parent form, or if the form has
autocomplete set to 'on'.
Information provided by the AutoComplete feature is not exposed to
the object model, and is not visible to a Web page until the user
selects one of the suggestions as a value for the text field.
Save the Un-submitted Form Data Locally:
You can store the input data locally, right before the page redirection or on focus-out event of every form control:
Cookies
The good-old cookies can come handy in this case but you should consider the down-sides:
Even though you can encrypt the values programmatically; since we will be working on the client-side, cookies are not truly secure for this. Http-Only and Secure marked cookies will not help us here, because these options are used to enforce SSL when the cookie is "sent" (secure) and cannot be accessed from Javascript (http-only).
Browsers have a cookie size limit. From MSDN: "Most browsers support
cookies of up to 4096 bytes. Because of this small limit, cookies
are best used to store small amounts of data". So, if you don't
watch for this size (when you write the cookie and/or by limiting
the control's value via maxlength attributes); that could be a
problem. (and trimming the value is the worst thing in this case).
Browsers also have a limit to the number of cookies that can be set
per domain. So; when storing the form data in the cookies; instead of setting cookies for each form field value; you should merge them into one or few cookies; for your site not to
exceed this limit.
Still, the bright side is they are supported by all browsers and if you don't plan to "cache" sensitive and too-long data via Cookies, then you can use the following solution. If this is not the case; you should better go with the next suggestion: localStorage.
// Below is just a demonstration and is not tested thoroughly for
// production-ready web applications by any means.
// But it should give you an idea.
/**
* Caches the user-input data from the targeted form, stores it in the cookies
* and fetches back to the form when requested or needed.
*/
var formCache = (function () {
var _form = null,
_formData = [],
_strFormElements = "input[type='text'],"
+ "input[type='checkbox'],"
+ "input[type='radio'],"
// + "input[type='password']," // leave password field out
+ "input[type='hidden'],"
// + "input[type='image'],"
+ "input[type='file'],"
// more input types...
+ "input[type='email'],"
+ "input[type='tel'],"
+ "input[type='url'],"
+ "select,"
+ "textarea";
function _warn() {
console.log('formCache is not initialized.');
}
return {
/**
* Initializes the formCache with a target form (id).
* You can pass any container id for the formId parameter, formCache will
* still look for form elements inside the given container. If no form id
* is passed, it will target the first <form> element in the DOM.
*/
init: function (formId) {
var f = (typeof formId === 'undefined' || formId === null || $.trim(formId) === '')
? $('form').first()
: $('#' + formId);
_form = f.length > 0 ? f : null;
console.log(_form);
return formCache; // make it chainable
},
/**
* Stores the form data in the cookies.
*/
save: function () {
if (_form === null) return _warn();
_form
.find(_strFormElements)
.each(function() {
var f = $(this).attr('id') + ':' + formCache.getFieldValue($(this));
_formData.push(f);
});
docCookies.setItem('formData', _formData.join(), 31536e3); // 1 year expiration (persistent)
console.log('Cached form data:', _formData);
return formCache;
},
/**
* Fills out the form elements from the data previously stored in the cookies.
*/
fetch: function () {
if (_form === null) return _warn();
if (!docCookies.hasItem('formData')) return;
var fd = _formData.length < 1 ? docCookies.getItem('formData').split(',') : _formData;
$.each(fd, function (i, item) {
var s = item.split(':');
var elem = $('#' + s[0]);
formCache.setFieldValue(elem, s[1]);
});
return formCache;
},
/**
* Sets the value of the specified form field from previously stored data.
*/
setFieldValue: function (elem, value) {
if (_form === null) return _warn();
if (elem.is('input:text') || elem.is('input:hidden') || elem.is('input:image') ||
elem.is('input:file') || elem.is('textarea')) {
elem.val(value);
} else if (elem.is('input:checkbox') || elem.is('input:radio')) {
elem.prop('checked', value);
} else if (elem.is('select')) {
elem.prop('selectedIndex', value);
}
return formCache;
},
/**
* Gets the previously stored value of the specified form field.
*/
getFieldValue: function (elem) {
if (_form === null) return _warn();
if (elem.is('input:text') || elem.is('input:hidden') || elem.is('input:image') ||
elem.is('input:file') || elem.is('textarea')) {
return elem.val();
} else if (elem.is('input:checkbox') || elem.is('input:radio')) {
return elem.prop('checked');
} else if (elem.is('select')) {
return elem.prop('selectedIndex');
}
else return null;
},
/**
* Clears the cache and removes the previously stored form data from cookies.
*/
clear: function () {
_formData = [];
docCookies.removeItem('formData');
return formCache;
},
/**
* Clears all the form fields.
* This is different from form.reset() which only re-sets the fields
* to their initial values.
*/
clearForm: function () {
_form
.find(_strFormElements)
.each(function() {
var elem = $(this);
if (elem.is('input:text') || elem.is('input:password') || elem.is('input:hidden') ||
elem.is('input:image') || elem.is('input:file') || elem.is('textarea')) {
elem.val('');
} else if (elem.is('input:checkbox') || elem.is('input:radio')) {
elem.prop('checked', false);
} else if (elem.is('select')) {
elem.prop('selectedIndex', -1);
}
});
return formCache;
}
};
})();
// Save form data right before we unload the form-page
$(window).on('beforeunload', function (event) {
formCache.save();
return false;
});
// Initialize and fetch form data (if exists) when we load the form-page back
$(document).on('ready', function (event) {
formCache.init().fetch();
});
Here is a working demo on jsFiddle.
Note: The "cookies reader/writer" script from developer.mozilla.org should be included with the code above. You can also use Yahoo's YUI 2: Cookie Utility which has a useful setSub() method for setting sub-cookies inside a single cookie, for the browser limit that I previously mentioned.
localStorage
You can also use more modern techniques like localStorage (HTML5). It is more secure and faster. All major browsers support this feature including IE 8+. (Additionally, iOS and Android support!)
if (typeof Storage !== 'undefined') { // We have local storage support
localStorage.username = 'Onur'; // to save to local storage
document.getElementById('uname').value = localStorage.username; // to fetch from local storage
}
So, just like in the cookies example;
$(window).on('beforeunload', function (event) {
saveFormToLocalStorage();
return false;
});
$(document).on('ready', function (event) {
fillFormFromLocalStorage()
});
SessionStorage
This works pretty much the same way. From W3C: The sessionStorage object is equal to the localStorage object, except that it stores the data for only one session.
Save Form Data to Server/DB via Silent AJAX Post(s):
Not a very efficient way but you might want to use this where others are not feasible. You can make the post on the beforeunload event and prompt a message to the user.
$(window).on('beforeunload', function (event) {
//check if at least one field is filled out.
//make the AJAX post if filled out.
return "You are leaving the page without submitting the form...";
});
Retrieve Previously Saved Data from Server on Page Load:
Just to remind you; if the user is filling out an "update" form, for example; you can always fetch the previously saved data from the server and automatically fill in the form (non-sensitive fields).
Conclusion
If you really need this and worth the trouble; you should consider a cross-browser solution that implements a fall-back mechanism; such as:
IF you have support for HTML5 features; use HTML5 autocomplete
attribute. (You can embed the attribute in the HTML beforehand, or
set it via Javascript/jQuery when you test for browser support.)
ELSE IF you have support for the Storage object; go with
localStorage;
ELSE IF [cookies your current session stores] + [cookie size your
form data needs] < 4096 bytes; then use cookies.
ELSE IF you have a server-side web-app make silent AJAX requests to
store data on server.
ELSE don't do it.
Note: For HTML5 feature detection, take a look at this page or this page or you can use Modernizr.
HTTPS Problem:
The reason, all form changes are gone when using HTTPS is that; it is a secure protocol. Forms are mostly used for user input and can (probably) contain sensitive data. So this behavior seems natural and expected. The solution(s) I offer above will work the same as they do on HTTP. So that should cover all your concerns.
Further reading:
Autofilling form controls: the autocomplete attribute
HTML5 form autocomplete attribute
DOM Storage
HTML5 Web Storage
Future of Local Storage for Web
Cookies
This is what worked for me.
<select
class="form-select custom-select page-number-select"
(change)="onPageChange($event)"
data-test="XXXX"
[attr.aria-labelledby]="XXXX"
[value]="pageNumber" <---- This fixed the problem
>
<ng-container
*ngFor="let pageNumber of totalPageCount"
>
<option value="{{ pageNumber }}" [attr.selected]="pageNumber == page ? '' : null" >
{{ t('pageN', { pageNumber: pageNumber }) }}
</option>
</ng-container>
</select>
Adding the data coming from the stream in the value attribute ensured that the correct value is shown at all times. Even upon browser's popstate events (back and forward button clicks)

callback function after submitting a form

I have the following form :
Is there a way to add a callback function to the form after it is submitted? Basically I am echoing the result of upload.php to an iframe and what I want to do is to get that information using a callback function.
You want to use Ajax to submit the post, and have the javascript catch the echo in a result and write out the HTML to the iframe.
As Gnostus says: use Ajax to submit the form, and attach a function for when the ajax has completed and has received a response... something like this (javascript)
// microsoft does their XMLHttpRequest differently, so make a different object for them.
var xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState==4 && xmlhttp.status==200) {
// this point is reached when ajax has completed
var output = xmlhttp.responseText;
doWhateverFunctionYouLike(output);
}
xmlhttp.open("GET","http://some.url.here,true);
xmlhttp.send();
Don't forget to get the values out of your form, do something like:
val1 = form.getElementsByTagName("input")[0].value;
val2 = form.getElementsByTagName("input")[1].value;
and submit them with the ajax call...
xmlhttp.open("GET", "http://some.url.here/?a=" + val1 + "&b=" + val2, true);
you get the idea.