How do I repopulate form fields after validation errors with express-form? - validation

Using node.js and express (2.5.9) with express-form.
How should I repopulate form fields with the submitted values?
I have a get and a post route. If there are validation errors when the form is posted, I redirect the user back to the get, the problem is that the repopulated locals don't show up (I do have autoLocals: true, so I assume it's because I am redirecting and res is reset.)
So how do you guys repopulate and what's your application flow, do you res.send instead of res.redirect and set up the whole thing again? That seems repetitive.
Here's an example of my post route:
app.post(
'/projects/:id'
, form(field("title").required("title", "Title is required)
, function (req, res){
if (!req.form.isValid){
res.redirect('/project/'+req.params.id+'/edit');
}
else{
// save to db
}
});

I am working with expressjs4.0 to repopulate the forms fields after validation you do:
router.route('/posts/new')
.get(function(req, res) {
res.render('posts/new', new Post({}));
});
The second argument in res.render below will set some variables in the view.
res.render('posts/new', new Post({}));
In my view I then set my form fields as follows:
...
<input type="text" name="title" value="<%- post.title %>">
<textarea name="article"><%- post.article %></textarea>
...
When you submit this form, it should be caught by your router like so:
router.route('/posts')
.post(function(req, res) {
var post = new Post(req.body)
post.save(function(err) {
if (err) {
res.locals.errors = err.errors;
res.locals.post = post;
return res.render('posts/new');
}
return res.redirect('/posts');
});
...
})
This line of code, resets the form fields in your view
res.locals.post = post;
I hope someone finds this useful ;)

Not sure if it's best practice, but when I have validation failure, I don't redirect I just re-render the view (often by passing control to the 'get' callback). Somethign like this:
function loadProject(req,res, id){ /* fetch or create logic, storing as req.model or req.project */}
function editProject(req,res){ /* render logic */ }
function saveProject(req,res){
if(!req.form.isValid){
editProject(req,res);
}else{
saveToDb(req.project);
res.redirect('/project'+req.project.id+'/edit');
}
}
app.param('id', loadProject);
app.get('/projects/:id/edit', editProject);
app.post('/projects/:id', saveProject);

I had to work on similar problem recently and used two node modules: validator and flashify.
In the form view I configured my form fields as follows:
div.control-group
label.control-label Description
div.controls
textarea(name='eventForm[desc]', id='desc', rows='3').input-xxlarge= eventForm.desc
div.control-group
label.control-label Tag
div.controls
select(id='tag', name='eventForm[tag]')
tags = ['Medjugorje', 'Kibeho', 'Lourdes', 'Fatima']
for tag in tags
option(selected=eventForm.tag == tag)= tag
Notice the naming convention of the form fields. Then in my config file I set one global variable, which is really just a placeholder for when the form first loads:
//locals
app.locals.eventForm = []; // placeholder for event form repopulation
The validation logic is in my router file and looks like this:
app.post('/posts', function(req, res){
var formData = req.body.eventForm;
var Post = models.events;
var post = new Post();
post.text = formData.desc;
post.tag = formData.tag;
// run validations before saving
var v = new Validator();
var isPostValid = true;
// custom error catcher for validator, which uses flashify
v.error = function(msg) {
res.flash('error', msg);
isPostValid = false;
}
v.check(post.text, "Description field cannot be empty").notEmpty();
v.check(post.tag, "Tag field cannot be empty").notEmpty();
Then I check to see there are errors, and if so, pass the form data back to the view:
// reject it
res.render('Event.jade', {page: req.session.page, eventForm: formData});
Notice this evenForm data gets passed back to the view, which repopulates the default values.
The final step is to include the flashify component in your form view.
div(style='margin-top: 60px').container-fluid
include flashify
The code for the flashify view looks like this:
if (flash.error != undefined)
div.container
div.alert.alert-error
b Oops!
button(type='button', data-dismiss='alert').close ×
ul
each error in flash.error
li= error
if (flash.success != undefined)
div.container
div.alert.alert-success
b Success!
button(type='button', data-dismiss='alert').close ×
ul
each success in flash.success
li= success

Related

Laravel render for differend controller method

I'm struggling with the render() method in Laravel 5.
When $whatever->render() is runned, it takes the controller method name as the route by default.
Example:
When i run this command in DelasController#updateFilter, the pagination route is set to whatever.com/marketplace/updateFiler?page=2, which does not make a sense to me.
Problem:
I want to keep the route as simple as whatever.com/marketplace?page=2.
Question:
Can anybody gives me a hint on how to solve this?
Thank you for your time and a discussion.
Looking forward for a reply.
I have an application in which various paginated lists are displayed in "windows" on the page and are updated via AJAX calls to the server. Here's how I did it:
Set up a route to render the whole page, something like this:
Route::get('/marketplace', function ($arguments) {
....
});
Set up a route which will return the current page of the list. For example, it might be something like this:
Route::get('/marketplace/updateFiler', function ($arguments) {
....
});
In your Javascript code for the page, you need to change the pagination links so that, instead of loading the new page with the URL for the link, it makes the AJAX request to the second route. The Javascript could look something like this:
$('ul.pagination a').on('click', function (event) {
// stop the default action
event.stopPropagation();
event.preventDefault();
// get the URL from the link
var url = $(event.currentTarget).attr('href');
// get the page number from the URL
var page = getURLParameterByName(url, 'page');
$.get('marketplace/updateFiler', { page: page }, function (data){
// do something with the response from the server
});
});
The getURLParameterByName function is simply a helper that extracts the named parameter from a URL:
var getURLParameterByName = function (url, name, defaultValue) {
// is defaultValue undefined? if so, set it to false
//
if (typeof defaultValue === "undefined") {
defaultValue = false;
}
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(url);
return results === null ?
defaultValue :
decodeURIComponent(results[1].replace(/\+/g, " "));
};
I adapted this code from an answer I found here on Stack Overflow: https://stackoverflow.com/a/901144/2008384.

Render form with errors via AJAX

I have an action called "myAction" that render the main page of my project. This view (page), is a 2 columns layout, where in the left side have a table with all project in the database.
In the right side i render the project resume or the new project form, as the case may be.
When the user click in "New Project" button, the form is rendered via load() jQuery function. So, when the form is validate, the project is saved and your resume is rendered in the right side of the layout (removing the form), but when the form is invalid i wish render the form with errors.
All form request are sending to newAction() method controller.
The AJAX Request is:
$.ajax({
url: $form.attr('action'),
type: $form.attr('method'),
dataType: 'json',
data: $form.serialize(),
success: function(response){
var object = JSON.parse(response);
if(object.success){
$("#ProjectList").load(Routing.generate('project_my'));
fn_render_resumen(object.message);
}else{
// The form is invalid.
}
}
});
The newAction in ProjectController is:
public function newAction(Request $request){
$project = new Project();
$form = $this->createForm(new ProjectType(), $project);
if($request->isMethod('POST')){
$form->bind($request);
$response = array();
if($form->isValid()){
// Persist in the database...
$response['success'] = true;
$response['message'] = $project->getSlug();
}else{
// Here need send the form with errors to the view.
}
return new JsonResponse(json_encode($response));
}
return $this->render('aView.html.twig',Array('form' => $form->createView()));
}
So, any ideas ? Thanks !
First of all you don't need to json_encode data passed to JsonResponse; Just do this:
return new JsonResponse($response);
Try this:
if($form->isValid()){
// Persist in the database...
$response['success'] = true;
$response['message'] = $project->getSlug();
}else{
$formHtml = $this->container
->get('templating')
->render('aViewForm.html.twig',Array('form' => $form->createView()));
$response['success'] = false;
$response['form'] = $formHtml;
}
return new JsonResponse($response);
Where aViewForm.html.twig is a template that renders only your form. For example it could looks like this:
{{ form }}
And you will have complete HTML code (with errors). Something like this:
{"success":false,"form":"\u003Cform\u003E...\u003C\/form\u003E"}
and then you can process it in JS script.

Joomla - OnMouseover show data from controller in tooltip using Qtip script

I need help to show the data (from controller) in tooltip (using qTip script) on MouseOVer event.
User mouserover on link (Multiple links like in foreach)
id send to a js function like function getData(id){}
Call the PHP function from the controller & using the variable return the data to tooltip.
Show return data into tooltip.
HTML & PHP:
foreach ($rows as $row) {
<a href="#" onmouseover="getData(<?php echo $row->id; ?>)" >Name</a>
}
JS function :
function getData(id)
{
var url='index.php?option=com_test&controller=test&task=getDetails&format=raw';
var data = 'item_id=' + id ;
var request = new Request({
url: url,
method:'post',
data: data,
async: true,
onSuccess: function(responseText)
{
// How i show the "responseText" data here in tooltip using qTip
}
}).send();
}
Controller function:
function getDetails()
{
echo $return = JRequest::getVar('item_id');
}
Would it not be easier to preload the tip in a content item view override (add it to the title= attribute), and adding the call to qTip (one per page) would do the trick.
Also, ajax would make the tooltip not very quick.
Just make sure you properly escape " or ' characters in the title attribute or you will break your markup.
edit: added code. i'm writing this here so there may be typos, but it should give you the idea:
foreach ($rows as $row) {
// let's assume $tip contains the right tip for each row:
Name
}
This first part will show the default browser tooltip.
Now you have to inject the qTip script:
$document = JFactory::getDocument();
$document->addScriptDeclaration('
jQuery("a.tipme").qTip();
');
Now how to put the value in $tip? I see you have a com_test component, you don't need to go through the controller, but can instantiate directly the model that will provide the tooltip; if it's called "tip":
$tipmodel=JModel::getInstance('tip', 'ComtestModel'); // get the model (once!)
$tip = $tipmodel->getTip($row->id);
All the variable, model, controller names in this story are fictional.

Zend Form: onchange select load another view content

In my application I have a form in controller/index that consists out of 3 select boxes. When all three boxes have a value selected I need to show additional html and extra form options in the same view based on those select values. The obvious solution seems to make an ajax call to another action that handles the database operation and creates a view and loading that view into the controller/index.phtml
I have been able to load a view of another action in the index.phtml by using:
$('#select').change(function() {
event.preventDefault();
var id = $(this).attr('id');
$('#results').show();
$('#results').load('/controller/index/' + $(this).attr('value'));
return false;
});
However I need to pass the variables of all three select boxes and for that I alternatively used:
$('#select1').change(function() {
var select1 = $('#select1').val();
var select2 = $('#select2').val();
var select3 = $('#select3').val();
$.ajax({
type: 'POST',
dataType: 'json',
url: '/controller/index/',
data: { select1: select1, select2: select2, select3: select3},
success: function(result){
var return1 = result.return1;
var return2 = result.return2;
}
});
});
The last method works in as far that I do see the variables passed in the headers and the response contains the view, but I cant fix it that just the content of the ajax view is placed within the index view. (Ofcourse by not using AjaxContent switching, the ajax view will load but that includes the complete layout as well.) Anything that I echo in the ajax action or ajax view do not show in the index view. Any pointer would be more than welcome
EDIT
the ajax action now looks like
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$select1 = $this->_request->getParam('select1');
$select2 = $this->_request->getParam('select2');
$select3 = $this->_request->getParam('select3');
// DO THE OTHER STUFF AND LOGIC HERE
$results = array(
'return1' => 'value1',
'return2' => 'value2'
);
$this->_response->setBody(json_encode($results));
and the controller init
public function init() {
$ajaxContext = $this->_helper->getHelper('AjaxContext');
$ajaxContext->addActionContext('ajax', 'json')->initContext();
}
So everything works, I can see the returned values in the response by using developer tool (network) in my browser, however I just do not know how I can use this to "update" the view
You can do two things:
You can enable the layout of the action you are calling via ajax. See you have disabled layout so even if the view phtml file of the ajax action contains something, it won't show. You can enable layout, use text/html dataType instead of json and show the returned HTML somewhere.
Or, in the success event of the ajax call, write javascript codes to update DOM.
Thanks #Salman for your suggestions as they lead me in the right direction and I managed to solve the problem.
I managed to pass multiple parameters with the ajax .load() call by passing them as get parameters.
The results of the ajaxAction could then be formatted in the ajax.ajax.phtml view and were consecutively
shown within the #results div that resides in the index.phtml where the select boxes are.
controller/index.phtml
<div id="results" style="display:block;">Select all three values</div>
IndexController init and ajaxAction
public function init() {
$ajaxContext = $this->_helper->getHelper('AjaxContext');
$ajaxContext->addActionContext('ajax', 'html')->initContext('html');
}
public function ajaxAction() {
$select1 = $this->_request->getQuery('select1');
$select2 = $this->_request->getQuery('select2');
$select3 = $this->_request->getQuery('select3');
$form = new Application_Form();
// Database operations and logic
$this->view->form = $form;
$this->view->array = $somearray;
}
}
jquery script in index.phtml
$(document).ready(function(){
$('.selector').change(function() {
var select1 = $('#select1').val();
var select2 = $('#select2').val();
var select3 = $('#select3').val();
if ( select1 && select2 && select3) {
$('#results').show();
$('#results').load('/controller/ajax?select1=' + select1 + '&select2=' + select2 + '&select3=' + select3);
}
});
});
controller/ajax.ajax.phtml
<?php if ( $this->array ) : ?>
<?php echo( $this->form ); ?>
<?php else: ?>
Nothing found for selected values
<?php endif ?>

Passing the signed_request along with the AJAX call to an ActionMethod decorated with CanvasAuthorize

This is a follow-up to AJAX Call Does Not Trigger Action Method When Decorated With CanvasAuthorize
So I found the following links and it seems that this is a common problem:
http://facebooksdk.codeplex.com/discussions/251878
http://facebooksdk.codeplex.com/discussions/250820
I tried to follow the advice by prabir but I couldn't get it to work...
Here's my setup:
I have the following snippet in the page where the button that triggers the whole post to facebook is located:
#if (!string.IsNullOrEmpty(Request.Params["signed_request"]))
{
<input type="hidden" id="signedReq" value="#Request.Params["signed_request"]" />
}
And then I have this snippet (inside a script tag inside the same page):
var signedRequest = $('#signedReq').val();
$('.facebookIcon').click(function () {
var thisItem = $(this).parent().parent();
var msg = thisItem.find('.compItemDescription').text();
var title = thisItem.find('.compareItemTitle').text();
var itemLink = thisItem.find('.compareItemTitle').attr('href');
var img = thisItem.find('img').first().attr('src');
postOnFacebook(msg, itemLink, img, title, signedRequest);
});
And finally, inside an external js file I have the following function:
/*Facebook post item to wall*/
function postOnFacebook(msg, itemLink, pic, itemTitle, signedReq) {
console.log(signedReq);
var siteUrl = 'http://www.localhost:2732';
$.ajax({
url: '/Facebook/PostItem',
data: {
'message': msg,
'link': siteUrl + itemLink,
'picture': siteUrl + pic,
'name' : itemTitle,
'signed_request': signedReq
},
type: 'get',
success: function(data) {
if(data.result == "success") {
alert("item was posted on facebook");
}
}
});
}
But signedReq is always undefined. And I'm not really sure I should be passing the 'signed_request' field inside the data object. Any thoughts?
Make sure you hidden input field is being populated.
Also, when you try to pull the ID of the input field via JQuery, you might not be referencing the proper element since .NET butcher's ID's of anything that's run on the server.
When I use the hidden input field trick, I set the jquery value like so:
var signedRequest = $('#<%=signedReq.ClientID %>').val();
This way, I'm getting the identifier that .NET is giving to the HTML element.
Hope that helps.
Just a guess - in your hidden field: id="signed_request" instead of id="signedReq"

Resources