Laravel Datatable column searching on relationship - datatables

I am using laravel datatable package, all things work fine. I have a problem with relational column searching. Let me elaborate more:
I have two tables:
ed_class (primary table)
ed_section(secondary table)
Here the result of above tables with relationship between them.
Main data is coming from ed_section table and by making relationship i am getting classes names from ed_class table. As the data is coming from ed_section table so datatable column searching works fine on section name column but it's not working on class name column, so how to implement searching on class name column too.?
Here's datatable js code:
$(function() {
$('#table').DataTable({
"pageLength": 25,
"ordering": false,
//"columnDefs": [{
//"targets": 7,
//"orderable": false
//}],
processing: true,
serverSide: true,
ajax: '{!! url('sections/data') !!}',
columns: [
{ data: 'sr_no', name: 'sr_no' },
{ data: 'classid', name: 'classid' },
{ data: 'sectiontitle', name: 'sectiontitle' },
{ data: 'Option', name: 'Option' }
]
});
});
And here's server side code (using laravel datatable package):
public function data(){
$model = Section::all();
$data = Datatables::of($model);
$data = $data->addColumn('sr_no', function(Section $section){
})->editColumn('classid', function(Section $section){
return $section->studentClass->classtitle;
})->addColumn('Option', function(Section $section) {
return '<div class="btn-group">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Action <span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-left">
<li><a class="edit_section" data-id='.$section->sectionid.'> <i class="fa fa-edit" data-toggle="tooltip" title="" data-original-title="Edit"></i> Edit</a></li>
<li role="separator" class="divider"></li>
<li> <i class="fa fa-trash-o" data-toggle="tooltip" title="" data-original-title="Delete"></i> Delete</li>
</ul>
</div>';
})->rawColumns(['Option']);
$sections = $data->make(true);
return $sections;
}
And this is model relationship:
class Section extends Model {
public function studentClass(){
return $this->belongsTo('App\Classes','classid','classId');
}
}

Try Like this,
$orders = Order::with('user');
return Datatables::of($orders)
->addColumn('user', function ($order) {
return $order->user->->name;
})
In view :
{ "data": "user" , name : "user.name"},

I have find solution and want to share so that it can help others too: Here's server side code in controller (using laravel datatable package):
public function data(){
$model = Section::with('studentClass');
$data = Datatables::of($model);
$data = $data->addIndexColumn()
->editColumn('classid', function(Section $section){
return $section->studentClass->classtitle;
})->addColumn('Option', function(Section $section) {
return '<div class="btn-group">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Action <span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-left">
<li><a class="edit_section" data-id='.$section->sectionid.'> <i class="fa fa-edit" data-toggle="tooltip" title="" data-original-title="Edit"></i> Edit</a></li>
<li role="separator" class="divider"></li>
<li> <i class="fa fa-trash-o" data-toggle="tooltip" title="" data-original-title="Delete"></i> Delete</li>
</ul>
</div>';
})->rawColumns(['Option']);
$sections = $data->make(true);
return $sections;
}
Here's datatable js code:
$(function() {
$('#table').DataTable({
"pageLength": 25,
"ordering": false,
"columnDefs": [
{ "searchable": false, "targets": [0,3] }
],
processing: true,
serverSide: true,
ajax: '{!! url('sections/data') !!}',
columns: [
{ data: 'DT_Row_Index', name: 'DT_Row_Index' },
{ data: 'classid', name: 'studentClass.classtitle' },
{ data: 'sectiontitle', name: 'sectiontitle' },
{ data: 'Option', name: 'Option' }
],
"oLanguage": {
"sSearch": "Search Class: "
}
});
});

Related

DataTable serverSide export

I am using DataTable with serverSide processing, there is a menu to export the data but it will only export the data that was loaded. Is there a setting in the buttons section to get all the data without using the page limiting the export? So if the page load page 1 with 50 records and there are three other pages with 50 records the export will all 150 rows of data? I did try this https://datatables.net/forums/discussion/59216/customizing-the-data-from-export-buttons#latest but its not working. The GetDataToExport is never called there are not errors. I have included the html because I am not sure if its getting bypass.
buttons: [
{
extend: 'print',
title: $(actions).closest("div").find(".caption").text(),
exportOptions: {
orthogonal: 'export',
columns: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
},
{
extend: 'pdf',
orientation: 'landscape',
title: $(actions).closest("div").find(".caption").text(),
exportOptions: {
orthogonal: 'export',
columns: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
},
{
extend: 'csv',
title: $(actions).closest("div").find(".caption").text(),
exportOptions: {
orthogonal: 'export',
columns: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
customizeData: function (d) {
var exportBody = GetDataToExport();
console.log('exportBody');
d.body.length = 0;
d.body.push.apply(d.body, exportBody);
}
}
}
]
function GetDataToExport() {
console.log('Not Getting Called');
var jsonResult = $.ajax({
url: 'GetPermissions.ashx?page=all',
data: { search: $("#search").val() },
success: function (result) { },
async: false
});
var exportBody = jsonResult.responseJSON.data;
return exportBody.map(function (el) {
return Object.keys(el).map(function (key) { return el[key] });
});
}
HTML
<div class="btn-group">
<a class="btn btn-default btn-sm" href="javascript:;" data-toggle="dropdown">
<i class="fa fa-share"></i>
<span class="hidden-xs">Export </span>
<i class="fa fa-angle-down"></i>
</a>
<ul class="dropdown-menu pull-right permissions_tools" id="permissions_tools">
<li>
<a href="javascript:;" data-action="0" class="tool-action">
<i class="icon-printer"></i>Print
</a>
</li>
<li>
<a href="javascript:;" data-action="1" class="tool-action">
<i class="icon-doc"></i>PDF
</a>
</li>
<li>
<a href="javascript:;" data-action="2" class="tool-action">
<i class="icon-cloud-upload"></i>CSV
</a>
</li>
</ul>
</div>
updated
<li>
<a id="exportAllData" data-action="3" class="tool-action">
<i class="icon-cloud-upload"></i>ALL CSV
</a>
</li>

error in loading the vue script for a drop-down list

This is my first project in vue and it is being very difficult for me to find functional examples in vue.
I'm trying to make a header component that shows a list of links, some of which I need to be a drop-down list of links. Here is my component
<template>
<nav class='header'>
<ul class='menu'>
<li class='menu-item'>
<a class='menu-link' href="https://www.esthima.fr/societe">QUI SOMMES-NOUS ?</a>
</li>
<li class="menu-item menu-item-dropdown" v-on:click="toggle('ranking')" v-bind:class="{'open' : dropDowns.ranking.open}">
<a class='menu-link menu-link-toggle'>QUAND L’ANIMAL S’EN VA</a>
<ul class='dropdown-menu'>
<li class='dropdown-menu-item'>
<a class='dropdown-menu-link'>TEST 1</a>
</li>
<li class='dropdown-menu-item'>
<a class='dropdown-menu-link'>TEST 2</a>
</li>
</ul>
</li>
<li class='menu-item'>
<a class='menu-link' href="https://boutique.esthima.fr">URNES ANIMAUX</a>
</li>
</ul>
<img src="../assets/logoesthima.png">
<a>NOS SERVICES</a>
URNES ANIMAUX
<a>TARIFS ET DEVIS</a>
<a>NOUS CONTACTER</a>
</nav>
</template>
<script>
export default{
let header = new Vue({
al: '.header',
ready: function()
{
var self = this
window.addEventListener('click', function(e){
if (! e.target.parentNode.classList.contains('menu__link--toggle'))
{
self.close()
}
}, false)
},
data: {
dropDowns: {
ranking: { open: false}
}
},
methods: {
toggle: function(dropdownName)
{
//alert(dropdownName)
this.dropDowns[dropdownName].open = !this.dropDowns[dropdownName].open;
},
close: function()
{
for (dd in this.dropDowns)
{
this.dropDowns[dd].open = false;
}
}
}
})
}
</script>
I'm following the few examples I've found and the documentation and they all return the same console loading error with the declaration inside the "new Vue" script whether I declare it directly or keep it as a constant.
Someone who can make me see my mistake.
Thank you very much in advance!
You should change the code to be a properly structured Single-File Component:
<template>
<nav class='header'>
<ul class='menu'>
<li class='menu-item'>
<a class='menu-link' href="https://www.esthima.fr/societe">
QUI SOMMES-NOUS ?
</a>
</li>
<li class="menu-item menu-item-dropdown" #click="toggle('ranking')" :class="{open: dropDowns.ranking.open}">
<a class='menu-link menu-link-toggle'>
QUAND L’ANIMAL S’EN VA
</a>
<ul class='dropdown-menu'>
<li class='dropdown-menu-item'>
<a class='dropdown-menu-link'>TEST 1</a>
</li>
<li class='dropdown-menu-item'>
<a class='dropdown-menu-link'>TEST 2</a>
</li>
</ul>
</li>
<li class='menu-item'>
<a class='menu-link' href="https://boutique.esthima.fr">URNES ANIMAUX</a>
</li>
</ul>
<img src="../assets/logoesthima.png">
<a>NOS SERVICES</a>
URNES ANIMAUX
<a>TARIFS ET DEVIS</a>
<a>NOUS CONTACTER</a>
</nav>
</template>
<script>
export default
{
mounted()
{
window.addEventListener('click', (evt) =>
{
if (! e.target.parentNode.classList.contains('menu__link--toggle'))
{
this.close()
}
}, false)
},
data()
{
return {
dropDowns:
{
ranking:
{
open: false
}
}
};
},
methods:
{
toggle(dropdownName)
{
//alert(dropdownName)
this.dropDowns[dropdownName].open = !this.dropDowns[dropdownName].open;
},
close()
{
for (dd in this.dropDowns)
{
this.dropDowns[dd].open = false;
}
}
}
};
</script>

Change component state form another component rendered in a v-for

I have a list of components rendered in a a v-for. I want to set the "show" Boolean property as false in the other components when one of them is set to true:
To simplify I am only adding two components
Main component code:
<template>
<aside class="main-sidebar">
<section class="sidebar">
<ul class="sidebar-menu" data-widget="tree">
<nav-bar-user-profile-item></nav-bar-user-profile-item>
<nav-bar-item></nav-bar-item>
<nav-bar-item></nav-bar-item>
</ul>
</section>
</aside>
</template>
<script>
import NavBarUserProfileItem from '#/components/NavBar/NavBarUserProfileItem';
import NavBarItem from '#/components/NavBar/NavBarItem';
export default {
name: 'NavBar',
components: {
NavBarUserProfileItem,
NavBarItem
},
methods: {
MenuHasBeenToggled(event) {
console.log(event);
}
}
}
NavBarItemComponent
<template>
<li class="treeview2 item" :class="{'menu-open': isOpen, 'active': menu.active}" #click="ToggleState">
<a href="#">
<i class="fa fa-th"></i>
<span>{{ menu.title }}</span>
<span class="pull-right-container">
<i class="fa fa-angle-right pull-right"></i>
</span>
</a>
<collapse-transition>
<ul class="treeview-menu" v-show="isOpen">
<li v-for="submenu in menu.submenus" :key="submenu.title" :class="{'active': (('active' in submenu) ? submenu.active : false)}">
<b-link :href="submenu.link">
<i class="fa fa-circle-thin"></i>
{{ submenu.title }}
</b-link>
</li>
</ul>
</collapse-transition>
</li>
</template>
<script>
export default {
name: 'NavBarItem',
data: function () {
return {
isOpen: false
}
},
computed: {
},
methods: {
ToggleState() {
this.isOpen = !this.isOpen;
this.$emit("toggle-state");
}
},
props: {
menu: {
type: Object,
default: function() {
return {
link: "#",
title: "Main menu",
active: true,
submenus: [
{
link: "#",
title: "Submenu 1",
},
{
link: "#",
title: "Submenu 2",
active: true
},
{
link: "#",
title: "Submenu 3",
},
]
}
}
}
}
}
</script>
<style scoped>
</style>
The goal is to click on one of the and show the menu contents while at the same time collapse the other components.
I thought about using an array of variables and bind it to the "show" prop and with an event listen to it and set every variable to false except the one form the component that sent the event.
How can I know which component sent the event?
Any better idea on how to accomplish this task?
I think, the best way to do it is to add a uniuque identifier property to each NavBarItem and a property for a selected NavBarItem. Then in the main component you can on click on NavBarItem set selected NavBarItem and in NavBarItem make the isOpen computed on the basis if current NavBarItem identifier equals the clicked NavBarItem. Something like this:
<template>
<aside class="main-sidebar">
<section class="sidebar">
<ul class="sidebar-menu" data-widget="tree">
<nav-bar-user-profile-item></nav-bar-user-profile-item>
<nav-bar-item item-id="1" :selected-item-id="selectedNavbarItemId" #click="selectedNavBarItemId = 1"></nav-bar-item>
<nav-bar-item item-id="2" :selected-item-id="selectedNavbarItemId" #click="selectedNavBarItemId = 2"></nav-bar-item>
</ul>
</section>
</aside>
</template>
<script>
import NavBarUserProfileItem from '#/components/NavBar/NavBarUserProfileItem';
import NavBarItem from '#/components/NavBar/NavBarItem';
export default {
name: 'NavBar',
components: {
NavBarUserProfileItem,
NavBarItem
},
data: function(){
return {
selectedNavBarItemId: 0
}
},
methods: {
MenuHasBeenToggled(event) {
console.log(event);
}
}
}
And in NavBarItem
<template>
<li class="treeview2 item" :class="{'menu-open': isOpen, 'active': menu.active}" #click="ToggleState">
<a href="#">
<i class="fa fa-th"></i>
<span>{{ menu.title }}</span>
<span class="pull-right-container">
<i class="fa fa-angle-right pull-right"></i>
</span>
</a>
<collapse-transition>
<ul class="treeview-menu" v-show="isOpen">
<li v-for="submenu in menu.submenus" :key="submenu.title" :class="{'active': (('active' in submenu) ? submenu.active : false)}">
<b-link :href="submenu.link">
<i class="fa fa-circle-thin"></i>
{{ submenu.title }}
</b-link>
</li>
</ul>
</collapse-transition>
</li>
</template>
<script>
export default {
name: 'NavBarItem',
data: function () {
return {
}
},
computed: {
isOpen:function(){
return itemId == selectedItemId;
}
},
methods: {
},
props: {
itemId:Number,
selectedItemId:Number,
menu: {
type: Object,
default: function() {
return {
link: "#",
title: "Main menu",
active: true,
submenus: [
{
link: "#",
title: "Submenu 1",
},
{
link: "#",
title: "Submenu 2",
active: true
},
{
link: "#",
title: "Submenu 3",
},
]
}
}
}
}
}
</script>
<style scoped>
</style>

why doesn't my interpolated html display properly in vue.js?

The data in my app.js file is not being interpolated and displayed on the screen. I tried the older v-repeat. Could it be some missing items in the installation? Any help would be appreciated.
Here is my html
<!-- show the events -->
<div class="col-sm-6">
<div class="list-group">
<a href="#" class="list-group-item" v-for="event in events">
<!-- <h1>{{text}}</h1> -->
<h4 class="list-group-item-heading">
<i class="glyphicon glyphicon-bullhorn"></i>
{{ event.name }}
</h4>
<h5>
<i class="glyphicon glyphicon-calendar" v-if="event.date"></i>
{{ event.date }}
</h5>
<p class="list-group-item-text" v-if="event.description">{{ event.description }}</p>
<button class="btn btn-xs btn-danger" v-on="click: deleteEvent($index)">Delete</button>
</a>
</div>
</div>
And here is my js file
new Vue({
data: {
text: 'hello world',
event: { name: '', description: '', date: '' },
events: []
},
// Anything within the ready function will run when the application loads
ready: function() {
// When the application loads, we want to call the method that initializes
// some data
this.fetchEvents();
},
// Methods we want to use in our application are registered here
methods: {
// We dedicate a method to retrieving and setting some data
fetchEvents: function() {
var events = [
{
id: 1,
name: 'TIFF',
description: 'Toronto International Film Festival',
date: '2015-09-10'
},
{
id: 2,
name: 'The Martian Premiere',
description: 'The Martian comes to theatres.',
date: '2015-10-02'
},
{
id: 3,
name: 'SXSW',
description: 'Music, film and interactive festival in Austin, TX.',
date: '2016-03-11'
}
];
// $set is a convenience method provided by Vue that is similar to pushing
// data onto an array
this.$set('events', events);
},
// Adds an event to the existing events array
addEvent: function() {
if(this.event.name) {
this.events.push(this.event);
this.event = { name: '', description: '', date: '' };
}
}
}
});
Thanks for any help.
So, there are a few issues here. I've cleaned them up for you to look over.
Added the el: "#app" property to tell Vue where to mount your Vue.
Changed your ready to mounted (you could also use created).
Changed this.$set('events',events) to this.events = events.
Added the unreferenced deleteEvent method.
Fixed the click handler syntax for v-on:click.
So the code ends up looking like this.
new Vue({
el: "#app",
data: {
text: 'hello world',
event: { name: '', description: '', date: '' },
events: []
},
mounted: function() {
this.fetchEvents();
},
// Methods we want to use in our application are registered here
methods: {
// We dedicate a method to retrieving and setting some data
fetchEvents: function() {
var events = [...];
this.events = events;
},
// Adds an event to the existing events array
addEvent: function() {
if(this.event.name) {
this.events.push(this.event);
this.event = { name: '', description: '', date: '' };
}
},
deleteEvent(){
alert('delete this one')
}
}
});
Template
<div id="app">
<!-- show the events -->
<div class="col-sm-6">
<div class="list-group">
<a href="#" class="list-group-item" v-for="event in events">
<!-- <h1>{{text}}</h1> -->
<h4 class="list-group-item-heading">
<i class="glyphicon glyphicon-bullhorn"></i>
{{ event.name }}
</h4>
<h5>
<i class="glyphicon glyphicon-calendar" v-if="event.date"></i>
{{ event.date }}
</h5>
<p class="list-group-item-text" v-if="event.description">{{ event.description }}</p>
<button class="btn btn-xs btn-danger" v-on:click=" deleteEvent(event)">Delete</button>
</a>
</div>
</div>
</div>
Working example.

VueJS default/starting a value ina list of searched terms

Trying to make a search plugin using Vue and I'm having a problem with adding a starting/default value to the list of options. If I comment out the pair or template lines involving the start prop the rest of it works fine, but nothing renders if I leave them in.
Component Code:
Vue.component('search', {
props: {
type: String,
hidein: String,
start: {
type: Object,
default: null
}
},
//props: ['type', 'hidein', 'start'],
data: function () {
return {
search: "",
select: "",
results: [],
};
},
template: '<div #load="loaded"><input :id="hidein" type="text" v-model="search" #keyup="updateList">'+
'<input type="hidden" :name="hidein" v-model="select" class="form-control">'+
'<div v-if="start">Current: <span #click="select=start.id" :class="{\'label label-success\':(select==start.id)}>'+
'+ {{start.name}}</span></div>'+
'<div v-if="results.length">Do you mean:<ul>'+
'<li v-for="result in results" :key="result.id"><span #click="select=result.id" :class="{\'label label-success\':(select==result.id)}">'+
'+ {{result.name}}</span></li>'+
'</ul></div></div>',
methods: {
updateList: function(e) {
var response = [];
console.log("search: "+this.search);
$.ajax({
method: "GET",
url: "/api/search/"+this.type,
data: { key: this.search }
}).done(function( msg ) {
this.results = msg;
console.log(this.results);
}.bind(this));
},
loaded: function () {
this.select=!!this.start ? this.start.id : null;
}
},
});
Component Call:
<search type="ships" hidein="ship_id" ></search>
Can anyone tell me what I'm doing wrong? (Besides the hacked together template, that's hopefully a completely separate issue with the pipeline I'm having)
There is a missing " here
:class="{\'label label-success\':(select==start.id)}
But also, please, use a template literal to make your life easier.
`<div #load="loaded"><input :id="hidein" type="text" v-model="search" #keyup="updateList">
<input type="hidden" :name="hidein" v-model="select" class="form-control">
<div v-if="start">
Current:
<span #click="select=start.id" :class="{'label label-success':(select==start.id)}">
{{start.name}}
</span>
</div>
<div v-if="results.length">
Do you mean:
<ul>
<li v-for="result in results" :key="result.id">
<span #click="select=result.id" :class="{'label label-success':(select==result.id)}">
{{result.name}}
</span>
</li>
</ul>
</div>
</div>`