AJAX Form will only either show success message OR post data to database not both - ajax

I am using Codeigniter as my framework and have a simple contact form. This uses the form helper and i have used AJAX and a fallback in the controller if AJAX is not present.
At the moment, my code with only either show the success message from the ajax form OR post the data to the database depending on if i change them around in the controller - my error messages work fine.
I am confused to how it will not both post and show success message - i think i may be missing something in my controller or AJAX request?
Here is my code as a guidance and if anyone can spot anything that would be great as it's getting on my nerves now!
*The code i am posting now lets the data be posted into the database. When i move the post data elements below this -> return $this->output->set_output(json_encode($respond)); It doesn't post to the database but shows the success message and vice versa.
CONTROLLER,
// if ajax request
if($this->input->is_ajax_request()) {
$respond = array();
if($this->form_validation->run() == FALSE) {
$respond['result'] = 'false';
$respond['error_message'] = $error_message;
$respond['errors'] = validation_errors();
// set individual errors - for warning classes
$respond['first_name_error'] = form_error('first_name');
$respond['country_error'] = form_error('country');
$respond['email_error'] = form_error('email');
$respond['message_error'] = form_error('message');
} else {
$respond['result'] = 'true';
$respond['success_message'] = $success_message;
// add contact message to the database
$this->contact_model->insert_contact_message($curr_lang, $this->input->post('first_name'), $this->input->post('country'), $this->input->post('email'), $this->input->post('phone'), $this->input->post('message'));
}
return $this->output->set_output(json_encode($respond));
} else {
// if ajax request failed - use CI
if($this->form_validation->run() == FALSE) {
$data['error_message'] = $error_message;
$data['errors'] = validation_errors();
} else {
// add contact message to the database
$this->contact_model->insert_contact_message($curr_lang, $this->input->post('first_name'), $this->input->post('country'), $this->input->post('email'), $this->input->post('phone'), $this->input->post('message'));
$data['success_message'] = $success_message;
}
}
// set field labels
$data['first_name'] = $first_name;
$data['country'] = $country;
$data['email'] = $email;
$data['phone'] = $phone;
$data['message'] = $message;
// initialize view name
$data['content'] = $page;
// load the view
$this->load->view('template', $data);
}
AJAX
$('#submit').click(function(e) {
e.preventDefault();
// send the form data to the controller
$.ajax({
url: $(this).attr('action'),
type: 'POST',
data: $('form').serialize(),
dataType: 'json',
success: function(respond) {
if(respond.result === 'false'){
// function to add warning class
function add_error(response, field){
if(response){
$(field).addClass('warning');
}
}
// add warning classes - doing this individually as some inputs have more than one error message
add_error(respond.first_name_error, 'input[name="first_name"]');
add_error(respond.country_error, 'input[name="country"]');
add_error(respond.email_error, 'input[name="email"]');
add_error(respond.message_error, 'textarea');
// post all errors to the view
var error_msg = respond.error_message + respond.errors;
$('#error_message').html(error_msg);
}
if(respond.result === 'true'){
// empty the form
$('#error_message').empty();
$('form').find("input[type=text], textarea").val('');
// set the success message
var success_msg = respond.success_message;
$('#success_message').html(success_msg).fadeOut(6000);
}
}
});
return false;
});

It's likely because you aren't parsing the JSON response so your if statements will never be true (as respond.result is probably evaluating to 'undefined').

In your Ajax respond.result === true or false not 'true' or 'false'. You just need to remove the quotes because it is a Boolean not a string.

Related

How to render ajax response to view

Here is my predicament: I need to render json response received from controller method. I do this by calling clicking on navbar item "List Articles" which activate method ajaxIndex(). Then tat method makes request to route which in turn call controller method also called ajaxIndex(). That method then gater all articles and sends it as a response. After that, that response i can't control, it just renders raw json ...
Navbar item:
<a class="nav-link" href="/articles" onclick="ajaxIndex(this)"> List Articles </a>
Route:
Route::get('/articles', "ArticlesController#ajaxIndex");
Method in ArticlesController
public function ajaxIndex(Request $request)
{
$var1 = $request->var1;
$var2 = $request->var2;
$elem = $request->elem;
$currUser = auth()->user();
$currUri = Route::getFacadeRoot()->current()->uri();
$articles = Article::orderBy("created_at","desc")->paginate(5);
$html = view('articles.List Articles')->with(compact("articles", "var1", "var2", "elem", "currUser", "currUri"))->render();
//return $request;
return response()->json(["success"=> true, "html" => $html], 200);
//return response()->json(["success"=> $articles,"var1"=> $var1, "var2"=> $var2, "elem"=> $elem, "currUser" => $currUser, "currUri" => $currUri], 200);
}
and here my ajax method
function ajaxIndex(me,formId){
let var1 = "gg";
let var2 = "bruh";
let token = document.querySelector("meta[name='csrf-token']").getAttribute("content");
let url = "/articles";
if(formId){
let form = $("#"+formId).serialize();
console.log(form);
}
$.ajax({
type: "GET",
url: url,
headers:{
"X-CSRF-TOKEN": token
},
data: {/*
var1: var1,
var2: var2,
elem: {
id: me.id ? me.id : null,
class: me.className ? me.className : null,
value: me.value ? me.value : null,
innerHTML: me.innerHTML ? me.innerHTML : null,
}
*/},
success: (data) => {
console.log(data);
$('#maine').html(JSON.parse(data.html));
},
error: (data) => {
console.log(data);
}
});
}
How to render acquired data to particular view?
Now just renders json response alongside html.
My question is how to render response itself and where goes response from controller method. I tried console logging it when route is hit, but there is nothing in console. What is actual approach or what i need to change to achieve this?
Addendum: "For List Articles you will send ajax request to rest api where it returns array of objects(articles)". I assumed i needed to make ajax request, after being sent to appropriate blade, i should now display sent data? Am i getting wrong something? ...
Edit1:
Now when i go to any page in my app, for example:
http://articleapp.test/articles?page=2
it shows json response:
Edit2:
I also modified my ajax method to correctly display current page for article listing. Problem start when try to go to next page.
Here is the code:
function ajaxIndex(me,formId){
let token = document.querySelector("meta[name='csrf-token']").getAttribute("content");
let url = "/articles";
if(formId){
let form = $("#"+formId).serialize();
console.log(form);
}
$.ajax({
type: "GET",
url: url,
headers:{
"X-CSRF-TOKEN": token
},
data: {},
success: (data) => {
console.log(data);
let html = "<div class='container'>";
let articleBody = "";
let pagination = "<ul class='pagination'><li class='page-item'><a class='page-link' href='#'>Previous</a></li>";
if(data.articles.data.length > 0){
for(let i=0;i<data.articles.current_page;i++){
let created_at = data.articles.data[i].created_at.replace(/-/g,"/").split(" ")[0];
html += "<div class='row' style='background-color: whitesmoke;'><div class='col-md-4 col-sm-4'><a href='/articles/"+data.articles.data[i].id+"'><img class='postCover postCoverIndex' src='/storage/images/"+data.articles.data[i].image+"'></a></div><div class='col-md-8 col-sm-8'><br>";
if(data.articles.data[i].body.length > 400){
articleBody = data.articles.data[i].body.substring(0, 400);
html += "<p>"+articleBody+"<a href='/articles/"+data.articles.data[i].id+"'>...Read more</a></p>";
}
else{
html += "<p>"+data.articles.data[i].body+"</p>";
}
html += "<small class='timestamp'>Written on "+created_at+" by "+data.articles.data[i].user.name+"</small></div></div><hr class='hrStyle'></hr>";
history.pushState(null, null, "/articles?page="+(i+1));
}
for(let i=0;i<data.articles.total;i++){
//console.log(data.articles.data[i].id);
pagination += "<li class='page-item'><a class='page-link' href='/articles?page="+(i+1)+"'>"+(i+1)+"</a></li>";
}
pagination += "<li class='page-item'><a class='page-link' href='#'>Next</a></li></ul>";
}
html+="<div class='d-flex' style='margin: 10px 0px;padding-top: 20px;'><div class='mx-auto' style='line-height: 10px;'>"+pagination+"</div></div></div>";
$('#maine').html(html);
//?page=2
},
error: (data) => {
console.log(data);
}
});
}
When i go to next page, it shows json response as i previously stated. Look in the image above. It won't render ...
In this case ajax response should contain only the real content you want to get with the assynchronous request (html tags inside body). Your #maine element should be a div or another structure capable of having html child tags.
Ps.: If you want to render the ajax response like another page by changing header tags and maybe even the http content type then the response should be load inside an iframe tag.
**Edit: ** In pratice, delete the previous content before body tag in the view returned by ajax. And #maine must be a to contain the ajax response.

DOMPDF not downloading file when using AJAX

I'm trying to work with the BarryVdh/DOMPDF code in my Laravel project.
I made a page with a print button, with
Print
This is calling the controller function :
public function printFacturen(Request $request) {
$facturen = Factuur::all();
view()->share('facturen', $facturen);
$pdf = PDF::loadView('pdf.facturen');
return $pdf->download('invoice.pdf');
}
This is successfully downloading the PDF file.
My route is :
Route::get('/print-facturen', 'PrintController#printFacturen')->name('print_overzicht_facturen');
But, I need the content of a radio button to fill my PDF instead.
So I change my a href to
Print
I add a jQuery function :
$(".printbtn").click(function(e)
{
var option = $("input[name='factuur_selectie']:checked").val();
$.ajax({
type: 'POST',
url: 'print-facturen',
data: {"optionID": option}
})
});
And my controller is changed to
public function printFacturen(Request $request) {
$option = $request->get('optionID');
$facturen = Factuur::all();
$searchFacturen = new \Illuminate\Database\Eloquent\Collection();
foreach ($facturen as $factuur) {
if ($option == 1) {
$searchFacturen->add($factuur);
}
else if ($option == 2) {
if ($factuur->voldaan == true) {
$searchFacturen->add($factuur);
}
}
else if ($option == 3) {
if ($factuur->voldaan == false) {
$searchFacturen->add($factuur);
}
}
}
view()->share('facturen', $searchFacturen);
$pdf = PDF::loadView('pdf.facturen');
return $pdf->download('invoice.pdf');
}
I can see my optionID successfully, but the PDF file is NOT being downloaded anymore ... :-(
As I got a POST error, I added this route :
Route::post('/print-facturen', 'PrintController#printFacturen')->name('print_overzicht_facturen');
When inspecting the network, I see this :
SORRY, I'm not allowed yet to post pictures here :-(
(https://user-images.githubusercontent.com/5870500/32404394-3555952c-c14f-11e7-82c3-2d000d1a2661.png)
What am I doing wrong ?
Best regards,
Davy
You need to set proper http response headers:
header('Content-Type: application/octet-stream; charset=utf-8');
header('Content-Disposition: attachment; filename="'.$filename.'"');
Other simple option to do it will be to dynamic modify link on radio click to get link like: example.org/download?radio=1

Ajax call not returning a response, just reloads page

I have a Symfony 3 CRM and I use ajax calls to action the removal of items throughout the system. It uses a single call and then uses a switch statement to determine what it is the user is attempting to delete and handles it accordingly.
However, for some strange reason one particular type of item doesn't seem to work, it just reloads the page.
Here is the trigger button (I am implementing bootstrap confirmation):
<a href="" data-type="unit" id="{{ unit.id }}"
data-toggle="confirmation-singleton"
data-btn-ok-class="btn btn-xs btn-success"
data-btn-cancel-class="btn btn-xs btn-danger"
class="btn btn-xs btn-danger remove-item">
<i class="fa fa-remove no-override"> </i>
</a>
My ajax call for removal of items:
$('.remove-item').confirmation({
rootSelector: '[data-toggle=confirmation-singleton]',
container: 'body',
onConfirm: function() {
var type = $(this).attr('data-type');
var id = $(this).attr('id');
var data = type + '|' + id;
$.ajax( '/app_dev.php/ajax-call/remove-item/' + data )
.done( function(response) {
if(response != 'success') {
if(response == 'units_exist') {
alert("You cannot delete this item as there are units already linked to it.");
} else if(response == 'no_property') {
alert("Sorry! Property could not be found.");
} else if(response == 'bookings_exist') {
alert("Sorry! This unit has bookings. Please delete the bookings first.");
}
}
});
return false;
},
onCancel: function() {
return false;
}
});
And on the PHP side, for this particular example:
$data = $request->get('data');
$parts = explode("|",$data);
$type = $parts[0];
$id = $parts[1];
// using switch on $type
case 'unit':
$em = $this->getDoctrine()->getManager();
$repo = $em->getRepository('AppBundle:Unit');
$booking_repo = $em->getRepository('AppBundle:Booking');
$bookings = $booking_repo->findBy(array('unitId' => $id)); // check to see if any bookings exist
if(!empty($bookings)) {
return new Response('bookings_exist');
} else {
$item = $repo->findOneBy(array('id' => $id));
if(!empty($item)) {
$em->remove($item);
$em->flush();
}
}
break;
In this example, it SHOULD return 'bookings_exist' and if I directly go to the URL in the browser, it does display this message - however, all it does it reload the page instead of throwing the alert as stipulated in the ajax call. I know this call works as it does successfully delete other items in the CRM, it just seems to be when it cannot delete it due to a condition such as this.
I may be missing something really obvious here, so any help is appreciated.
For jQuery Ajax, use success and error handlers
Other handlers in jQuery's Ajax object are unreliable at best, and vary in their behavior and support between versions and browsers.
Prevent Default is generally a good idea with ajax handled events
Should jQuery fail, and NOT return false, the element will do it's default behavior, which in your case is which reloads the page.
onConfirm: function(e) {
e.preventDefault();
var type = $(this).attr('data-type');
var id = $(this).attr('id');
var data = type + '|' + id;
$.ajax( '/app_dev.php/ajax-call/remove-item/' + data )
.success( function(response) {
if (response.errorMessage) {
alert(response.errorMessage);
}
})
.error( function(xhr, status, error) {
console.log(status + '\n' + error);
})
;
return false;
}
PHP Side, build a JSONResponse
if(!empty($bookings)) {
return new JsonResponse([
'errorMessage' => 'Sorry! Property could not be found.'
);
}
instead of just adding .done() you should also use
.fail(function( jqXHR, textStatus, errorThrown ) {});
to catch any errors.
If the bookings is not empty then the function will return the new response 'booking_exist' and stop ... it will not proceed to next statments .
So if you need to delete the item use this code instead :
if(empty($bookings)) {
return new Response('bookings_not_exist');
} else {
$item = $repo->findOneBy(array('id' => $id));
if(!empty($item)) {
$em->remove($item);
$em->flush();
}

How to prevent validate function call while calling model.save in backbone JS

I have a backbone view where I call model.save to create/updated date submitted in the form. Before calling the save I explicitly call model.isValid(true) to validate the form fields then I process the form data to make it ready for API expected format (by adding or modifying additional fields) and then make call to mode.save function which is again triggering validate function where the validations are getting failed due to the modified data. As I have already called the isValid function explicitly, I want to prevent the call again during save. How can I do it in backbone. Here is sample code.
var data = Backbone.Syphon.serialize($(e.currentTarget).closest('form.my_form')[0]));
this.model.set(data);
if(this.model.isValid(true)) {
data['metas'] = this.context.metaData;
data['metas'][0]['locale'] = this.parentObj.model.get('locale');
data['metas'][0]['name'] = data['name'];
delete data['name'];
}
var tempDynAttrs = [];
if(data['dynamicAttributes']){
$.each(data['dynamicAttributes'], function(index,obj) {
if(obj['attributeValue'] !== null && obj['attributeValue'] !== undefined ) {
tempDynAttrs.push({
attributeName: obj['attributeName'],
attributeValue: [obj['attributeValue']],
locale: data['defaultLocale'],
status: 'active'
});
}
});
}
data['dynamicAttributes'] = tempDynAttrs;
this.model.save(data, {
url: this.model.url(),
patch: true,
success : function(model, response) {
$('#headerMessage').html('Data is updated successfully');
},
error : function(model, response) {
$('#headerMessage').html('Error updating data');
}
});
} else {
$('#formPanel').animate({
scrollTop: $('.has-error').first().offset().top-50
}, 100);
return false;
}
Try passing {validate:false} in the save options, like
book.save({author: "Teddy"}, {validate:false});
According to change log of version 0.9.10:
Model validation is now only enforced by default in Model#save and no longer enforced by default upon construction or in Model#set, unless the {validate:true} option is passed.
So passing {validate:false} should do the trick.

submit form if don't have error

i am using ajax for send active form by this function
public function Link()
{
$id=$this->params['id'];
$url=$this->params['url'];
$dviId=$this->params['divId'];
$url=Yii::$app->urlManager->createAbsoluteUrl($url);
$js2="$('#".$id."').on('click', function() { $.ajax({url: '".$url."',type: 'POST',success : function(res){ $('#".$dviId."').html(res);}});});";
$view = $this->getView();
AjaxAsset::register($view);
if ($js2 !== '') {
$view->registerJs($js2);
}
return ;
}
And want to show error if any happened else send form
There is a plugin in jquery to do client side validation if you are using javascript and want to do initial validation of the form.
http://jqueryvalidation.org/
Also you can use "required" attribute in your text tags to do some intial checks. More can be found here:
http://www.w3schools.com/tags/att_input_required.asp
Hope this helps a bit.
You can also set enableAjaxValidation to true in your form.
There is an example in the docs about that (see the controller part).
public function Link()
{
$id=$this->params['id'];
$url=$this->params['url'];
$dviId=$this->params['divId'];
if(isset($this->params['confirm'])) {
$confirm = "if(confirm('".$this->params['confirm']."')){";
$endConfirm = "}";
}
else
{
$confirm = "";
$endConfirm = "";
}
$url=Yii::$app->urlManager->createAbsoluteUrl($url);
$js2="$('#".$id."').on('click', function() {".$confirm."$.ajax({url: '".$url."',type: 'POST',beforeSend: function(){ $('body').addClass('wait');},complete: function(){ $('body').removeClass('wait');},success : function(res){ $('#".$dviId."').html(res);}});".$endConfirm."});";
$view = $this->getView();
AjaxAsset::register($view);
if ($js2 !== '') {
$view->registerJs($js2);
}
return ;
}

Resources