How to add "Column Search" with infyom laravel-generator datatable - laravel

Is-it possible to add "Column Search" with infyom laravel-generator yajra datatable.
I change tables_searchable_default from false to true
form this doc https://www.infyom.com/open-source/laravelgenerator/docs/8.0/configuration#options
But It does not change anything ...
Does so has a solution?
Thank in advance.

My answer on Infyom Gihthub
https://github.com/InfyOmLabs/laravel-generator/issues/972
How I did it (with a little modification in yajra/datables-html/builler - see Step 5)
0-https://datatables.net/examples/api/multi_filter.html
◦ reference example
1-php artisan infyom.publish:templates
2-resources/infyom/infyom-generator-templates/scaffold/views/datatable_body.stub
datatable_body.stub
#section('third_party_stylesheets')
#include('layouts.datatables_css')
#endsection
{{-- $dataTable->table(['width' => '100%', 'class' => 'table table-striped table-bordered']) --}}
{!! $dataTable->table(['width' => '100%', 'class' => 'table table-striped table-bordered'],true) !!} {{-- drawFooter --}}
3 config/datatables-html.php
'datatables::script',
'script' => 'layouts/script',
];
4 resources/views/layouts/script.blade.php
$(document).ready(function() {
// Setup - add a text input to each footer cell
$("#dataTableBuilder tfoot th").each( function () {
var title = $(this).text();
$(this).html( "" );
} );
});
$(function(){window.{{ config('datatables-html.namespace', 'LaravelDataTables') }}=window.{{ config('datatables-html.namespace', 'LaravelDataTables') }}||{};window.{{ config('datatables-html.namespace', 'LaravelDataTables') }}["%1$s"]=$("#%1$s").DataTable(%2$s
,initComplete: function () {
// Apply the search
this.api().columns().every( function () {
var that = this;
$( "input", this.footer() ).on( "keyup change clear", function () {
if ( that.search() !== this.value ) {
that
.search( this.value )
.draw();
}
} );
} );
}
} ); }
);
5 • vendor/yajra/laravel-datatables-html/src/Html/Builder.php
public function generateScripts()
{
$parameters = $this->generateJson();
$parameters=substr($parameters,0,strlen($parameters)-1); # remove the last } in order to add a parameter
return new HtmlString(
sprintf($this->template(), $this->getTableAttribute('id'), $parameters)
);
}

Related

Problem with edit function using a modal Laravel

I have the next function
public function edit($id)
{
if(request()->ajax())
{
$data = Tbl_Perimetro::findOrFail($id);
return response()->json(['result' => $data]);
}
}
public function update(Request $request, Tbl_Perimetro $user)
{
$rules = array(
'rif' => 'required',
'razon_social' => 'required',
'holdings_id' => 'required',
'pines_id' => 'required'
);
$error = Validator::make($request->all(), $rules);
if($error->fails())
{
return response()->json(['errors' => $error->errors()->all()]);
}
$form_data = array(
'rif' => $request->rif,
'razon_social' => $request->razon_social,
'holdings_id' => $request->holdings_id,
'pines_id' => $request->pines_id
);
Tbl_Perimetro::whereId($request->hidden_id)->update($form_data);
return response()->json(['success' => 'Datos actualizados satisfactoriamente.']);
}
My problem is that I would like to work with some models... and I know how to work with one model which is Tbl_Perimetro... I would like to work with another model called Tbl_Holding, But I'm using a modal to edit all this information.. here is the code of the modal:
$('#create_record').click(function(){
$('.modal-title').text('Add New Record');
$('#action_button').val('Add');
$('#action').val('Add');
$('#form_result').html('');
$('#formModal').modal('show');
});
$('#user_form').on('submit', function(event){
event.preventDefault();
var action_url = '';
if($('#action').val() == 'Add')
{
action_url = "{{ route('perimetro.store') }}";
}
if($('#action').val() == 'Editar')
{
action_url = "{{ route('perimetro.update') }}";
}
$.ajax({
url: action_url,
method:"POST",
data:$(this).serialize(),
dataType:"json",
success:function(data)
{
var html = '';
if(data.errors)
{
html = '<div class="alert alert-danger">';
for(var count = 0; count < data.errors.length; count++)
{
html += '<p>' + data.errors[count] + '</p>';
}
html += '</div>';
}
if(data.success)
{
html = '<div class="alert alert-success">' + data.success + '</div>';
$('#user_form')[0].reset();
$('#user_perimetro').DataTable().ajax.reload();
}
$('#form_result').html(html);
}
});
});
$(document).on('click', '.edit', function(){
var id = $(this).attr('id');
$('#form_result').html('');
$.ajax({
url :"/perimetro/"+id+"/edit",
dataType:"json",
success:function(data)
{
$('#rif').val(data.result.rif);
$('#razon_social').val(data.result.razon_social);
$('#holdings_id').val(data.result.holdings_id);
$('#pines_id').val(data.result.pines_id);
$('#carteras_id').val(data.result.carteras_id);
$('#hidden_id').val(id);
$('.modal-title').text('Editar Registro');
$('#action_button').val('Editar');
$('#action').val('Editar');
$('#formModal').modal('show');
}
})
});
This is the button to edit the information
$button = '<button type="button" name="edit" id="'.$data->id.'" class="edit btn btn-primary btn-sm">Editar</button>';
I would like to know is it exist a way to work with two models in the same modal... In this way I only can work with one model at the same time...
I think I got what you mean...
What you can do is using a route with 2 variables passed, the first variable could be the id of the tbl_perimetro and the 2nd variable could be the id of the tbl_holding.
So, this would fit like this:
public function edit($id, $idholding)
{
if(request()->ajax())
{
$data = Tbl_Perimetro::findOrFail($id);
$data1 = Tbl_Holding::findOrFail($idholding);
$alldata = array($data, $data1); //----->THIS WILL BREAK YOUR JSON STRUCTURE BE CAREFUL
return response()->json(['result' => $alldata]);
}
}
And the same in the update function:
public function update(Request $request, Tbl_Perimetro $user, Tbl_Holding $holding)
{
...
}
Be careful editing your routes, because starting in laravel 6 (I think) the variables HAVE to be literal, so the new routes have to match the variable name of the function p.ex:
Route::get('editmodels/{id}/{idholding}','Controller#edit'); //<----This will work
Route::get('editmodels/{id}/{id2}','Controller#edit'); //<----This won't work
Hope it helps!!

How to forceUpdate vue data

I am using vuejs with laravel and I have links for next and previous pages while url is changing but new data won't show.
Code
Controller
$verse = Chapter::where('slug', $slug)->with(['verses', 'book'])->first();
$next = Chapter::where('id', '>', $verse->id)->first();
$previous = Chapter::where('id', '<', $verse->id)->first();
return response()->json([
'verses' => $verse,
'next' => $next,
'previous' => $previous,
]);
component
<div v-if="previous !== null" :v-model="previous" class="bible-nav-button previous">
<router-link :to="`/${testament_slug}/${book_slug}/${previous.slug}`">
<i class="fas fa-chevron-left"></i> <span>Previous</span>
</router-link>
</div>
<div v-if="next !== null" :v-model="next" class="bible-nav-button next">
<router-link :to="`/${testament_slug}/${book_slug}/${next.slug}`">
<span>Next</span> <i class="fas fa-chevron-right"></i>
</router-link>
</div>
<script>
export default {
name: "books",
data() {
return {
url: window.location.origin + "" + window.location.pathname,
title: '',
testament_slug:'',
book_slug: '',
slug: '',
verses: [],
next: '',
previous: ''
}
},
methods: {
getBooks: function(){
this.testament_slug = this.$route.params.testament_slug
this.book_slug = this.$route.params.book_slug
this.slug = this.$route.params.slug
axios.get('/api/'+this.$route.params.book_slug+'/'+this.$route.params.slug+'/'+this.$route.params.slug).then((res) => {
this.verses = res.data.verses
this.title = "Read Bible: " +res.data.verses.book.name+ " " +res.data.verses.name
this.next = res.data.next
this.previous= res.data.previous
})
.catch((err) => {
console.log(err)
});
},
myHTML: function(item) {
return "<strong>"+item.number+"</strong> "+item.body+" ";
}
},
mounted() {
this.getBooks();
}
}
</script>
Any idea how i can fetch my new data?
You can use :key props in <router-view> tag and use the route fullpath.
<router-view :key="$route.fullPath"></router-view>
I used to put a watcher of the current route and call function to fetch data, but I just found that we can do this after watching vue-router tutorial from vueschool.
Also see the answer of this question
You have two simple options:
1) Change the "previous" and "next" to #click actions that call the fetch function again and then update the URL (like this this.$router.push()).
<span #click="goNext(/* needed arguments here */)">Next</span>
goNext (/* params here */) {
//set whatever variables you need to
this.$router.push(/* route as string here */)
this.getBooks()
}
2) Since you're using the router, use guards to update every time the URL changes:
https://router.vuejs.org/guide/advanced/navigation-guards.html
beforeRouteUpdate(to, from, next) {
this.getBooks()
next()
}

Laravel nova, custom resource tool not appears in edit mode

I have a custom resource-tool working fine in the view panel of a resource, but it dont appears when i go o the edit mode. Is there something i should add to the component or to the Nova configuration to enable the component in the edit mode?
Code in User.php
public function fields(Request $request)
{
return [
ID::make()->sortable(),
Text::make('First name', 'firstName')
->sortable()
->rules('required', 'max:255'),
Text::make('Last name', 'lastName')
->sortable()
->rules('required', 'max:255'),
Text::make('Email')
->sortable()
->rules('required', 'email', 'max:254')
->creationRules('unique:users,email')
->updateRules('unique:users,email,{{resourceId}}'),
Password::make('Password')
->onlyOnForms()
->creationRules('required', 'string', 'min:6')
->updateRules('nullable', 'string', 'min:6'),
YesNovaUserPermissions::make(),
];
}
User view:
User edit:
Nova does not seem to allow you to obtain this functionality with a custom resource but you can with a custom field. You basically create a "dummy" field which does not really exist on the model and use a mutator on the model to overwrite the default model saving functionality.
Following the documentation above, you can build a Vue component which will appear within the resource edit form itself, similarly to how I have done with the tags picker pictured below.
Code for that:
<template>
<default-field :field="field" :errors="errors" :show-help-text="showHelpText">
<label for="tag" class="inline-block text-80 pt-2 leading-tight">Tag</label>
<template slot="field">
<div id="multitag-flex-holder">
<div id="multitag-search-holder" class="w-1/2">
<div class="search-holder">
<label>Search Categories</label>
<input type="text" v-model="searchString" #focus="isSearching = true" #blur="isSearching = false" style="border:2px solid #000"/>
<div class="found-tags" v-if="isSearching">
<div v-for="(tag, i) in foundTags" #mousedown="addToSelected(tag)" :key="i">{{tag.name}}</div>
</div>
</div>
</div>
<div class="select-tags-holder w-1/2">
<div class="selected-tags">
<div v-for="(tag, i) in selectedTags" :key="'A'+i" #click="removeTag(tag)">{{tag.name}} X</div>
</div>
</div>
</div>
</template>
</default-field>
</template>
<script>
import { FormField, HandlesValidationErrors } from 'laravel-nova'
export default {
mixins: [FormField, HandlesValidationErrors],
props: ['resourceName', 'resourceId', 'field'],
data: function () {
return {
selectedTags:[],
isSearching:false,
searchString:''
}
},
mounted(){
console.log(this.field)
this.field.value.forEach((tag)=>{
this.addToSelected(tag)
})
formData.append('whereType', 'Tag');
},
computed: {
// a computed getter
foundTags() {
// `this` points to the vm instance
return this.field.tags.filter((tag) => {
if(tag.name.search(new RegExp(this.searchString, "i")) >= 0){
if(this.selectedTagNames.indexOf(tag.name) == -1){
return tag;
}
};
})
},
selectedTagNames(){
var selNames = this.selectedTags.map((tag) => {
return tag.name;
})
return selNames;
}
},
methods: {
/*
* Set the initial, internal value for the field.
*/
setInitialValue() {
this.value = this.field.value || ''
},
removeTag(tag){
var index = this.selectedTags.indexOf(tag);
if (index > -1) {
this.selectedTags.splice(index, 1);
}
},
addToSelected(tag){
this.selectedTags.push(tag)
},
/**
* Fill the given FormData object with the field's internal value.
*/
fill(formData) {
var tagIdArray = []
this.selectedTags.forEach((tag)=>{
tagIdArray.push(tag.id)
})
formData.append(this.field.attribute, tagIdArray)
},
},
}
</script>
Then, you can overwrite how the save functionality works in your model to accommodate for the "dummy" field. Note below instead of syncing the tags directly on the mutator, which will work most of the time depending on your data structure, I had to pass the tags to the "Saved" event on the model to accommodate for when creating a new record and the associated record id is not yet available, thus cannot be synced for a many to many relationship.
public function setTagsAttribute($value)
{
$tags = explode(",", $value);
$this->tempTags = $tags;
unset($this->tags);
}
protected static function booted()
{
static::saved(function ($article) {
$article->tags()->sync($article->tempTags);
});
}

Codeigniter auto suggestion text box

I'm using codeigniter in my project and want to implement a text box which suggests related word s from the data base. In this one I want to get the ID of the selected vehicle. But so far I was only able to retrieve the vehicle names with out IDs.
The code so far,
Model
function searchVehicle($name){
$this->db->like('Name', $name, 'both');
return $this->db->get('vw_vehicle_search')->result();
}
Controller
public function vehicle_search(){
$this->load->model('model_vehicle');
if(isset($_GET['term'])){
$result = $this->model_vehicle->searchVehicle($_GET['term']);
if(count($result)>0){
foreach($result as $object)
$arr_result[] = $object->Name;
echo json_encode($arr_result);
}
}
}
View
<script type="text/javascript">
$(document).ready(function(){
$('#vehicle_name').autocomplete({
source: "<?php echo base_url();?>vehicle/vehicle_search/?"
});
});
</script>
<div class="col-md-4">
<?php
$input_data = array(
'name' => 'vehicle_name',
'id' => 'vehicle_name',
'class' => 'form-control'
);
echo form_input($input_data)?>
</div>
How can I pass the id of the vehicle with this one and get the id when i select a vehicle to insert to the db.
Thank you.
Try it in this way:
controller:
public function vehicle_search(){
$this->load->model('model_vehicle');
if(isset($_GET['term'])){
$result = $this->model_vehicle->searchVehicle($_GET['term']);
if(count($result)>0){
foreach($result as $object)
$arr_result[] = array( 'label' => $object->Name, 'value' => $object->id);
echo json_encode($arr_result);
}
}
}
View:
$(document).ready(function(){
$('#vehicle_name').autocomplete({
source: "<?php echo base_url();?>vehicle/vehicle_search/?",
select: function(event, ui) {
event.preventDefault();
$("#vehicle_name").val(ui.item.label);
//$("#vehicle_name-hidden").val(ui.item.value);
},
focus: function(event, ui) {
event.preventDefault();
$("#vehicle_name").val(ui.item.label);
}
});
});

How to update pagination template of knppaginatorbundle after ajax query

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"])
}
}

Resources