Ajax php/mysql Load More Button on Very Active feed - php

I have finally implemented the load more button and load more data on my page using ajax and the below code works wonderful.
However, since it's an activity feed, it's always being updated, so the start and offset is always inaccurate.
E.g.
On Page Load Table consist of
Item 8
Item 7
Item 6
Item 5
Item 4
Item 3
Item 2
Item 1
By time I click on load more button for 5 more post it could have became like this because of new activities
Item 10
Item 9
Item 8
Item 7
Item 6
Item 5
Item 4
Item 3
Item 2
Item 1
Using above logic of offset 5 and limit 5, when there's no update I will get
Item 3, Item 2, Item 1
But because there's update, I ended up with duplicates display of Item 5 and Item 4 since the offset is now wrong new to new data in the mysql table.
Item Item 5(already displayed), Item 4(already Displayed), item 3, item 2 item 1
Question: I think this could be a common problem for activity feed. Any help is appreciated
$(document).ready(function() {
var a = 5; /*5 items already loaded on page load, so my start count will be 5*/
$("#showMoreActivity").on("click", function(b) {
b.preventDefault(), $.ajax({
url: "/activty/",
type: "POST",
dataType: "JSON",
data: {
start: a
},
success: function(data) {
if (data.length == 0) {
$("#showMoreActivity").addClass("disabled");
$("#showMoreActivity").html("End of Activity List");
} else {
a += 5;
$("#activity-feed").append(data);
}
}
});
});
});
on PHP end I have something like the following (simplified/modified)
public function getAgentActivity($start = 0, $limit = 5) {
$query = $this->db->prepare('SELECT * from activity WHERE ORDER BY created DESC LIMIT :limit OFFSET :offset');
if ($result = $query->execute(array(
':limit' => $limit,
':offset' => $start
))) {
//query success, fetch data and do something
}
}

For this you could use cursor based pagination. For e.g., instead of using limit and offset, use creation time of the record to paginate.
On each request send the last record creation time and send response accordingly. For this approach to work, your record must be sorted on creation time and creation time field should be unique. You could use auto-increment primary key as well.
For more detail See here

Below code may help:
success: function(data) {
a += data.length;
if (data.length > 0) {
$("#activity-feed").append(data);
}
if (data.length < 5) {
$("#showMoreActivity").addClass("disabled");
$("#showMoreActivity").html("End of Activity List");
}
}

Related

Ajax request takes too long to complete when working with large database

I am working on a website written in Yii framework (version 1.1.14) that allows uploading and displaying news. The admin of the site can select three news to promote to homepage and specify the order in which they are displayed. I am using Mysql database. The news table has two fields: isChecked (0 or 1) and homepagePos (integer) in addition to the other fields. The isChecked field determines whether the news is selected for displaying in homepage and the homepagePos field determines the order in which the news are displayed. I have used jquery's sortable plugin to sort the news. When the user selects which news to display and clicks save button, the news ids are sent to php via ajax.
The javascript portion to send the values to news controller is as follows:
$(document).on('click', '#saveToHomepage', function()
{
var url = ajaxRequestSendUrl; //ajaxRequestSendUrl contains url to news controller's promote to homepage method.
$.ajax({
method: "GET",
url: url,
data: {
contentIds: contentIds, //contentIds contains an array of news Ids in certain order
},
success: function() {
// Show success message
},
error: function() {
alert('Some error occured. Please reload the page and try again.');
}
});
});
Here's the promote to homepage method in news controller:
public function actionHomepage()
{
$allNews = News::model()->findAll();
$value = $_GET['contentIds'];
foreach ($allNews as $news) {
if($news->id == $value[0] ||$news->id == $value[1] ||$news->id == $value[2])
{
$news->isChecked = 1;
$news->homepagePos = array_search($news->id, $value); //Assign index of the array as the position
$news->save();
}
else
{
$news->isChecked = 0;
$news->homepagePos = -1;
$news->save();
}
}
}
My problem is that the news table I have has over 2k data. So the ajax call takes really long time (over a minute) to complete. Is there any way I can optimize the code or is there other way I can approach this to reduce the time taken to complete this operation?
Thanks in advance
Three queries: One first to set the whole table to not checked status, and the rest to set the checked status only in the row each selected id
public function actionHomepage()
{
$values = $_GET['contentIds'];
$sql = "UPDATE news SET idChecked=0,homepagePos = -1";
Yii::app()->db
->createCommand($sql)
->execute();
for($ii = 0; $ii < 3; $ii++) {
$sql = "UPDATE news SET idChecked = 1,homepagePos = ':homepagePos' WHERE id=:id";
Yii::app()->db
->createCommand($sql)
->bindValues(array(':homepagePos' => array_search($ii, $values), ':id' => $values[$ii]))
->execute();
}
}
i am not sure, but why don't you get the filtered records from the database itself by sending the id's of selected news. I don't know your backend language. Seems, you are getting all the records and applying filtering. which consumes time. Instead get the filtered news from the database.
Hope this helps!
Its taking a long time because you're fetching all the records and then updating those three id. You could try this:
$criteria = new CDbCriteria;
$criteria->addInCondition( "id" , $value ) ; // $value = array ( 1, 2, 3 );
News::model()->updateAll(array('isChecked'=>'1','homepagePos'=>'val2'), $criteria);
You could do an update before hand to reset all the homepagePos and then update only the ids you need.

Lazy Load - Browser Back Button Issue - Hidden Field Does Not Change

I am developing a laravel website, in which there is a product list page (List page) where I show all the products with ajax lazy load. I also have a Product Details page which show details of the individual product. I created a hidden field which saves scroll count I call this field as Page_No field. When I go to list page, Initially I show 6 products from database and also set Page_No to "0", after I scroll down, another 6 products gets added to list by ajax and by determining Page_No field and that time I increment the Page_No field by one. And so on.
When I scroll down for some time (say three times) that means I have 24 products on my list page, and my Page_No count is "3". After that I click on any product and go to product page to see details. After that when I click browser's back button and go to list page, I see first 6 products are there and then if I scroll down I get more products, but it skips some of the products (for ex it shows me 45th product and so on..so what about those product from 24-45?).
My understanding:
When I get back to list page by clicking browser's back button Page_NO field is set to 3, if I scroll down it gets incremented by one i.e. "4", So my ajax query executes and get result from that point skipping some products.
Things I tried:
I understood that I have to control Page_No field in order to get my list work properly after pressing back button. when I go to product details page by clicking any one product that time I creates a session which will have value "0". After that I created SetInterval Ajax function which will continuously check that session and update the Page_No field. But this thing also does not work properly (Only works for first instance).
lazyload.js
onscroll = doLazyLoad;
//global check flag for do lazy load
var ll_check_flag = true;
function doLazyLoad()
{
var productsDiv = document.getElementById('ProductsView');
if(window.scrollY > (productsDiv.scrollHeight - 200))
{
if(ll_check_flag)
{
ll_check_flag = false;
getMoreProducts();
}
}
}
function getMoreProducts()
{
var product_filters = [];
$('input.checkfilter:checkbox:checked').each(function ()
{
product_filters.push($(this).val());
});
var pageno = parseInt($('#pageNo').val())+1;
$('#pageNo').val(pageno);
var range_filter = $('#range').val();
productsLazyLoadAjax(product_filters,range_filter,'',pageno);
}
function productsLazyLoadAjax(product_filters,range_filter,search_term,pageno)
{
weburl = $('#MasterURL').val();
$.ajax({
url: weburl + '/index/get/products',
type: "POST",
data: {pf:product_filters,rf:range_filter,st:search_term,page:pageno},
beforeSend:function()
{
res.container.append(res.loader);
},
success: function(data)
{
if(data != 0)
{
res.container.find(res.loader).remove();
$('#ProductsView').append(data);
ll_check_flag = true;
}
},
complete: function() {
}
});
}
Product Detail Page (After click on list page products we land on product detail page)
jQuery(document).ready(function()
{
$('#pageNo').val(0);
}

extjs 4 : store callback issue

I'm working on a Extjs 4 application and I'm at a point where I need to create view components dynamically.
I have a store that's loaded with new items when I select a value from a combobox. And I want to create a new view component for each item the new store has.
I'm using Extjs 4 with the MVC architecture.
This is the function that creates a new combobox that's fired when I choose an Item from another combo :
function createComboBox(label) {
var combo = new Ext.form.ComboBox({
displayField: 'combo',
typeAhead: true,
mode: 'local',
forceSelection: true,
triggerAction: 'all',
emptyText: 'Select item...',
selectOnFocus: true,
fieldLabel: label
});
return combo;
}
This is the code inside my "select combobox" handler event :
onSelectedValue: function (combo) {
var selected = combo.getValue();
var guiDataStore = this.getGuiDataStore();
guiDataStore.getProxy().url = 'gui_comp_items.php?id_metric=' + selected;
guiDataStore.load({
params: {
id_metric: selected
},
scope: this,
callback: function () {
var paramsRef = this.getParams();//this is the view where I'd like to create the combobox
var total = guiDataStore.getTotalCount();
if (total > 0) {
guiDataStore.each(function (model) {
if (model.get('type_guicomp') == 'combobox') {
paramsRef.down('fieldset[id=filterfieldset]').add(createComboBox(model.get('name_filter')));
paramsRef.down('fieldset[id=filterfieldset]').doLayout();
}
})
}
}
})
}
So my problem is, the first time I choose an item from the existing combobox and total = 0 , no combobox is created and everything is fine, then when I choose a value that returns total = 2, 2 new comboboxes are created, that's perfect. BUT, when right after that, I choose again a value with total = 0, the store is not updated and I STILL get 2 new comboboxes.
Is there a problem with my callback? Please any help would be much appreciated.
Once guiDataStore has 2 records in it, why would it be empty next time?
As in, do you empty the store between the various callback calls?

Dynamically change dropdowns via SQL and calculate sums?

I've got a row in a table with 3 fields laid out as so:
Job Pay Grade Cost
<Select> <Select> <Calculation>
I've got an SQL table with the information above in it, for example:
Job Pay Grade Cost
Techie 1 100
Techie 2 200
Engi 2 300
Engi 3 400
Engi 4 500
What I need to do is to be able to select a Job from the dropdown and then the Pay Grade select box will change depending on what matches that job in the SQL database. It will then show the cost which relates to the to selected.
How can I go about this as I am a little stuck
First create ajax request when a job title is selected. WIthin the success callback of the request will generate html for the options for pay grade select from JSON response from server
jQuery
$('select.jobTitle').change(function(){
var $titleSelect=$(this);
$.getJSON('processJobGrades.php', { jobTitle : $(this).val() }, function(response){
var gradesOptionsHtml='';
/* create options html from json response */
$.each( response, function(i, item){
gradesOptionsHtml+='<option value="'+item.grade+' data-cost="'+item.cost+'">'+item.grade+'</option>';
});
$titleSelect.parent().find('select.jobGrade').html(gradesOptionsHtml);
});
});
IN processJobGrades.php receive $_GET['jobTitle'] . Do DB lookup and create json to send back.
PHP
$outputArray=array();
/*in loop over DB data:*/
$outputArray[]= array( 'grade'=>$row['grade'], 'cost'=>$row['cost']);
/*Output final array as JSON*/
echo json_encode( $outputArray);
jQuery change handler for paygrade select to get cost
$('select.jobGrade').change(function(){
var cost=$(this).find(':selected').data('cost')
$(this).parent().find('input.jobCost').val( cost);
})
You need to post the select job via $.ajax and then in success function populate the dropdown list like this:
function selectHandler (event, ui)
{
var id = event.target.id;
$.ajax({
type: "POST",
url: "/php/get_quantity_type.php",
dataType:"json",
data: { ingridient : ui.item.value},
success: function(data){$("#"+id+"_t").empty(); $.each(data,function(index,value) {$("#"+id+"_t").append('<option value="' + value + '">' + value + '</option>');})}
});
}
This example takes a name of a material from select list posts it via $ajax() to a php script and writes it down to a new dropdown list which id is based on id that triggered the event. If you need the php code just ask:)
You bind your job list to the event handler above:
("#job_list").bind("select",selectHandler);
This code posts data to "/php/get_quantity_type.php", and passess the result to function declared in success attribute;

Reloading new content while scrolling

Im using the following jQuery, which sets the id of the last item in a UL to 'last_item'.
function oldest() {
j('ul li:last-child').addClass( 'last_item' );
oldestName = j('.last_item').attr('id');
alert('last ID is:'+ oldestName +'.');
}
j(window).scroll(function(){
if (j(window).scrollTop() == j(document).height() - j(window).height()){
j.ajax({
url: "older.php?oldest="+oldestName+"",
cache: false,
success: function(html){
j(".older").html(html);
oldest();
}
})
}
});
oldest(); //Call function on page load
$('.button2').click(function() {
j('.older ul > *').clone().hide().appendTo('.tweets ul').slideDown();
oldest();
});
While scrolling older.php is called which contains:
$oldest = $_GET['oldest'];
$getolder = mysql_query(" SELECT * FROM table where date < $oldest ORDER BY date DESC LIMIT 20");
However what currently happens is the records from the database do get pulled back, stored in a hidden DIV. I then use the jQuery to add these records to the end of the list.
However the next time i scroll to the bottom of the page and press the button, the same records are added to the lists.
Can any help me to find out why this is?
Essentially it seems $oldest isnt being updated with the new last UL item date.
You'll need to remove the original "last_item" class from list item before you add it to the new last item...
j('ul li').removeClass( 'last_item' );

Categories