I'm trying to display some AJAX loaded charts that change content when the user changes a filter without reloading the page. Followed Backpack and Laravel charts docs but I'm kinda stuck on how to re-render the chart without reloading the page. This is what I have so far:
routes:
Route::get('charts/test-chart', 'Charts\TestChartChartController#response')->name('charts.test-chart.index');
Route::get('charts/test-chart-ajax', 'Charts\TestChartChartController#data');
controller:
public function setup()
{
$this->chart = new Chart();
$labels = [];
for ($days_backwards = 30; $days_backwards >= 0; $days_backwards--) {
if ($days_backwards == 1) {
}
$labels[] = $days_backwards.' days ago';
}
$this->chart->labels($labels);
$this->chart->load(backpack_url('charts/test-chart-ajax'));
}
public function data(Request $request)
{
for ($days_backwards = 30; $days_backwards >= 0; $days_backwards--) {
$users[] = \App\Models\User::whereDate('created_at', today()
->subDays($days_backwards))
->count();
}
$this->chart->dataset('Users', 'line', $users)
->color('rgb(77, 189, 116)')
->backgroundColor('rgba(77, 189, 116, 0.4)');
return $this->chart->api();
}
view:
#extends(backpack_view('blank'))
#php
#extends(backpack_view('blank'))
#php
Widget::add([
'type' => 'event_select',
'view' => 'event_select',
]);
Widget::add(
[
'type' => 'div',
'class' => 'row',
'content' => [ // widgets
[ 'type'=> 'chart', 'controller' => \App\Http\Controllers\Admin\Charts\TestChartChartController::class ],
]
]);
#endphp
#section('content')
#endsection
js: (here is the part where I'm lost)
#push('after_scripts')
<script>
$(document).ready(function() {
$('.eventSelector').each(function(index, element) {
selected_event = this.getAttribute('selected_event');
this.value = selected_event;
});
$('.eventSelector').on('change', changeType);
});
function changeType() {
var eventId = $('.eventSelector').val();
$.ajax(
{
This is the part where I'm stuck, I don't know how to make the ajax call to reload the chart without reloading the page.
});
}
</script>
#endpush
It works alright the first time but I don't really know how to re-render the chart when the user changes the filter. Apparently there is something in the docs here : https://v6.charts.erik.cat/api_charts.html#update-the-chart-after-rendering but I have no idea how to use that api_url mentioned. At the end of the documentation there is a link to a sample, but now seems broken (404)
Any help would be greatly appreciated. Thanks!
***EDIT: I think I made some progress, but now I get an error I haven't been able to solve:
I added this to the script.blade.js file and it looks like its refreshing the table (because my endpoint is getting called twice):
var original_api_url = {{ $chart->id }}_api_url;
{{ $chart->id }}_refresh(original_api_url + "?year=22");
But I get this error on the console:
Uncaught (in promise) TypeError: Cannot set properties of undefined (setting 'datasets')
Related
I want to be redirected to single question page after creation of question
summitQuestion(){
this.question_button = true
axios.post('/api/question/ask', {
'title' : this.question.title,
'body' : this.question.body,
'tags' : this.tags,
'user_id' : this.$page.props.user.id
}).then(response=>{
this.question_button = false
console.log(response.data)
}).catch(error=>{
this.question_button = false
console.log(error.response.data.errors)
if(error.response.data.errors){
this.title_errors = error.response.data.errors.title
this.body_errors = error.response.data.errors.body
}
})
},
I have this function I want after the success of the request to redirect I a spa way without page reloading to question single page I am using inertia js and jetstream my laravel router is below
Route::middleware(['auth:sanctum', 'verified'])->get('/question/{question}', 'App\Http\Controllers\QuestionController#show')->name('question-single');
Simply use the visit method on the inertia like shown below.
this.$inertia.visit(route('question-single'), { method: 'get' });
If you got everything correct from your code above remaining the redirection without your page reloading, then I guess the modification of your code will be the sample as folows;
summitQuestion(){
this.question_button = true
axios.post('/api/question/ask', {
'title' : this.question.title,
'body' : this.question.body,
'tags' : this.tags,
'user_id' : this.$page.props.user.id
}).then(response=>{
this.question_button = false
// console.log(response.data)
this.$inertia.visit(route('question-single'), { method: 'get', data: response.data });
}).catch(error=>{
this.question_button = false
console.log(error.response.data.errors)
if(error.response.data.errors){
this.title_errors = error.response.data.errors.title
this.body_errors = error.response.data.errors.body
}
})
},
You can make reference to this by visiting The Official Inertiajs Website
If you are using Inertia, you are supposed to create a form with v-model linked to fields. Add to that a button of type submit that call your method (see below the method example).
<form #submit.prevent="submitQuestion">
<input type="text" v-model="form.title">
<button type="submit"></button>
</form>
<script>
export default {
data() {
return {
form: this.$inertia.form({
title: this.question.title,
body: this.question.body,
tags: this.tags,
user_id: this.$page.props.user.id,
}, {
bag: 'default',
}),
}
},
methods: {
summitQuestion: function() {
this.form.post('/api/question/ask');
},
}
};
</script>
The redirection can be done directly on your controller method.
class QuestionController extends Controller
{
public function create(Request $request) {
// Create your question
return redirect()->route('your.route');
}
}
When I click submit I get this message.
ReferenceError: input is not defined
How to get last id number in console.log when I create a new data and click on save or submit button?
Controller
public function store(Request $request)
{
if($request->ajax()) {
$school = new SchoolsList();
$school->user_id = auth()->user()->id;
$school->province_id = $request->province_id;
$school->city_id = $request->city_id;
$school->center_id = $request->center_id;
$school->save();
return response()->json(['data_school' => $request->all(), 'id' => $school->id]);
}
}
Ajax
<script type="text/javascript">
$("#schools").submit(function (event) {
event.preventDefault();
data_school = input.value;
province_id = $('#province_id').val();
city_id = $('#city_id').val();
center_id = $('#center_id').val();
$.post("{{ route('schools-list.store') }}", {province_id:province_id, city_id:city_id, center_id:center_id}, function (data_school) {
console.log(response.data_school.id), function (response) {
console.log(response.data_school.id);
}
});
});
</script>
In your JavaScript you have "data_school = input.value;"
input is not defined and also not needed.
just remove that.
Also the last line of code console.log(response.data_school.id) is wrong. You set your own data structure, which is response.id
I am using laravel 5.0
I am also using datatable jquery plugin to display grid.
Controller mehtod
public function index() {
$jobs = \App\Job::orderBy('created_at', 'DESC')->limit(1000)->get();
return View::make('jobs.index', ['jobs' => $jobs]);
}
The issue:
Right now I hard-coded the ->limit(1000) to 1000 jobs in datatable grid to display
it but i have more then 1000 records to display.
What I want?
I want to display 500 records with grid and then 500 records.
I am not sure if there is any call back data-table plugin function available?
I need a dynamic way to load next 500
NOTE:
I am not willing to us this solution of scrolling
https://datatables.net/extensions/scroller/examples/initialisation/server-side_processing.html
You can user ajax data source:
please visit : https://datatables.net/examples/ajax/objects.html
Example PHP Script:
// function will process the ajax request
public function getMembers(Request $request) {
$draw = $request->get('draw');
$start = $request->get('start');
$length = $request->get('length');
$search = (isset($filter['value']))? $filter['value'] : false;
$total_members = 1000; // get your total no of data;
$members = $this->methodToGetMembers($start, $length); //supply start and length of the table data
$data = array(
'draw' => $draw,
'recordsTotal' => $total_members,
'recordsFiltered' => $total_members,
'data' => $members,
);
echo json_encode($data);
}
Example JavaScript :
$('#all-member-table').DataTable( {
"processing": true,
"serverSide": true,
"ajax": {
url: base_url+"ajax/members"
},
"columns": [
{ data: '1' },
{ data: '2' },
{ data: '3' },
{ data: '4' },
{ data: '5' },
]
} );
Example HTML:
<table id="all-member-table">
<thead>
<tr>
<th>Column1</th>
<th>Column2</th>
<th>Column3</th>
<th>Column4</th>
<th>Column5</th>
</tr>
</thead>
</table>
I think above answer should be extended with search feature.
Update the answer;
$filter = $request->get('search');
$search = (isset($filter['value']))? $filter['value'] : false;
where('somecolumnonyourdb','like', '%'.$search.'%')
This works for me
You can use standard pagination:
$jobs = \App\Job::latest()->paginate(500);
Or create it manually.
Im using knppaginatorbundle to create pagination. I have created a jquery code to select data with ajax.
Everything is okay when I click on the page number , the content is loaded with the correct data.
But I have a problem , The pagination template is not changed after after ajax query:
previous and next links values must changed
current page must be disabled
and other changes that need to be done ...
How can I do this ?
public function listAction($page, Request $request)
{
$em = $this->getDoctrine()->getManager();
$paginator = $this->get('knp_paginator');
$qb = $em->getRepository('AppBundle:Travel')->getListTravels();
$pagination = $paginator->paginate(
$qb, $request->query->get('page', $page), 3
);
//ajax request
if ($request->isXmlHttpRequest()) {
$view = $this->renderView('#App/Frontend/Travel/list.html.twig', array(
'pagination' => $pagination
));
$response = new JsonResponse(array('ok' => $view));
return $response;
}
return $this->render('AppBundle:Frontend/Travel:travel-list-view.html.twig', array(
'pagination' => $pagination,
));
}
I have added an attr data-target to pagination template like this:
<a data-target="{{ page }}" href="{{ path(route, query|merge({(pageParameterName): page})) }}">{{ page }}</a>
View
//.....
<div id="mydiv">
// list.html.twig contains the loop
{% include "AppBundle:Frontend/Travel:list.html.twig" %}
</div>
<br>
{{ knp_pagination_render(pagination) }}
//....
<script>
$(document).ready(function () {
$("ul#pagination a").click(function (e) {
e.preventDefault();
var dataTarget = $(this).attr("data-target"); // each <a> has attr named data-target contains num of page
var hash;
hash = 'page=' + dataTarget;
window.location.hash = hash;
if (window.location.hash != "") {
$.ajax({
type: 'get',
dataType: 'json',
url: Routing.generate('frontend_travels_list', {'page': dataTarget}),
success: function (msg) {
if (msg["ok"] === undefined) {
alert('error');
} else {
$("#mydiv").html(msg["ok"]);
}
}
});
}
});
});
</script>
Route
frontend_travels_list:
path: /travels/{page}
defaults: { _controller: AppBundle:TravelFrontend:list, page: 1 }
options:
expose: true
If someone else needs a solution there 2 ways.
You can use that bundle https://github.com/nacholibre/knppaginator-ajax
You should build new pagination string in controller and send it in JsonResponse as a param. Then replace pagination element in DOM via jQuery on success.
For SF 4.3 you can use my approach
To be able to inject the Processor in controller you have to add alias for autowiring in services.yaml
Knp\Bundle\PaginatorBundle\Helper\Processor: '#knp_paginator.helper.processor'
Based on injected PaginatorInterface you should build your $pagination object (PaginationInterface)
Use Processor to build the context array for Twig.
$paginationContext = $processor->render($pagination);
render method expects SlidingPagination object, but got $pagination which is PaginationInterface - however it seems that is ok
Get the Twig and render a final string
$twig = $this->get('twig');
$paginationString = $twig->render($pagination->getTemplate(), $paginationContext);
Example of working controller
if ($request->isXmlHttpRequest()) {
$view = $this->render('#App/Frontend/Travel/list.html.twig', array(
'pagination' => $pagination
))->getContent();
$paginationContext = $processor->render($pagination);
$twig = $this->get('twig');
$paginationHtml = $twig->render($pagination->getTemplate(), $paginationContext);
$response = new JsonResponse(['view' => $view, 'paginationHtml' => $paginationHtml]);
return $response;
}
then in jQuery
success: function (msg) {
if (msg["ok"] === undefined) {
alert('error');
} else {
$("#mydiv").html(msg["view"]);
$("#myDivContainingPagination").html(msg["paginationHtml"])
}
}
I am adding some extra html to some input attributes in the category edit area by creating a new attribute input renderer type in my modules mysql install script:
$installer->addAttribute(
'catalog_category',
'mymodule_category_popularity',
array(
'label' => 'My Label',
'group' => 'My Group',
'type' => 'int',
'class' => 'validate-number',
'required' => false,
'default' => 50,
'input_renderer' => 'mymodule/adminhtml_catalog_category_widget_slider',
)
);
And the renderer:
class Mynamespace_Mymodule_Block_Adminhtml_Catalog_Category_Widget_Slider extends Varien_Data_Form_Element_Text
{
public function getAfterElementHtml()
{
$html = parent::getAfterElementHtml();
return $html." <div id=\"slider\"></div>";
}
}
And the javascript:
var $j = jQuery.noConflict();
$j(document).ready(function(){
$j( "#slider" ).slider();
});
This works fine, the plugins get loaded correctly and so does jQuery (as I have loaded it using my layout xml for the module). I see a slider. However, after the page has completely finished (the Magento ajax has re-loaded the category edit tabs) my slider dissapears. This, I assume, is because the javascript code is not getting ran again.
I have tried adding my javascript to the core edit.phtml (as there is other Magento JS there) however I just get an error: $j('#slider).slider() is not a function. This is because the content is getting loaded via ajax.
So, how do I make sure that I can use $j('#slider).slider() in the category edit area? Confusing I know.
I accomplished this by creating a javascript function:
function loadedInput() {
$('#slider').slider();
}
And calling that function in the attribute renderer:
class Mynamespace_Mymodule_Block_Adminhtml_Catalog_Category_Widget_Slider extends Varien_Data_Form_Element_Text
{
public function getAfterElementHtml()
{
$html = parent::getAfterElementHtml();
return $html." <div id=\"slider\"></div><script>loadedInput();</script>";
}
}