Combining laravel pagination with live search using ajax - php

I have a list of projects displayed in a table which has a pagination at the bottom by using laravel's paginatation.
$projects = projects::where('active', '=', 1)->orderBy('id', 'desc')->paginate(15);
return view('index', compact('projects'));
I also have a live search to filter the projects by name, using ajax.
public function search(Request $request){
if($request->ajax()){
$query = $request->get('query');
if($query != ''){
$data = DB::table('projects')
->where('active', '=', 1)
->whereNull('deleted_at')
->where('name', 'like', '%'.$query.'%')
->get();
}else{
$data = DB::table('projects')->where('active', '=', 1)->whereNull('deleted_at')->orderBy('id', 'desc')->get();
}
}
return response($data->jsonSerialize(), Response::HTTP_OK);
}
Js:
searchProjects = function(query){
$.ajax({
url: "/projects/search",
type: "POST",
data: {
query: query,
},
success: function (response){
displayData(response);
}
})
},
So first I load the projects via server side and then if I do a search, the data gets replaced with the result via ajax.
The problem I'm facing is that whenever I do a search, the paginations doesn't update.
Let's say the search displays 5 results, I want the pagination to go from 1-2 to 1-1 or even just disappear (if that makes sense) if there are less than 15 results (for instance).
Does anyone have any idea how I would combine both a live search and a pagination that work together on laravel?

I believe your problem is you want to combine both PHP generated HTML with JS generated HTML.
The paginate part will generate a specific HTML, if you want to update your data without a full reload of the page (via the Ajax call) you need to regenerate the HTML.
You can do this by creating a JS that builds your HTML. However this would mean you have 2 places to maintain your HTML.
I would propose to update your code to (instead of the JSON data) return a newly generated HTML part, and inject this new HTML in your page.
Also be sure to update your search function to make full use of you model instead of using the DB::table.
Hope this response is a bit clear.

Let's say you have a jobs/index.blade.php file. This will act as the base file for your jobs. It will list all the jobs available:
<div class="container">
<div class="row">
<div class="col-sm-9">
#if (count($jobs) > 0)
<section class="jobs">
#include('jobs.load')
</section>
#endif
</div>
<div class="col-sm-3">
</div>
</div>
</div>
<script>
$(function() {
$('body').on('click', '.pagination a', function(e) {
e.preventDefault();
$('#load a').css('color', '#dfecf6');
$('#load').append('<img style="position: absolute; left: 0; top: 0; z-index: 100000;" src="/images/loading.gif" />');
var url = $(this).attr('href');
getJobs(url);
window.history.pushState("", "", url);
});
function getJobs(url) {
$.ajax({
url : url,
success: function(data) {
$('.jobs').html(data);
},
error: function(err) {
alert("jobs cannot be loaded");
console.log(err);
}
});
}
});
</script>
This will be the file component, jobs/load.blade.php that will be requested on the ajax:
<div id="load" style="position: relative;">
#foreach($articles as $article)
<div>
<h3>
{{$article->title }}
</h3>
</div>
#endforeach
</div>
{{ $articles->links() }}
Now to open the page, you will need a controller function, it is self documented:
public function index(Request $request)
{
// get jobs and limit to 5 data
$jobs = Jobs::paginate(5);
// this condition will be executed on the ajax request
// that is everytime you click on prev/ next
// it will be handled by jquery's .html() function
if ($request->ajax()) {
return view('jobs.load', ['jobs' => $jobs])->render();
}
// on traditional HTTP request, it will load the base page
return view('jobs.index', compact('jobs'));
}
Reference:
https://laraget.com/blog/how-to-create-an-ajax-pagination-using-laravel

Related

Laravel how to pass data from controller to modal dialogue using ajax

I have items and for every item there are related items, so when I open the homepage it shows all item, when I want to click on any item, ajax will pass this item id to controller to get the related items for this item, the problem is I want to show the related item in a modal dialogue, the modal dialogue now show all related items to all items not to the current item.
I think the problem is because the include of modal in the homepage which has the foreach!, hope you can help me in solving this issue
route
Route::get('/',['as'=>'showItems','uses'=>'HomeController#getItem']);
Route::get('Get_relateditem',['as'=>'Get_relateditem','uses'=>'HomeController#getItem']);
ajax
$(function(){
$('.Item_root').on("click", function () {
var item_id = $(this).data('id');
$.ajax({
type:'get',
url: '/',
data: {
'_token': $('input[name=_token]').val(),
'item_id':item_id,
},
success:function(data){}
});
});
});
controller
public function getItem(Request $request)
{
$currentitemid =$request->item_id;
$ritems = Relateditem::orderBy('id', 'asc')->where('ritemf_id','LIKE','%'.$currentitemid.'%')->with('items')->get()->groupBy('ritemf_id');
$items = Item::orderBy('category_id', 'asc')->with('category')->get()->groupBy('category_id');
$categories = Category::orderBy('category_id', 'asc')->get();
return view('home')->with('items',$items)->with('categories',$categories)->with('ritems',$ritems);
}
}
modal
#foreach($ritems as $item_id => $realtedItems)
#foreach($realtedItems as $ritem)
<div class="SuggestedItem_container">
<label color="red" class="Checker_root Checker_red Checker_left">
<input type="checkbox" class="Checker_input" value="on">
<div class="SuggestedItem_nameContainer">
<div>
<span class="SuggestedItem_name">{{$ritem->riteml_id}}</span>
<span class="SuggestedItem_price styles_small styles_base styles_spacing-base">+$3.95</span></div></div>
<div class="SuggestedItem_description styles_small styles_base styles_spacing-base">
<span class="SuggestedItem_category styles_bold">Appetizers</span>
<span> ยท Edamame soybean pods harvested right before the beans begin to harden are lightly boiled and seasoned with sea salt.</span>
</div>
</label>
</div>
#endforeach
#endforeach
Modify routes:
Route::get('/','HomeController#getItem');
Route::get('/get_related_items/{id}','HomeController#getRelatedItems');
modify getItem to get only the items and categories:
public function getItem(Request $request)
{
$items = Item::orderBy('category_id', 'asc')
->with('category')->get()
->groupBy('category_id');
$categories = Category::orderBy('category_id', 'asc')->get();
return view('home',['items' => $items,'categories' => $categories]);
}
get related items for a single item id:
public function getRelatedItems(Request $request, $id)
{
$ritems = Relateditem::orderBy('id', 'asc')
->where('ritemf_id',$id)
->with('items')
->get()
->groupBy('ritemf_id');
return response()->json($ritems);
}
now for the js part:
$(function(){
$('.Item_root').on("click", function () {
var item_id = $(this).data('id');
$.ajax({
type:'get',
url: '/get_related_items/' + item_id,
data: {
'_token': $('input[name=_token]').val()
},
success:function(data){
if(data && data.length){
var con_el = $('.SuggestedItem_container');
for(var i = 0; i < data.length;i++){
var related_item_el = "<div class='related_item'><p>" + data[i].id + "</p></div>"
con_el.append(related_item_el);
}
}else{
console.log('no related items found');
}
}
});
});
});
this will insert all the related items inside the SuggestedItem_container
i didn't write the view template cuz that part is easy, and note that i only included the related item id as example cuz i don't know what fields the item has.
i hope this helps you

Laravel with angularJS load template after $http call

I am using Laravel with AngularJs trying to fetch data using $http method of angularJS
I got data successfully but the problem is that I want to redirect to another page to show the result how can I achieve this using AngularJs?
Here is my jobs.js controller
app.controller('JobsController', function($scope, homepagesearchService) {
$scope.opts = [];
$scope.city = ["All", "Dubai", "Abu Dhabi", "sharjah", "ras al khaimah", "ajman", "umm al quwain", "fujairah", "Al Ain"];
for (var i = 0; i <= 30; i++) {
$scope.opts.push(i);
}
$scope.homepageSearch = function(search) {
homepagesearchService.getData(search).then(function(data) {
$scope.result = data;
console.log(data);
});
}
});
A service to get data from database
angular.module('jobs').factory('homepagesearchService', function($http){
return {
getData: function(search){
if(angular.isUndefined(search.keyword)){
search.keyword = 'search-job';
}
return $http({
method : 'POST',
url : '/search/'+angular.lowercase(search.keyword).replace(/[^a-zA-Z0-9_]/g,'-')+'-'+'jobs',
data : search, //forms user object
headers : {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function(result){
return result.data;
});
}
}
});
JobsController from where I got the data
<?php
/*
*Developer: Kritika Shrivastava
*Jobs Controller to Fetch All Jobs
*
*/
namespace App\Http\Controllers;
use App\Http\Requests;
use Illuminate\Http\Request;
use App\Jobs;
class JobsController extends Controller
{
/**
* Show the application welcome.
*
* #return \Illuminate\Http\Response
*/
public function welcome(){
return view('welcome');
}
/*
*
*Ajax Call to search Home Page
*
*/
public function homepageSearch(){
$_POST = json_decode(file_get_contents('php://input'), true);
$jobs = Jobs::latest('created_at')->search()->get();
echo $jobs;
}
}
This is my search form
#extends('layouts.app')
#section('content')
<div ng-app="jobs">
<div class="m-banner-1" ng-controller="JobsController">
<div class="main ui container m-banner-1-cover" >
<form ng-submit="homepageSearch(search)">
<div class="ui grid">
<div class="sixteen wide mobile five wide tablet five wide computer column auto-k">
<p style="color:#ffffff;font-size:20px;">Enter Keywords</p>
<div class="ui fluid icon input">
<input type="text" placeholder="Search a very wide input..." ng-model="search.keyword">
</div>
</div>
<div class="sixteen wide mobile five wide tablet five wide computer column ui fluid">
<p style="color:#ffffff;font-size:20px;">Location</p>
<select class="ui fluid normal dropdown"
ng-options="cit for cit in city" ng-model="search.city">
</select>
</div>
<div class="sixteen wide mobile five wide tablet five wide computer column">
<p style="color:#ffffff;font-size:20px;">Experience</p>
<select class="ui fluid normal dropdown" ng-model="search.experience"
ng-options="opt as opt for opt in opts">
<option value="">Years</option>
</select>
</div>
<div class="sixteen wide column align center">
<button class="ui massive button orange-btn wide-bt">Find Jobs</button>
</div>
</div><!--ui grid-->
</form>
</div><!--ui container-->
</div><!--m-banner-1-->
</div>
<br>
<br>
#endsection
I want when user clicks search button it should redirect to another page and display data got from Ajax call.
Also please suggest is this the correct way of achieving this using AngularJS.
I am new to AngularJs and Laravel.
There are multiple ways to achieve this.
1. Load data in a service. After get data from http
$scope.homepageSearch = function(search) {
homepagesearchService.getData(search).then(function(data) {
$scope.result = data;
DataService.set(data);
// and move to another page using $state.go('whatever page you want')
console.log(data);
});
}
//in that page load data from service
var data = DataService.get()// and here you can use that data
//in service
//make a var holdingdata; like
set(data){
holdingdata = data;
}
get(){
return holdingdata;
}
you can use localStorage after http success save data in local storage, and move to another page and get data from localStorage
you can also use $state service of angular like
$state.go('toState', { 'data':httpData});
and from that page get data from param using $stateParam service of anguar.
***From my point of view method 1 and 3 is good. you can use any of them.
I consider another page means ANOTHER VIEW OF ANGULAR APP:
app.config(function ($routeProvider, $locationProvider) {
$routeProvider
.when('/router-url', {
controller: 'AnotherController',
templateUrl: 'Partials/ViewStart.html'
})
$scope.homepageSearch = function(search) {
homepagesearchService.getData(search).then(function(data) {
$scope.result = data;
console.log(data);
$location.path('/router-url/');
});
}

AngularJS Pass Database Data between Controllers

I am a little confused about how to pass my database data loaded in a controller to another controller.
I load some list items from the server and on each item click I want to open the details about that item on another screen according to its id.
I read some questions about making a service or use $rootScope but $rootScope should be avoid as much as possible.
What is the best way of doing this in my case and could you show me how to do it? Should I load the data inside a service or there is an easiest way in my case?
list item using 1st controller:
<div class="item item-body list-container" id="temporada2016-list-item-container4" ng-model="item_id" ng-repeat="x in items" item="x" href="#/x/{{x.ID}}" ng-click="open_item(x)" ng-show="news_list">
<div id="temporada2016-markdown7" style="margin-top:0px;color:#666666;">
<h2 style="color:#008BBB;">{{ x.TITLE }}</h2>
</div>
</div>
1st controller
.controller('temporada2016Ctrl', ['$scope', '$http', function ($scope, $http) {
$scope.active_news_btn = true;
$scope.search_news = true;
$scope.news_list = true;
$scope.albums_list = false;
$http.get("http://localhost/select-news.php").then(function(response){
console.log(response);
console.log(JSON.stringify(response));
$scope.items = response.data;
});
$scope.open_item = function(x){
//alert("Item id: " + x.ID);
$http.post("http://localhost/select-news-by-id.php", {'item_id': x.ID}).then(function(response){
console.log(response);
console.log(JSON.stringify(response));
$scope.all = response;
$scope.title = response.data[0].TITLE;
$scope.body = response.data[0].BODY;
});
}
}])
second screen (details) using 2nd controller where I want to load the same title and news body
<ion-view title="Detalhes" id="page4" style="background-color:#FFFFFF;">
<ion-content padding="true" class="has-header">
<h3 id="detalhes-heading1" style="color:#008BBB;font-weight:600;font-style:italic;">{{title}}</h3>
<div id="detalhes-markdown3" style="color:#000000;">
<p>{{body}}</p>
</div>
<form id="detalhes-form4" class="list">
<button id="detalhes-button6" style="color:#008BBB;text-align:left;border-radius:9px 9px 9px 9px;" class="button button-calm button-clear icon ion-ios-heart-outline like_btn"></button>
<label class="item item-input" id="detalhes-textarea1">
<span class="input-label"></span><textarea placeholder=""></textarea>
</label>
</form>
<button id="detalhes-button17" style="color:#FFFFFF;" class="button button-positive">Comment</button>
</ion-content>
</ion-view>
2nd controller
.controller('detalhesCtrl', ['$scope', '$stateParams', function ($scope, $stateParams) {
}])
PHP
<?php
include_once('conn.php');
$data = json_decode(file_get_contents("php://input"));
if(property_exists($data, 'item_id')){
$item_id = $data->item_id;
$sql = $mysqli->query("SELECT * FROM news WHERE id = '".$item_id."'");
if($sql->num_rows > 0){
while($row = $sql->fetch_array(MYSQLI_BOTH)){
$registro = array(
"ID" => $row['id'],
"TITLE" => $row['title'],
"BODY" => $row['body']
);
$retorno[] = $registro;
}
}
$mysqli->close();
$retorno = json_encode($retorno);
echo $retorno;
}
?>
In your app-config
$stateProvider
.state('master', {
url: '/master',
templateUrl: 'views/master.html',
controller: 'MasterCtrl',
data: {
someThingToPassToMasterState: false
}
})
.state('details', {
url: '/details',
data : {
somethingToPassToDetailsState: false
},
templateUrl: 'views/details.html',
controller: 'DetailsCtrl'
});
And then in your MasterCtrl
$scope.onClick = function(obj) {
var dataToPass = {};
dataToPass.obj = obj;
dataToPass.somethingElse = 'blah blah';
$state.go('details', {somethingToPassToDetailsState: dataToPass});
}
// Now in the DetailsCtrl
if(!$state.params.somethingToPassToDetailsState) {
// handle this
// maybe do a $state.go('default') and then return to end execution of this controller
}
// Some code
In master.html, using ng-repeat to simulate master-details page redirection
<div ng-repeat="o in objects">
<div ng-click="redirectTo(o)">{{o.name}}</div>
</div>
The idea is to pass day directly from one state to another on state transition. You can either pay in I'd and make api call AFTER transitioning to these new state or get the response from api and then paas required data to the next state
First of all, it is highly recommended you make your http calls in a factory or a service. This will make your code more reusable and it will look something like this:
app.factory("responseFactory", function($http) {
return {
getData: function() {
//Code for making http call goes in here
$http.get("http://localhost/select-news.php").then(function(response){
return(response.data);
});
},
postData: function(x) {
$http.post("http://localhost/select-news-by-id.php", {'item_id': x.ID})
.then(function(response){
return(response.data);
});
}
};
});
You could later use this to call in your controller by injecting this factory in your controller and calling this factory something like this:
app.controller('temporada2016Ctrl', ['$scope', 'responseFactory', function ($scope, responseFactory) {
$scope.items = responseFactory.getData();
$scope.opnItem = function(x){
$scope.all = responseFactory.postData(x);
$scope.title = all.TITLE;
$scope.body = all.BODY;
}
}]);
Now, to make the data available in your second controller, you could do a few things.
Pass it through the $rootScope, which as you already said, should be avoided as much as possible to not clutter the rootScope. It can have many consequences. - NOT RECOMMENDED
Make a service call from the second controller and you will have all the data you need from the api. However, if you edit the data in the first controller and wish to make edited data available in the second controller, that will not be possible using this method. Also, making http calls is costly and it highly recommended to minimize your number of http calls in the app. - NOT RECOMMENDED
Use Angular service/factory - HIGHLY RECOMMENDED
app.factory('commonData', function() {
var data;
return{
setData: setData,
getData: getData
};
//setter
function setData(dataToBeShared) {
data = dataToBeShared;
}
//getter
function getData() {
return data;
}
});
Now you can inject this factory into your controllers and use the setter and getter methods easily. DO not forget to inject the responseFactory which we created earlier!
After injecting it into your first controller, you can call the commonData factory and use the setter method to set the data, something like this:
app.controller('temporada2016Ctrl', ['$scope', 'responseFactory', 'commonData', function ($scope, responseFactory, commonData) {
//All your controller code, including calling the factory like I earlier explained...it all goes here
commonData.setData(passTheDataThatYouWantToShare);
}]);
Now, to get the data in the other controller, all you need to do is access the factory's getter method and you get the data! That will be something like this:
app.controller('detalhesCtrl', ['$scope', '$stateParams', 'commonData', function ($scope, $stateParams, commonData) {
$scope.commonData = commonData.getData();
//Use $scope.commonData to use all the data that has come in from first controller
}]);
Now, the data that is passed from controller 1 is stored in the factory and can be retrieved in the second controller whenever you want. Suppose you would like to display them as panes, next to each other, you might want to add watchers, otherwise this method should work fine for you.
NOTE: This can be achieved without using setter and getter methods, but using them is a good practice and is very useful when an app gets bigger.
Pass data through state params using Angular UI router. It seems to me that you are using Angular UI router which comes bundled with ionic. This can also be used when you are routing. At the time of writing this answer, another answer on this thread (by SLearner) has already explained this method and if it is recommended or not is more or less your choice depending on the level of functionality you want. However, in my opinion, I would not go in with this solution. You can find some more answers on this topic on this thread: AngularJS: Pass an object into a state using ui-router
So, concluding my answer, in my opinion, it is best you go in for an angular factory. IMHO, that is the best solution. Hope your queries are answered.
Cheers!
Share data between two controller is not good practice.
We need to put the data in service which can easily be shared with any number of controllers
Service: MyService
this.dbDataSearch = function(parameters){
// Search record from database
this.resultData = data;
}
In Convtoller 1:
$scope.data = MyService.resultData;
In Convtoller 2:
$scope.data = MyService.resultData;
....
In Convtoller n:
$scope.data = MyService.resultData;
Once service variable will update all these controller variables automatically updated.

Updating content Laravel

I want to make a feature for my web app in laravel(i'm new at it) that every Post/Comment/Theme(in my case) the user has the ability to upVote and downVote. Now i am woking with upVote, but it is not working whatsoever.
In the view (welcome.blade.php)
I have:
<img class="media-object" style="height:40px; width:40px;" src="images/upVote.svg" alt="...">
Where $Theme->name is the one that the user wants to upVote/like(whatsoever).
The route:
Route::put('/', [
'uses'=>'Vote#VotePlus',
'as' =>'Voteplus' //Name of route
]);
And the controller:
<?php
namespace App\Http\Controllers;
use App\NewTheme;
use DB;
class Vote extends Controller {
public function VotePlus($name){
DB::table('New_Themes')
->where('name', $name)
->increment('upVotes', 1);
$Themes = NewTheme::paginate(5);
return redirect()->route('welcome', ['Themes'=>$Themes]);
}
};
I am trying everything, but it isn't working. Can someone help me please?
With an anchor tag, you only send a get request. If you want it to be put, you must create a form, and then add :
<input type="hidden" name="_method" value="PUT">
Another way to solve this issue is to use Ajax. Right now the page will get refreshed each time a user wants to up-vote a theme and that can be quite frustrating.
I think you should post to the back end using Ajax and on the success callback update the view with javascript. I recommend using Angular for the front end. It has all what you need and it is super simple to make an Ajax-request.
So, here's a quick example how you could use Angular + Laravel to make your web application work.
Front End
<html ng-app="exampleApp">
<head>
<title>MyTitle</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body>
<div ng-controller="ThemeController">
<ul>
<li ng-repeat="theme in themes">
<span><% theme.name %></span>
<p><% theme.description %></p>
<p><% theme.votes %></p>
Vote Up
</li>
</ul>
</div>
<script type="text/javascript">
var app = angular.module("exampleApp", [], function($interpolateProvider) {
// This we need to not collide with Laravel's {{ }}
$interpolateProvider.startSymbol('<%');
$interpolateProvider.endSymbol('%>');
});
app.controller('ThemeController', function($scope, $http) {
$scope.themes = [];
$scope.voteUp = function(theme) {
$http({
url: '/api/themes/voteUp',
method: 'POST',
data: {
id: theme.id
}
}).success(function(response) {
theme.votes += 1;
});
}
// On init we need to get the themes
$http({
url: '/api/themes',
method: 'GET'
}).success(function(themes) {
$scope.themes = themes;
});
});
</script>
</body>
</html>
Back End
Your routes
Route::get('api/themes', 'ThemeController#getFive');
Route::post('api/themes/voteUp, 'ThemeController#voteUp');
Your ThemeController
function getFive() {
return Theme::paginate(5);
}
function voteUp(Request $request) {
$theme = Theme::whereId($request->id);
$theme->votes += 1;
$theme->save();
return $theme;
}
This code is not tested. But I'll think you get the point!

Symfony JsonResponse: Fetching each element of multidimensional array returned by symfony controller

basically what I am trying to do is trying to fetch each element from the array of "arrays" passed by symfony controller when I try to fetch all the rows of the table. my controller is working absolutely fine, and giving me output:
[{"id":1,"name":"johnsonn's baby shampoo","dsc":"shampoo","pic":"572f8a02d59b3.jpg","company":4,"type":"baby products","price":150,"sold":null,"stock":200},{"id":2,"name":"johnson's soap","dsc":"baby soap","pic":"57303fc35f72c.jpg","company":4,"type":"baby products","price":52,"sold":null,"stock":1000}]
and my javascript for fetching this output is the following:
<script>
function fetchprod() {
$.ajax({
url: "/show/prod",
dataType: "json"
}).success(function(data){
$('#container').html(JSON.stringify(data));
});
}
fetchprod();
setInterval(function () {fetchprod()}, 3000);
</script>
what I wanna do is fetch each element of each array separately so that I can place create a div out of them so they could be arranged in a grid properly.
also the array is fetched by symfony controller (if you need an extra info):
public function showAction($slug){
$em = $this->getDoctrine()->getManager();
if($slug == 'prod'){
$repo = $em->getRepository('SystemBundle:Products');
$q = $repo->createQueryBuilder('u')
->getQuery()
->getArrayResult();
return new JsonResponse($q);
}
}
Since you're using jQuery, maybe you could use the each method:
// ajax call
.success( function (data) {
$.each(data, function(key, value) {
var div = '<div>' + JSON.stringify(value) + '</div>';
$('#container').append(div);
});
});
// close function
In this way you iterate over every object in the array, place it in a div, and after that append it to the #container.
It should create something like:
<div id="container">
<div>{"id":1,"name":"johnsonn's baby shampoo","dsc":"shampoo","pic":"572f8a02d59b3.jpg","company":4,"type":"baby products","price":150,"sold":null,"stock":200}</div>
<div>{"id":2,"name":"johnson's soap","dsc":"baby soap","pic":"57303fc35f72c.jpg","company":4,"type":"baby products","price":52,"sold":null,"stock":1000}</div>
</div>

Categories