I have a view with one argument, and a set of exposed filters. When the user filters the view, the form is submitted using Ajax, and the filters are appended to the url using location.hash.
My goal is to filter the view upon initial page load, if the filters are present in the location.hash.
Currently, I'm loading the view through an Ajax callback, which works perfectly fine. But the big problem is that Ajax for the view doesn't work.
This is the callback that loads the View.
// Load the view object.
$view = views_get_view('taxonomy_term');
$view->set_display('page');
$view->set_use_ajax(TRUE);
// Pass the current tid as the argument.
$view->set_arguments(array($tid));
// Set the current page.
$view->set_current_page($page);
// Set the exposed filters.
$view->get_exposed_input();
// Execute.
return $view->execute_display();
When I navigate directly to that callback, everything works. But not when I load it through Ajax.
Any ideas?
Update:
It seems that Drupal.behaviors.ViewsAjaxView() doesn't execute for some reason. If I execute it manually, everything works.
Ok, so I've found the answer.
Instead of loading the View from my own callback, I'm now loading the View from the regular ajax callback.
On my page, I create the view object, and add the configuration to Drupal.settings.
$view = views_get_view('taxonomy_term');
$view->set_display('page');
$view->set_use_ajax(TRUE);
$view->set_arguments(array($tid));
$settings = array(
'views' => array(
'ajax_path' => url('views/ajax'),
'ajaxViews' => array(
array(
'view_name' => $view->name,
'view_display_id' => $view->current_display,
'view_args' => check_plain(implode('/', $view->args)),
'view_path' => check_plain($_GET['q']),
'view_base_path' => $view->get_path(),
'view_dom_id' => 1,
'pager_element' => $view->pager['element'],
),
),
),
);
drupal_add_js($settings, 'setting');
views_add_js('ajax_view');
Then I load my js, which adds the current filter from the location.hash to the settings. And finally, loads the View.
var data = {};
// Add view settings to the data.
for (var key in Drupal.settings.views.ajaxViews[0]) {
data[key] = Drupal.settings.views.ajaxViews[0][key];
}
// Get the params from the hash.
if (location.hash) {
var q = decodeURIComponent(location.hash.substr(1));
var o = {'f':function(v){return unescape(v).replace(/\+/g,' ');}};
$.each(q.match(/^\??(.*)$/)[1].split('&'), function(i,p) {
p = p.split('=');
p[1] = o.f(p[1]);
data[p[0]] = data[p[0]]?((data[p[0]] instanceof Array)?(data[p[0]].push(p[1]),data[p[0]]):[data[p[0]],p[1]]):p[1];
});
}
$.ajax({
url: Drupal.settings.views.ajax_path,
type: 'GET',
data: data,
success: function(response) {
var viewDiv = '.view-dom-id-' + data.view_dom_id;
$('#content > div.limiter').html(response.display);
// Call all callbacks.
if (response.__callbacks) {
$.each(response.__callbacks, function(i, callback) {
eval(callback)(viewDiv, response);
});
}
},
error: function(xhr) {
$('#content > div.limiter').html('<p id="artist-load-error">Error text.</p>');
$('#block-request-0').hide();
},
dataType: 'json'
});
This way, the view loads through the regular flow, and everything works as expected =)
Related
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.
I have got this scheduler displayed but not binding to tasks. The scheduler in the view. I am using java script method to read/create call to web api
#(Html.Kendo().Scheduler<TaskViewModel> ()
.Name("AppointmentSearchScheduler")
.DataSource(dataSource => dataSource
.Custom()
.Schema(schema => schema
.Model(m => {
m.Id(f => f.TaskID);
m.Field(f => f.OwnerID).DefaultValue(1);
}))
.Transport(new {
read = new Kendo.Mvc.ClientHandlerDescriptor() {
HandlerName = "customRead"
},
create = new Kendo.Mvc.ClientHandlerDescriptor() {
HandlerName = "customCreate"
}
})))
Below is javascript handler method I am not including create handler for brevity.
function customRead(options){
//get the selected Row of the kendo grid
var selectedRow = $("#locationgridKendo").find("tbody tr.k-state-selected");
var scheduler = $("#AppointmentSearchScheduler").data("kendoScheduler")
//get SelectedRow data
var rowData = $('#locationgridKendo').data("kendoGrid").dataItem(selectedRow);
if (rowData !== null) {
//Convert data to JSON
var rowDataJson = rowData.toJSON();
//extract the location ID
var locationId = rowDataJson.LocationID;
var CalenderweekStartDate = new Date().toISOString();
baseUrl = $('base').attr('href');
$.ajax({
url: baseUrl + 'Schedular/api/GetAppPerLocation?locationId=' + locationId + '&date=' + CalenderweekStartDate,
type: 'GET',
cache: false,
contentType: 'application/json',
success: function (result) {
//This method is hitting and i can see the data being returned
console.log('data is received : ' + result.Data);
options.success(result.Data);
},
error: function (xhr, status, error) {
//alert("Error: Search - Index.js - submitAppointment()");
var err = eval("(" + xhr.responseText + ")");
alert(err.Message);
}
});
}
}
Here is the web API controller called by making ajax call . The controller works perfectly when i used the basic read/create syntax . The ajax call complete and it does hit back the success method and returns the data but scheduler for some reason is not binded to incoming data. Here is my controller code
[HttpGet]
[Route("api/GetAppPerLocation")]
public DataSourceResult GetAppointmentPerLocation([ModelBinder(typeof(Usps.Scheduling.Web.ModelBinders.DataSourceRequestModelBinder))] DataSourceRequest request, int locationId, DateTime date) {
List < TaskViewModel > locationAvailableAppointmentList = new List < TaskViewModel > ();
locationAvailableAppointmentList = data.Select(appt => new TaskViewModel() {
TaskID = appt.ServiceAppointmentId,
Title = "Appointment Available",
Start = DateTime.SpecifyKind(appt.AppointmentBegin, DateTimeKind.Local),
End = DateTime.SpecifyKind(appt.AppointmentEnd, DateTimeKind.Local),
Description = "",
IsAllDay = false
}).ToList();
return locationAvailableAppointmentList.ToDataSourceResult(request);
}
For some reason the scheduler is not binding to incoming data . the incoming data works perfectly when i use a basic binding approach but not using transport . My goal for using this approach is once i am done with read(scheduler is not binding now) , on create I need to grab the ID of the newly created task returned by my controller and then pass that id to another mvc controller to render a confirmation page. Any other approach to accomplish this goal will be highly recommended.
Please excuse me for any mistake since this is my first question on stackoverflow.
My goal for using this approach is once i am done with read(scheduler is not binding now) , on create I need to grab the ID of the newly created task returned by my controller and then pass that id to another mvc controller to navigate render a confirmation page.
I speculated that read was not returning correct result so i had to fix that .Also my basic goal was redirection to another page after with appointment id and displaying a confirmation screen. This is how accomplished it . I understand this is not the best approach but it has been more than a year no body answered by question. Here is the approach i took .
I added a error to the model state like this in my controller
if (!String.IsNullOrEmpty(task.TaskID.ToString()))//redirect to confirmation page if the appointment was added to the queue
ModelState.AddModelError("AppointmentID", confirmationNumber);
then on client side i configure the error event on grid like this
.Events(
events => events.Error("RedirectToConfirmationPage"))
Here is the Javascript method details
function RedirectToConfirmationPage(e) {
console.log('RedirecToConfirmationPage method......');
console.log(e);
if (e.errors) {
var appointmentID = "";
// Create a message containing all errors.
$.each(e.errors, function (key, value) {
console.log(key);
if ('errors' in value) {
$.each(value.errors, function () {
appointmentID += this + "\n";
});
}
});
console.log('Newly Generated AppointmentID = ' + appointmentID);
// Redirect URL needs to change if we're running on AWS instead of on local developer machines
if (window.location.href.indexOf('/TestProject.Scheduling') > 1) {
window.location.href = '/Scheduler/AppointmentConfirmation?confirmationNumber=' + appointmentID
}
else {
window.location.href = '/Scheduler/AppointmentConfirmation?confirmationNumber=' + appointmentID
}
}
}
Hope it is helpfull to someone down the road.
I have followed Matt Van Andel's Custom List Table Example to create an admin table which displays enquiries to a website via an external MySql database.
Having implemented the displaying of my data correctly, I have added a select box column which will allow the admin to update the 'status' of the enquiry (Awaiting Response, Responded etc.) and I need this to update my database via AJAX.
I need a change in these select boxes to trigger an AJAX call which will update the database with the new value but I seem to be struggling to link my external AJAX file to my plugins .php file correctly.
I have reached a point where (in the Network tab) I can see I am loading the .js file like so:
Code in list-table.php:
function ajax_test_enqueue_scripts() {
wp_enqueue_script( 'list-table', plugins_url( 'js/list-table.js', __FILE__ ), array('jquery'));
}
add_action( 'admin_enqueue_scripts', 'ajax_test_enqueue_scripts' );
And my AJAX:
jQuery('.status-select').on( 'change', function ajaxSubmit() {
alert("IT WORKED!");
$.ajax({
url: ajaxurl,
type: "POST",
cache: false,
data: this.val()
})
});
At the moment the file is showing but the 'on change' part doesn't seem to be firing (hence the 'alert' in the .js).
Apologies if this question is worded or organised poorly, it is my first time posting!
Hope someone can explain what/ where I am going wrong.
This is quite a specific requirement but for anyone else using custom tables in WordPress and wanting to update an external myqsl database via AJAX - here's how I did it.
The AJAX side of things -
<script>
jQuery('select.status').on('change', function() {
var $statusSelect = jQuery( this );
var $statusSelectCell = $statusSelect.parent();
var enquiryStatusValue = $statusSelect.val();
var currentBackgroundColor = $statusSelectCell.parent().css("backgroundColor");
var ajaxData = {
'action': 'update_status_db',
'currentId': $statusSelect.attr('id'),
'data': enquiryStatusValue
}
jQuery.ajax({
type: "POST",
url: "/wp-admin/admin-ajax.php",
data: ajaxData,
success: function( response ) {
console.log("Data returned: " + response );
$statusSelectCell.parent().css({"background-color": "#b3e6b3"});
$statusSelectCell.parent().animate({backgroundColor: currentBackgroundColor}, 1200);
},
error: function() {
alert("FAILED TO POST DATA!!");
}
});
})
</script>
Note, the users success confirmation in this case is for the specific row to flash green. This is optional.
Next, the PHP to process the AJAX request. This is to be written outside the tables class.
wp_enqueue_script('jquery');
add_action( 'wp_ajax_update_status_db', 'update_status_db_callback' );
function update_status_db_callback(){
global $wpdb;
$newStatus = $_POST['data'];
$currentId = $_POST['currentId'];
$table = 'wp_enquiryinfo';
$result = $wpdb->update( $table, array( 'status' => $newStatus ), array( 'id' => $currentId ));
echo $_POST['data'];
if (!$result) {
echo "FAILED TO UPDATE";
} else {
$result;
echo "WILL UPDATE SUCCESSFULLY - CALL RESULT FUNCTION";
};
wp_die();
}
Here are a couple of the things I was getting wrong originally:
Firstly, the callback function HAS to end with _callback. Secondly, I didn't call the wp_die function at the end of this - this again is required.
Hopefully this may be of use to someone in the future.
I don't want to refresh a page when I am searching through a database eg. on post, so I had help in using a $.post call which works for sending information. There is a .done(function( data ){ line which I haven't used yet.
I also came across this question which I'm not sure if this ties to my question.
Return $.get data in a function using jQuery
I'm trying to search through a database, string match, and return the rows with matching strings. But I want to do this without refreshing the page so I would think that I am using the $.post call and using the .done(function( data ){ which is triggered by javascript (a button).
So I have two parts, the page I'm on and a separate PHP page that processes the call when made.
How do I make the bridge where I can return the data back? Or is there an easier way to do this?
The method .done(function(){}) is exactly what You would like to use, but You can also take a look at third argument (callback) of $.post function.
On server side, do all the queries and prepare the stuff in jsoned array like:
// set up data to send
$contentArray = [
'content' => 'Some content',
'foo' => 'bar',
];
$jsonResults = json_encode($contentArray);
// you can always send header('Content-Type: application/json'); instead of using simple die function.
die($jsonResults);
Then on client side:
<div class="content-container"></div>
<script type="text/javascript">
function someFunc() {
(...)
$.post(addr, values, function(res) {
var response = $.parseJSON(res);
$('.content-container').html(response.content);
});
}
</script>
This should update the content of the .content-container class only. You can send as much as you want, even prepared view to be displayed in the container. This is up to You.
EDIT:
Just to be sure, you're calling someFunc() on some button click event, right? If not, do it as follows:
<div class="content-container"></div>
Click here
<script type="text/javascript">
function changePageContent(addr, contentId) {
$.post(addr, {contentId:contentId}, function(res) {
var response = $.parseJSON(res);
$('.content-container').html(response.content);
});
}
$('.callMe').on('click', function() {
changePageContent($(this).attr('href'), $(this).attr('data-content-id'));
return false;
});
</script>
someScript.php:
<?php
// you should force your script to allow only XML HTTP request here
if(empty($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest') {
die('AJAX requests only..');
}
// always remember to escape somehow your post values before you use them
$contentId = is_numeric($_POST['contentId']) ? intval($_POST['contentId']) : null;
if (null == $contentId) (...) // throw exception or return status=false
// do your mysql query like: "SELECT * FROM content WHERE id=".$contentId;
// well, it would be better to send headers instead of that
die(json_encode([
'status' => true, // this is a good practice to send some info, if everything is fine, if mysql row has been found etc..
'result' => $result, // mysql row, this is just in case you need other values to display
'content' => $result['content'], // I assume you have 'content' column in your mysql
]));
?>
Take a look at the docs for Ajax, there really is a lot of info there which will help.
In short, you could do something like this:
function myPost() {
// Set the data
var data = {
'key' : 'value',
'key_2' : 'value_2'
};
// Do the post
$.post( '/your-url/', data, callBack );
}
function callBack( data ) {
// If the $.post was successful
success: function( data ) {
// do stuff
console.log( data ); // returned from your endpoint
},
// If there was an error
error: function( jqXHR, textStatus ) {
// do stuff
console.log( "Request failed: " + textStatus );
}
}
// On click of your element, fire the post request
$('#element').on('click', function() {
myPost();
});
I'm wondering what is the best method to handle AJAX calls with jQuery? Right now I'm doing something like following:
$("#test").live('click', function(){
// Process form
$.ajax({
type: "post",
url: "test.php",
success: function(html){
if(html.success == 0) {
alert('Error');
} else {
var obj = $.parseJSON(html.rows);
$("#success").html(obj[0].name);
}
},
dataType:'json'
});
return false;
});
In test.php file, I'm checking if request is an AJAX request. If it's an AJAX request I'm running a database query to get some data (this part isn't important in this question, I think):
// query goes here
if(mysql_num_rows($query) > 0 ) {
$result['success'] = 1;
$result['data'] = json_encode($data);
} else {
$result['success'] = 0;
}
Now I'm wondering if my method is the best possible? FYI I'm using KohanaPHP framework currently, so I want to not break MVC "rules". If I'm doing it wrong, do you have any tips and suggestions how to handle AJAX calls in controllers?
Regards,
Tom
What you have looks good here, though I don't think you need a $.parseJSON() there, it should already be an object at that point, this should work:
$("#success").html(html.rows[0].name);
As a side note, from a readability/maintainability perspective, I'd rename your html argument to be data, like this:
success: function(data) {
This is purely preference, but using html when it's an HTML type response, and data or something else when it's JSON/already an object you're expecting keeps things a bit easier to read for outsiders.
#tom - you need to encode the PHP array like this:
$data = array(
'status' => 'success'
);
echo json_encode($data);
But you might want to change the array structure a little. Since the xhr object has a text status I usually encode an JSON array like this:
$response = array(
'status' => 'success' // or flash or error
,'flash' => array(
'header' => 'whatever is wrong with submission of data html list format'
'fields' => array('field names to be highlighted')
)
,'html' => 'html presentation'
,'data => array('json data')
);
echo json_encode($response);
Now you can do some nice things like this:
,success: function(response) {
if (response.status === 'success') {
$('.basic_container').hide();
that.removeDataTable();
that.getContent(contentUrl);
$('.basic_container').show();
}
if (response.status === 'flash') {
$('#flash').html(response.flash.header);
$('#flash').show();
that.highlightWithFlash(response.flash.fields);
}
}