I'm using Laravel 5.5.* and jQuery (jquery-3.3.1.min.js).
I commercially develop mostly (like 95% of the time) in PHP, so using jQuery is really different for me, so I need help.
I am developing a blog's landing page and I must show just 3 posts in it. In it's bottom, I have a button <a> that is supposed to load 3 more posts and show it to the user. Everytime the user hits this button, 3 more posts must load in the page.
I have the following codes so far.
Posts controller
public function index() {
// this loads the landing page with 3 initial posts
// Working like a charm
$posts = Posts::with('categories', 'user', 'media')
->where('status', 1)
->orderBy('published', 'desc')
->limit(3)
->get();
$rank = self::blogPanel();
return view('portal.pages.blog',
compact(
'rank',
'posts'
)
);
}
I call this action from the route
Route::get('/', 'User\PostsController#index')->name('landingPage');
For the logic in which I load more posts, I have the following:
Posts Controller
public function loadMore() {
$posts = Post::with('categories', 'user', 'media')
->where('status', 1)
->orderBy('publicacao', 'desc')
// ->limit(3) I took this out because I was trying to set the limit in front-end
->get();
return json_decode($posts);
}
Which returns the following:
array:48 [▼
0 => {#257 ▼
+"id": 48
+"title": "Lorem ipsum"
+"summary": "Perferendis labore veritatis voluptas et vero libero fuga qui sapiente maiores animi laborum similique sunt magni voluptate et."
+"content": """
Really long text here, with line-breaks and all
"""
+"seo_id": null
+"url": "Sunt rerum nisi non dolores."
+"link_title": "dolor"
+"published": "2018-03-01 10:35:12"
+"scheduled": "2018-03-01 10:25:12"
+"user_id": 1
+"media_id": null
+"status": 1
+"created_at": "2018-03-01 10:25:12"
+"updated_at": "2018-03-01 10:25:12"
+"category_id": 3
+"slug": "cum-aut-officia-consequatur-dolor"
+"categories": []
+"user": {#313 ▼
+"id": 1
+"name": "Jonessy"
+"email": "jonessy#email.com"
+"status": 1
+"grupo_id": 1
+"created_at": null
+"updated_at": null
}
+"media": null
}
1 => {#341 ▶}
2 => {#254 ▶}
]
Please, note I'm using json_decode() because it looks easier to work with in front-end.
This is my blade file, where I should print my results
blogPost.blade.php
#foreach($posts as $post)
#php
$date = date_create($post->published);
#endphp
<div class="blog-post" id="blog-post">
<div class="blog-post__title" >
<h3 id="artTitle">
{{ $post->title }}
</h3>
#foreach($post->categories as $cat)
<span class="blog-post__category"> {{ $cat->name }} </span>
#endforeach
<span class="blog-post__date" id="artDate">
{{ date_format($date,"d/m/y - H") }}H
</span>
</div>
<div class="blog-post__image" id="artImage">
#if(isset($post->media_id))
<img src="{{ asset('img/post-img/' . $post->media->file) }}">
#else
<img src="{{asset('img/post-img/default-img-post.jpg')}}">
#endif
</div>
<div class="blog-post__resume">
<p id="artSumma">
{{ $post->summary }}
</p>
</div>
<div class="blog-post__link">
<a href="{{ route('blogPost', $post->slug) }}">
Read More
</a>
</div>
<div class="blog-post__social">
// Some social media links for sharing
</div>
</div>
#endforeach
I am calling the loadMore() method from PostsController using a GET route:
Route::get('/', 'User\PostsController#loadMore')->name('loadMore');
For jQuery, here is the code I got so far:
<script type="text/javascript">
// after importing jquery file...
$(document).on('click', '#loadingPosts', function(e) {
e.preventDefault();
var listing = {!! $posts !!};
console.log("list ", listing);
var lastId = listing[2].id;
console.log("id from pos 2, listing ", lastId);
var parts = $(listing).slice(lastId);
console.log("part ", parts);
// THIS DOESN'T WORK, BY THE WAY
// var lastId = listing[2].id;
// console.log("listing 2 id", lastId);
$('#loadingPosts').html("Loading More Posts");
$.ajax({
url: '{{ route('loadMore') }}',
method: 'GET',
// data: {
// 'id': lastId,
// I was trying to set up an ID here, but it didn't work as well
// },
// contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(data) {
// console.log("checking if data not null", data);
// this returns the expected $posts array
$('#blog-post').html(data);
// using .html() because .append() didn't work, which is weird, I guess
console.log("data depois do apend", data);
// This returns the same $posts array
lastId = data[2].id;
console.log("last id from data", lastId);
// I can retrieve the id from the given position from the array.
// I was trying to set up the id here so I could check, with jQuery, the last post iterated, so I could continue from that point forward
$('#loadingPosts').html("load More");
return data[2].id;
// A last minute despair
}
});
});
</script>
Well, it doesn't work (that's the reason I'm here). I really don't know what I am doing wrong, since the $posts array is passing...
I need help with this, please.
A few things worth saying:
Laravel comes with a default pagination, but it works "horizontally", and the projects asks for a "vertical" pagination.
The page must have a "load more" button because the footer has some much needed info, so the content can not load automatically
IF there is a way to make it work using vanilla JavaScript OR using Laravel's PHP methods (EXCEPT FOR THE PAGINATION METHOD, AS STATED BEFORE), I would be really happy
Thank you all in advance.
public function loadMore(Request $request) {
$posts = Post::with('categories', 'user', 'media')
->where('status', 1)
->orderBy('publicacao', 'desc')
->limit($request->input('limit'))
->skip($request->input('skip'))
->get();
return json_decode($posts);
}
But you can just use the next page from pagination()
So, after a little while I came up with a fix for my needs.
Turns out I didn't need to json_encode() or json_decode() anything.
First of all, I'll use a pseudo mark-up for everything inside blades. It'll be easy to understand, since what I am using is HTML. For jQuery, someone involved with the project came up with a pseudo-jQuery-like functions that emulate its syntax. It is a straight forward syntax, easy to understand, nothing out of the ordinary.
Then, here it is.
PostsController
public function loadMore(Request $request) {
$limit = $request->input('limit');
$skip = $request->input('skip');
// as #Dry7 suggested, I am taking a dynamic skip
$posts = Post::with('categories', 'user', 'media')
->where('status', 1)
->orderBy('published', 'desc')
->limit($limit)
->skip($skip)
->get();
return view(
'portal.pages.blogPost',
compact(
'posts'
)
)->render(); // here is the difference
}
So, what I did is pre-render the view where the posts will be printed WITHOUT loading a new page.
Before we continue, here is the structure of the blog.(Using pseudo-markup, as stated before)
main page
#extends('layouts.layout')
div class=container
div class=blog
h1
Page title
/h1
div class=blog-body
#include('portal.pages.blogPost')
a id=loadMorePosts class=none
Load More
/a
/div
div class=sidebar
#include('portal.components.panel')
/div
/div
/div
Then in my pages.blogPost I have the same code I posted in my question (The code is the one with the foreach loop).
After this, I came up with this pseudo-jQuery-like code.
// I'll start listening to any 'click' in the element I am passing the event
// then I'll increment the number of clicks in the element
var click = 0;
// this is the first skip number
var startCounting = 6;
// start a click event in the <a #loadMorePosts> element
$.event('#loadMorePosts','click',function () {
// increment the number of clicks
click++;
// set my skip that will be sent to server and
// applied in my PostsController
skip = startCounting * click;
// open an ajax request passing the route I set up
// that calls PostsController#loadMore method
HttpRequest.get('{{ route('loadPosts') }}?limit=6&skip=' + skip,function (res) {
// I am concatenating my skip var here, so It'll be sent to server
// checking if I have an empty data
if(res.data != "") {
// not empty, so I'll append it to my div with blog class
// first finding the element, searching for its class
// then passing the data to be appended
$.append('.blog',res.data);
} else {
// the data is empty, so first I'll search for
// the element with class=none
// clean up any innerHtml it has
// then set up a new innerHtml in it
$.replaceAll('.none',"No More Posts");
// to finish it all up, I style the same element with some suggesting colors and actions
$.css('.none', 'pointer-events: none; background-color: lightgrey;');
}
});
});
And its done. The posts are appended, the skip is working, so I don't take repeated posts, it works until all of my posts are loaded and when there are no more posts to show, the button is disabled, stopping any new request to be sent to server.
I hope that with these comments the process made to implement this functionality is clear and you can apply the same steps with whatever framework or library you are using.
Thank you all for reading and for taking time to answer my question.
Related
I've written this bit of jQuery code in Oxygen Builder's JavaScript element to query the job board API and return an array of departments and their jobs. I'm testing to see if the department[0].jobs.length returns 0 then hide the #job-list div, otherwise show it and its associated jobs. The code succeeds in querying the API and returning 0 jobs but the remainder of the ternary operator will not hide the div.
jQuery(document).ready(function($) {
$.getJSON('https://boards-api.greenhouse.io/v1/boards/forwardnetworks/departments', postings => {
$("#div_block-420-61456").html(`
<div id="job-list">${postings.departments[0].jobs.length == 0 ? $("#job-list").hide() : $("#job-list").show()}<h3 class="dept">${postings.departments[0].name}</h3>
${postings.departments[0].jobs.map(item => `<h4 class="job-title">${item.title}</h4>
<p class="job-descrip">${item.location.name}`).join('')}</div> `);
});
});
I generally get a return of [object object]
As I mentioned in the comments, I would add a guard within the .getJSON success handler that will return early if there are no jobs to display.
The resulting function would be:
const departmentIndex = 0;
$(function ($) {
$.getJSON('https://boards-api.greenhouse.io/v1/boards/forwardnetworks/departments', postings => {
if (postings.departments[departmentIndex].jobs.length === 0) { return; }
$("#div_block-420-61456").html(`
<div id="job-list">
<h3 class="dept">${postings.departments[departmentIndex].name}</h3>
${postings.departments[departmentIndex].jobs.map(item => `
<a href="${item.absolute_url}">
<h4 class="job-title">${item.title}</h4>
</a>
<p class="job-descrip">${item.location.name}`
).join('')}
</div>
`);
});
});
Note: I put the index in a variable so that I could easily test with different departments.
Here is an example fiddle.
Pre Info:
In the PHP I declare a global array ($GLOBALS[]), in it I stow the data for the blade.
On the Blade i have a dropdown. If the dropdown changes, i send via AJAX the value to the Controller.
With the new data i calculate the new information, i want to show on the Blade.
But when I try to get the data in HTML like {{$GLOBALS['MyNumber']}} I only get the initialization value.
<?php
.
.
.
$GLOBALS = array(
'MyNumber' => 1,
'companyCode' => 0
);
class MyClass extends Controller
{
public function getData(){
$GLOBALS['companyCode'] = #$_POST['companyCode'];
if ($GLOBALS['companyCode'] == 902){
$GLOBALS['MyNumber'] = 100; //for the example, I set the data fix
}else{
$GLOBALS['MyNumber'] = 20; //for the example, I set the data fix
}
return response()->json(array('AJAX succses' => $response), 200);
}
.
.
.
In HTML i wanna show an dashboard-card with the new data like this:
I have 4 Cards and 7 Tables with data from SQL.
#section('page_content')
<div class="card #if($GLOBALS['MyNumber'] >95) dashboard-card-success #else card-anger-#endif" id="bkfI">
<div class="dashboard-title">
{{ __('bkf_usage') }} {{ today()->year }}
</div>
<div class="dashboard-content">
<div class="dashboard-number" id="bkf_usage">{{$GLOBALS['MyNumber'] }} %</div>
<div class="dashboard-description"></div>
</div>
</div>
But every time i reload the div (so the color can change) the $GLOBALS['MyNumber'] stays on the init of 1.
But the internal use (if $GLOBALS['companyCode']) works fine. How can i solve this issue? Or can you help me to find a work-around?
Maybe pass all the Vars back via ajax to JS and store tem in an JS variable? So i can still access from HTML to them?
Thank You!
So what im trying to do is query a Minecraft server with javascript, and with the response i get back with the api, i want to grab the .playerlist and put the response in this url (https://cravatar.eu/avatar/ {name} /100.png) for each person connected
If someone knows a better way to achieve this, i would very much appreciate your input!
Im also pretty new to javascript, so not fully know what im doing :/
Heres the HTML that i have (i know it may be messy, its also not fully my code)
<div class="card"> <div class="icon"><img src="https://cdn.worldvectorlogo.com/logos/minecraft-1.svg"></div><div class="header">
<div class="image"> <img src="https://res.cloudinary.com/lmn/image/upload/e_sharpen:100/f_auto,fl_lossy,q_auto/v1/gameskinnyc/u/n/t/untitled-a5150.jpg" alt="" /> </div>
<h2>Server Status</h2>
</div>
<div id="rest">Loading...</div>
<img src="https://cravatar.eu/avatar/" $face "/>
</div>
And here is the javascript
//Query api at this address
var url = "https://api.minetools.eu/query/play.aydaacraft.online/25565";
$.getJSON(url, function(r) {
//data is the JSON string
if(r.error){
$('#rest').html('Server Offline.');
return false;
}
var p1 = '';
if(r.Players > 0 ){ p1 = '<br>'+r.Playerlist; }
// Text to display below
$('#rest').html('Total Online: '+r.Players+p1);
// Trying to add playerlist to html url
$('#face').html+p1;
});
Since you've pasted jQuery code, I'll submit my answer in jQuery. However, I do recommend you learn primitive JavaScript and not focus your attention just on jQuery... it's become something of a meme on StackOverflow.
Starting off, you really should be wrapping your code in $(document).ready this'll only run the code when the page has loaded.
$(document).ready(() => {
// The document is ready, let's run some code!
});
Then add your AJAX request as normal inside this bracket.
$(document).ready(() => {
let url = "https://api.minetools.eu/query/play.aydaacraft.online/25565";
$.getJSON(url, response => {
});
});
Okay, whilst writing this, I checked the URL provided by OP and saw that it was timing out so I've grabbed a sample response from the Minetools' documentation.
{
"MaxPlayers": 200,
"Motd": "A Minecraft Server",
"Playerlist": [
"Connor",
"Kamil",
"David"
],
"Players": 3,
"Plugins": [],
"Software": "CraftBukkit on Bukkit 1.8.8-R0.2-SNAPSHOT",
"Version": "1.8.8",
"status": "OK"
}
So in your JSON response, you can see that Playerlist is a array which can contain multiple things in one variable. You can also iterate through an array, which is what we'll be doing to build the image URLs.
We iterate through an array using forEach.
$(document).ready(() => {
let url = "https://api.minetools.eu/query/play.aydaacraft.online/25565";
$.getJSON(url, response => {
response.Playerlist.forEach(playerName => {
console.log(playerName);
});
});
});
//Console:
//Connor
//Kamil
//David
Now that we're iterating through the player list we can start assembling the URLs for these images and adding them to your document's body.
I've cleaned up your HTML, take note of the new div#user-images I've added. This'll be the place where jQuery will add your images from the forEach loop.
<div class="card">
<div class="icon">
<img src="https://cdn.worldvectorlogo.com/logos/minecraft-1.svg">
</div>
<div class="header">
<div class="image">
<img src="https://res.cloudinary.com/lmn/image/upload/e_sharpen:100/f_auto,fl_lossy,q_auto/v1/gameskinnyc/u/n/t/untitled-a5150.jpg" alt="" />
</div>
<h2>Server Status</h2>
</div>
<!-- This div tag will need to hide when there is no error, or say when there is. -->
<div id="rest">Loading...</div>
<!-- The user images will be added inside this div. -->
<div id="user-images"></div>
</div>
Now we have our HTML ready we can start using the jQuery function appendTo to add elements into our div#user-images.
$(document).ready(() => {
let url = "https://api.minetools.eu/query/play.aydaacraft.online/25565";
$.getJSON(url, response => {
response.Playerlist.forEach(playerName => {
$(`<img src="https://cravatar.eu/avatar/${playerName}" />`).appendTo("#user-images");
});
});
});
Your div#user-images should start filling up with the images of players from the Playerlist array.
I noticed you added a simple way of showing whether or not there's an error with the API. We can interact with div#rest to show/hide or change text depending on the success of the response.
$(document).ready(() => {
let url = "https://api.minetools.eu/query/play.aydaacraft.online/25565";
$.getJSON(url, response => {
if(response.error){
$("#rest").html("The server is offline!");
}else{
//There is no error, hide the div#rest
$("#rest").hide();
response.Playerlist.forEach(playerName => {
$(`<img src="https://cravatar.eu/avatar/${playerName}" />`).appendTo("#user-images");
});
}
});
});
And that's it really. I hope this gives you some understanding of arrays, and iterating through them, as well as some DOM functions from jQuery.
I have a small 70x70 box in an HTML player I built where I wish to place in the album artwork to coincide with my now playing information from my shoutcast server. Is there a way, using the artist-song information the shoutcast server provides, that I can search a web service (amazon/last.fm) and have it place the (most likely) album cover there?
Here is the JS code I'm using now:
jQuery(document).ready(function() {
pollstation();
//refresh the data every 30 seconds
setInterval(pollstation, 30000);
});
// Accepts a url and a callback function to run.
function requestCrossDomain( callback ) {
// Take the provided url, and add it to a YQL query. Make sure you encode it!
var yql = 'http://s7.viastreaming.net/scr/yql.php?port='+port+'&username='+user+'&callback=?';
// Request that YSQL string, and run a callback function.
// Pass a defined function to prevent cache-busting.
jQuery.getJSON( yql, cbFunc );
function cbFunc(data) {
// If we have something to work with...
if ( data ) {
// Strip out all script tags, for security reasons. there shouldn't be any, however
data = data[0].results.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '');
data = data.replace(/<html[^>]*>/gi, '');
data = data.replace(/<\/html>/gi, '');
data = data.replace(/<body[^>]*>/gi, '');
data = data.replace(/<\/body>/gi, '');
// If the user passed a callback, and it
// is a function, call it, and send through the data var.
if ( typeof callback === 'function') {
callback(data);
}
}
// Else, Maybe we requested a site that doesn't exist, and nothing returned.
else throw new Error('Nothing returned from getJSON.');
}
}
function pollstation() {
requestCrossDomain(function(stationdata) {
var lines = stationdata.split('|+|');
jQuery('#sname').html(lines[0]);
jQuery('#sgenre').html(lines[1]);
jQuery('#clisteners').html(lines[2]);
jQuery('#bitrate').html(lines[3]);
jQuery('#artist_block').html('' + jQuery.trim(lines[4]) + '');
var prev = lines[5].split('+|+');
jQuery('#np_table').html('');
for (var i = 0; i < 8; i++)
{
if(typeof(prev[i]) != 'undefined')
{
jQuery('#np_table').append('<tr>'+'<td>'+ prev[i] + '</td>'+'</tr>');
jQuery("tr:odd").css("background-color", "#154270");
}
}
jQuery('#mplayers').html(lines[6]);
jQuery('#mobile').html(lines[7]);
jQuery();
} );
}
and here's the HTML:
<div id="col_left">
<div id="now_playing">
<div id="np_ribbon"><span>Now Playing</span></div>
<div id="np_img"><img name="nowplayingimage" src="" width="70" height="70" alt="album cover" style="background-color: #000000" /></div>
<div id="artist_block">
<span class="artist_name"><strong>Artist:</strong> Artist name</span><br />
<span class="song_name"><strong>Song:</strong> "song title"</span><br />
<span class="album_name"><strong>Album:</strong> Album Name</span> <br />
</div>
<div id="player">
<div id="container"><script type="text/javascript" src="http://shoutcast.mixstream.net/js/external/flash/s7.viastreaming.net:8790:0:::999999:::1"></script></div>
</div>
</div><!-- end now playing -->
<div id="recent">
<div class="table_title">Recently Played</div>
<table id="np_table">
</table>
</div><!-- end recent -->
</div><!-- end col_left -->
So naturally, I want the image to appear where the div "np_img" is. Any ideas what code to use and how to implement it. You can probably tell by my code that I'm an amateur so please be clear and gentle. :)
You can use the iTunes search API. It supports JSONP so you can use it directly within your webpage, without worrying about cross-domain.
As #Brad mentioned, iTunes has terms of use. In particular:
(...) provided such Promo Content: (i) is placed only on pages that promote
the content on which the Promo Content is based; (ii) is proximate to
a "Download on iTunes" or "Available on the App Store" badge (as
approved by Apple) that acts as a link directly to pages within iTunes
or the App Store where consumers can purchase the promoted content. (...)
Here's how the code looks like:
function refreshArtwork(artist, track) {
$.ajax({
url: 'http://itunes.apple.com/search',
data: {
term: artist + ' ' + track,
media: 'music'
},
dataType: 'jsonp',
success: function(json) {
if(json.results.length === 0) {
$('img[name="nowplayingimage"]').attr('src', '');
return;
}
// trust the first result blindly...
var artworkURL = json.results[0].artworkUrl100;
$('img[name="nowplayingimage"]').attr('src', artworkURL);
}
});
}
I've found this great example to implement a twitter like "x new tweets" http://blog.hycus.com/2011/03/14/realtime-updates-like-twitter-using-phpmysqljquery/
In this example the .arte jQuery plug-in is used. However I think it can be done just as the same with .ajax and I've coded as:
$.ajax({
url:'async.php? main='+$('.boxOfMainPage:first').attr('id'),
success:function(results)
{
if(results!='')
{
if(results.indexOf('boxOfMainPage')>=0)
$('#tweetEveryone').prepend(results);
else
$('#newTweet').html("<center><a href=''>I found "+results+" new tweets</a></center>").show();
}
}
});
This checks the results and loads the result to tweetEveryone. Async.php simply makes a mysql_query and brings the new results. I've actually done exactly the same with the example however when I click the 'new tweet's like it sometimes causes a postback. In the example I haven't experience it. Can it be because of the difference between .arte and .ajax ?
It's nothing about the differences between arte and ajax (in fact and in a short way, arte is ajax that is called with an interval, trying to do something like "long polling")
So, u have a link without href value, this must "reload" ur page, ie, it will perform a GET request to the actual URL in window.location. A postback performs a POST request, this is really happening?
--- edited ---
If you wanna to do the same effect from twitter, it's simple.. In async.php, instead u write an link element that shows how many tweets has after the old state, make this page write a JSON object with all tweets, then, ur ajax function must get this JSON and convert it into a JS object. With this object, u'll be able to count how many updates u have to show and exactly which are they.
So, ur function could be like this (assuming that "#boxOfMainPage" is ur tweets container):
$.ajax({
url : 'async.php?main='+$('.boxOfMainPage:first').attr('id'),
success : function (tweets) {
window.NEW_TWEETS = tweets;
if ( NEW_TWEETS && NEW_TWEETS.length ) {
$('#newTweet').html("<center><a href='#' onclick='showNewTweets()'>I found "+NEW_TWEETS.length+" new tweets</a></center>").show();
}
}
});
The showNewTweets functions will be:
function showNewTweets() {
if ( window.NEW_TWEETS && NEW_TWEETS.length ) {
$('#newTweet').hide().html("");
for ( tweet in NEW_TWEETS ) {
$("#boxOfMainPage").prepend(buildTweetHTML(tweet));
}
}
}
And buildTweetHTML:
function buildTweetHTML(tweet) {
var $tweetElm = $("<div class='tweet'>");
$tweetElm.append("<h2>"+tweet.user+" said:</h2>");
$tweetElm.append("<p>"+tweet.content+"</p>");
$tweetElm.append("<p class='time'>"+tweet.time+"</p>");
return $tweetElm;
}
Finally, async.php should write JSON object like this:
[
{ user : 'Rafael', content : 'The content from tweet', time : 'X time ago' },
{ user : 'George', content : 'The content from tweet', time : 'Y time ago' }
{ user : 'Jack', content : 'The content from tweet', time : 'H time ago' }
]