DOMPDF not downloading file when using AJAX - 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

Related

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();
}

adding git tag (version ) info and change history to website

I am using Laravel for site development purposes. What would be nice is to have a button on the main page that shows the current version of the project. When clicking on the button, one would see all of the changes that have taken place (in the form of git commits) to the system. Is there a way to do this? If so, how?
TIA
On click of the button initiate an ajax call
$("#button").on("click", function () {
url: window.location.origin + '/fetch-git-commits',
type: "get",
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
success: function (data) {
// data contains the git commit informations
$(".container").html(data);
}
});
In the Route.php file add an entry
Route::get('fetch-git-commits', 'YourController#getGitCommits');
In the controller
public function getGitCommits ()
{
exec('git log', $output);
$history = array();
foreach($output as $line) {
if(strpos($line, 'commit') === 0) {
if(!empty($commit)) {
array_push($history, $commit);
unset($commit);
}
$commit['hash'] = substr($line, strlen('commit'));
}
else if(strpos($line, 'Author') === 0) {
$commit['author'] = substr($line, strlen('Author:'));
}
else if(strpos($line, 'Date') === 0) {
$commit['date'] = substr($line, strlen('Date:'));
}
else {
if(isset($commit['message']))
$commit['message'] .= $line;
else
$commit['message'] = $line;
}
}
return $history; // Array of commits, parse it to json if you need
}
References:
Reading a git commit message from php
Parse git log with PHP to an array
There is a little package for that:
https://github.com/tremby/laravel-git-version

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 ;
}

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

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.

Backbone.js REST URL with ASP.NET MVC 3

I have been looking into Backbone.js lately and i am now trying to hook it up with my server-side asp.net mvc 3.
This is when i discovered a issue. ASP.NET listens to different Actions, Ex: POST /Users/Create and not just POST /users/. Because of that, the Model.Save() method in backbone.js will not work.
How should we tackle this problem? Do i have to rewrite the Backbone.Sync?
The answer is not to override Backbone.sync. You rarely would want to do this. Instead, you need only take advantage of the model's url property where you can assign a function which returns the url you want. For instance,
Forum = Backbone.Model.extend({
url: function() {
return this.isNew() ? '/Users/Create' : '/Users/' + this.get('id');
}
});
where the url used for a model varies based upon whether the model is new. If I read your question correctly, this is all you need to do.
You either have to tell ASP.NET MVC to route proper REST urls or fix Backbone.sync so it sends the GET/POST requests at the proper URLs.
Backbone works with REST not with RESTful URLs. There may be an OS implementation of Backbone.sync that matches your urls though.
Recommend URLs that play more nicely with Backbone:
GET /forums -> index
GET /forums/new -> new
POST /forums -> create
GET /forums/:forum -> show
GET /forums/:forum/edit -> edit
PUT /forums/:forum -> update
DELETE /forums/:forum -> destroy
I wrote a blog post recently describing how to bind .NET MVC to the default Backbone service layer.
Like previous posters have mentioned, there are a number of approaches you could take. I prefer this approach because it requires little configuration.
The controller:
public class ZocController : Controller
{
public ActionResult Docs()
{
return Json(GetDocs(), JsonRequestBehavior.AllowGet);
}
[ActionName("Docs")]
[HttpPost]
public ActionResult HandlePostDoc(Doctor doc)
{
doc.id = Guid.NewGuid();
CreateDoc(doc);
return Json(doc);
}
[ActionName("Docs")]
[HttpPut]
public ActionResult HandlePutDoc(Doctor doc)
{
UpdateDoc(doc);
return new EmptyResult();
}
[ActionName("Docs")]
[HttpDelete]
public ActionResult HandleDeleteDoc(Guid id)
{
DeleteDoc(id);
return new EmptyResult();
}
}
The Backbone
window.Doctor = Backbone.Model;
window.Doctors = Backbone.Collection.extend({
model: Doctor,
url: '/zoc/docs'
});
i found the following code in https://github.com/sgentile/BackboneContacts
/// <reference path="backbone.js" />
ModelBase = Backbone.Model.extend({
defaults: {
id: null
},
url: function (type) {
//expecting the following conventions on the server:
//urlRoot should be the controller : controller/
//create → POST /action
//read → GET /action[/id]
//update → PUT /action/id
//delete → DELETE /action/id
var fqUrl = this.urlRoot;
switch (type) {
case "POST":
fqUrl += "create";
break;
case "PUT":
fqUrl += "update";
break;
case "DELETE":
fqUrl += "delete/" + this.get('id');
break;
case "GET":
fqUrl += "read/" + this.get('id');
break;
}
return fqUrl;
}
});
var methodMap = {
'create': 'POST',
'update': 'PUT',
'delete': 'DELETE',
'read': 'GET'
};
// Helper function to get a URL from a Model or Collection as a property
// or as a function.
var getUrl = function (object) {
if (!(object && object.url)) return null;
return _.isFunction(object.url) ? object.url() : object.url;
};
// Throw an error when a URL is needed, and none is supplied.
var urlError = function () {
throw new Error('A "url" property or function must be specified');
};
Backbone.sync = function (method, model, options) {
var type = methodMap[method];
options.url = _.isString(this.url) ? this.url : this.url(type);
// Default JSON-request options.
var params = _.extend({
type: type,
dataType: 'json'
}, options);
// Ensure that we have a URL.
if (!params.url) {
params.url = getUrl(model) || urlError();
}
// Ensure that we have the appropriate request data.
if (!params.data && model && (method == 'create' || method == 'update')) {
params.contentType = 'application/json';
params.data = JSON.stringify(model.toJSON());
}
// For older servers, emulate JSON by encoding the request into an HTML-form.
if (Backbone.emulateJSON) {
params.contentType = 'application/x-www-form-urlencoded';
params.data = params.data ? { model: params.data} : {};
}
// For older servers, emulate HTTP by mimicking the HTTP method with `_method`
// And an `X-HTTP-Method-Override` header.
if (Backbone.emulateHTTP) {
if (type === 'PUT' || type === 'DELETE') {
if (Backbone.emulateJSON) params.data._method = type;
params.type = 'POST';
params.beforeSend = function (xhr) {
xhr.setRequestHeader('X-HTTP-Method-Override', type);
};
}
}
// Don't process data on a non-GET request.
if (params.type !== 'GET' && !Backbone.emulateJSON) {
params.processData = false;
}
// Make the request.
return $.ajax(params);
};

Resources