Currently I am handling likes like so:
class LikeController extends Controller
{
public function like(Post $post)
{
$attributes = [
['user_id', '=', auth()->user()->id],
['post_id', '=', $post->id]
];
$like = Like::where($attributes);
if($like->exists()) {
$like->delete();
} else {
Like::create(['user_id' => auth()->user()->id, 'post_id' => $post->id]);
}
return redirect()->to(url()->previous());
}
}
and I have a route:
Route::post('like/{post}', [LikeController::class, 'like']);
What I don't like is that each time a like is clicked a new request is sent and the page refreshes to the top, so I added this javascript to scroll back to the previous position when a like is clicked:
$(window).on("scroll", function(e) {
$.cookie("tempScrollTop", $(window).scrollTop());
});
$(".like-form").each(function() {
$(this).submit(function(e) {
$.cookie("action", 'like', {path: '/'});
})
});
$(window).on("load", function (e) {
if ($.cookie("tempScrollTop") && $.cookie('action') === 'like') {
$(window).scrollTop($.cookie("tempScrollTop"));
$.cookie("action", '', {path: '/'});
}
})
This all works great, but I still don't like seeing the page refresh when hitting a like button. Is there a way to make the post request async and/or let it run in the background such that when hitting the like button the page doesn't refresh but the like does get sent to the server? What would be otherwise the correct method to achieve this result?
Thank you any help will be appreciated!
You can do Ajax request via jQuery, or you can use Livewire.
Here is a good blog how to make Livewire Like button: https://rappasoft.com/blog/building-a-like-button-component-in-laravel-livewire
You should'nt use a redirection in your api, but instead implement an ajax request that just return a success message.
public function like(Post $post)
{
// ...
return response()->json("Post successfuly liked"); // Or you could return the Post, or Like object if you want.
}
On the front end side, when calling that route you could then handle the response, for example incrementing the counter :
$(".like-form").each(function() {
$(this).submit(function(event) {
// call the api here, and then handle the response through a callback
// ex:
fetch(`${API_URL}/post/like`).then(response =>
// do something, like querying the dom element for its value and incrementing it.
)
});
});
If AJAX request reloads the page is the issue then simple apply event.preventDefault() will fix this
$(".like-form").each(function() {
$(this).submit(function(event) {
event.preventDefault();
}
}
FYI - Check this reference Laravel 9 Form Submit Using Ajax Example Tutorial
Related
I have a page "GetData.cshtml" where I need to fill up some textbox data and send to the controller method. I am using jquery to send data to the controller method.
//GetData.cshtml
$(document).ready(function () {
$('#btn_sbmt_recommendation').click(function () {
$.post("/RegistrationModels/Registration_card",
{
firm_id: $('#id_reg_id').val(),
amount: $('#id_amount').val()
});
console.log("job done....");
});
})
While debugging the app, the controller method is called successfully.
public ActionResult Registration_card(string reg_id, string amount)
{
try
{
// code block
return View(updated_firm);
}
catch (Exception ex) { throw; }
}
As my requirement, I want to display the data in Registration_card.cshtml page, but it is not redirecting to the page.
The console message console.log("job done...."); in the jquery block is showing. It means the page is again back to GetData.cshtml. Is it due to using jquery to call controller method?
How can I go to page Registration_card.cshtml after calling the controller method Registration_card via jquery.
I also tried with the below code, but it is also not working.
public ActionResult Registration_card(string firm_id, string ward_calani_num, string amount)
{
try
{
// code block
return RedirectToAction("Registration_card", updated_firm);
}
catch (Exception ex) { throw; }
}
According to your question, it seems you want to redirect/go to the Registration_card.cshtm from GetData.cshtml by taking some data from the GetData.cshtml to the Registration_card ActionMethod which will actually show the Registration_card.cshtm.
If the above scenario is true, then you do not need to Ajax Post request as your ActionMethod is HttpGet as you do not specify it is as HttpPost. So the workaround can be something like the below code.
$(document).ready(function () {
$('#btn_sbmt_recommendation').click(function () {
var id_reg_id=$('#id_reg_id').val();
var id_amount=$('#id_amount').val();
location.href=`/RegistrationModels/Registration_card?reg_id=${id_reg_id}&amount=${id_amount}`;
//$.post("/RegistrationModels/Registration_card",
// {
// firm_id: $('#id_reg_id').val(),
// amount: $('#id_amount').val()
// });
//console.log("job done....");
});
})
If want to redirect page you should use javascript or jquery because when you use jquery then controller not working for redirect.
I've implemented a simple AJAX call that is bound to a button. On click, the call takes input from an and forwards the value to a FLASK server using getJSON. Using the supplied value (a URL), a request is sent to a website and the html of a website is sent back.
The issue is the AJAX call seems to run multiple times, incrementally depending on how many times it has been clicked.
example;
(click)
1
(click)
2
1
(click)
3
2
1
Because I am sending requests from a FLASK server to another website, it effectively looks like I'm trying to DDOS the server. Any idea how to fix this?
My AJAX code;
var requestNumber = 1; //done for testing purposes
//RUNS PROXY SCRIPT
$("#btnProxy").bind("click", function() . //#btnProxy is the button
{
$.getJSON("/background_process", //background_process is my FLASK route
{txtAddress: $('input[name="Address"]').val(), //Address is the input box
},
console.log(++requestNumber), //increment on function call
function(data)
{$("#web_iframe").attr('srcdoc', data.result); //the FLASK route retrieves the html of a webpage and returns it in an iframe srcdoc.
});
return false;
});
My FLASK code (Though it probably isn't the cause)
#app.route('/background_process')
def background_process():
address = None
try:
address = request.args.get("txtAddress")
resp = requests.get(address)
return jsonify(result=resp.text)
except Exception, e:
return(str(e))
Image of my tested output (I've suppressed the FLASK script)
https://snag.gy/bikCZj.jpg
One of the easiest things to do would be to disable the button after the first click and only enable it after the AJAX call is complete:
var btnProxy = $("#btnProxy");
//RUNS PROXY SCRIPT
btnProxy.bind("click", function () //#btnProxy is the button
{
btnProxy.attr('disabled', 'disabled');//disable the button before the request
$.getJSON("/background_process", //background_process is my FLASK route
{
txtAddress: $('input[name="Address"]').val(), //Address is the input box
},
function (data) {
$("#web_iframe").attr('srcdoc', data.result); //the FLASK route retrieves the html of a webpage and returns it in an iframe srcdoc.
btnProxy.attr('disabled', null);//enable button on success
});
return false;
});
You can try with preventDefault() and see if it fits your needs.
$("#btnProxy").bind("click", function(e) {
e.preventDefault();
$.getJSON("/background_process",
{txtAddress: $('input[name="Address"]').val(),
},
console.log(++requestNumber),
function(data)
{$("#web_iframe").attr('srcdoc', data.result);
});
return false;
});
Probably you are binding the click event multiple times.
$("#btnProxy").bind("click", function() { ... } );
Possible solutions alternatives:
a) Bind the click event only on document load:
$(function() {
$("#btnProxy").bind("click", function() { ... } );
});
b) Use setTimeout and clearTimeout to filter multiple calls:
var to=null;
$("#btnProxy").bind("click", function() {
if(to) clearTimeout(to);
to=setTimeout(function() { ... },500);
});
c) Clear other bindings before set your calls:
$("#btnProxy").off("click");
$("#btnProxy").bind("click", function() { ... } );
$(".getDetails").click(function() {
// some stuff like fetching response from server
})
when user clicks getDetails button on UI multiple times within fraction of second , jquery generates two calls for click function and my logic fails.
I think solution to this will be to disable the button on first click itself(so that use can't click multiple times). Once i get the response or just before returning
from click method i make it enable. Is there any better solution ?
If no, how can i make button disable as soon as user click button first time. I think it needs to be done before calling click method or some where in html element ?
Java provides synchronized keyword so that only one thread enters at time inside method , i am not sure is similar thing exist in javascript or not ?
Assuming the click handler executes an AJAX request you can set the button as disabled before making the request, then enable it again once the request completes. Try this:
$(".getDetails").click(function(){}
var $btn = $(this).prop('disabled', true);
$.ajax({
url: '/foo'
success: function() {
console.log('It worked!');
},
error: function() {
console.log('It failed!');
},
complete: function() {
$btn.prop('disabled', false);
}
});
});
you can try unbinding click event and after ajax call again bind click to that class
$(".getDetails").click(function(){}
$(".getDetails").unbind('click');
// some stuff like fetching response from server
)
You can use simple flag to prevent firing your logic multiple times:
var flag = true
$(".getDetails").click(function() {
if (flag) {
flag = false;
//your logic...
//when your code ends (in after-AJAX callback for example)
flag = true;
}
});
$(".getDetails").click(function(e){
var $target = $(e.currentTarget);
// assuming the click listener is on the button
$target.prop('disabled',true);
// request, stuff...and when done:
$target.prop('disabled',false);
})
try Prevent Default and return false to avoid any other event propagation
This is solution is like semaphore or monitor
var progress = false;
$(".getDetails").on('click', function(e) {
if(!progress){
progress = true;
// some stuff like fetching response from server
//also after sucessfull fetch make true to false again
}else{
console.log('something in progress');
}
e.preventDefault();
return false;
})
This should make sure that your button will not fire the async request twice, until you have a response.
function doAjaxReq() {
/*
Add your ajax operation here
as a return value of doAjaxReq
like so:
return $.ajax({
url: '/foo',
type: 'POST',
data: data
})
Since i can't use ajax here let's smilulate
it useing a promise.
*/
promise = new Promise(function(res, rej) {
setTimeout(function(){
res({foo: "bar"});
}, 1000)
})
return promise;
}
/*
Inside here you add the click handlder
only once use `elem.one('click'...`
*/
function addClickHandler(elem) {
elem.one('click', function() {
// do your ajax request and when its
// done run `addClickHanlder` again
// i'm using `.then` because of the promise,
// you should be using `.done`.
doAjaxReq().then(function(data) {
console.log(data);
addClickHandler(elem);
});
})
}
addClickHandler($(".getDetails"));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="getDetails">Get Details</button>
I am using ajaxComplete to run some functions after dynamic content is loaded to the DOM. I have two separate functions inside ajaxComplete which uses getJSON.
Running any of the functions once works fine
Running any of them a second time causes a loop cause they are using getJSON.
How do I get around this?
I'm attaching a small part of the code. If the user has voted, clicking the comments button will cause the comments box to open and close immediately.
$(document).ajaxComplete(function() {
// Lets user votes on a match
$('.btn-vote').click(function() {
......
$.getJSON(path + 'includes/ajax/update_votes.php', { id: gameID, vote: btnID }, function(data) {
......
});
});
// Connects a match with a disqus thread
$('.btn-comment').click(function() {
var parent = $(this).parents('.main-table-drop'), comments = parent.next(".main-table-comment");
if (comments.is(':hidden')) {
comments.fadeIn();
} else {
comments.fadeOut();
}
});
});
Solved the problem by checking the DOM loading ajax request URL
$(document).ajaxComplete(event,xhr,settings) {
var url = settings.url, checkAjax = 'list_matches';
if (url.indexOf(checkAjax) >= 0) { ... }
}
I am trying to dynamically get and set a pageTitle for my sample Meteor app, ideally I'd like to use jQuery, but any other solution would be good too.
I am trying to do it the rough way by setting a session when a certain div class exists on the page, but it's not working for some reason.
Basically, I have this:
Template.layout.helpers({
pageTitle: function() { return Session.get('pageTitle'); }
});
And I want to do something like
if ($('section').hasClass('single')) {
Session.set('pageTitle', 'Post Page');
}
Any idea ho to make this work? thanks!
You need to call it in a controller or the templates rendered section like this:
Template.layout.helpers({
pageTitle: function() {
return Session.get('pageTitle');
}
});
// set when a page template renders
Template.posts.rendered = function() {
setPageTitle("Blog Posts");
};
// or with Iron Router on *every* route using some sort of variable
Router.onBeforeAction(function() {
var someDynamicValue = "FOO";
setPageTitle(someDynamicValue);
});
// or in an IronRouter Controller
PostsController = RouteController.extend({
onBeforeAction: function() {
setPageTitle("Blog Posts");
}
});
// helper function to set page title. puts a prefix in front of page, you
// could opt set a boolean to override that
function setPageTitle(titleName) {
var prefix = "My Site - ";
if ($('section').hasClass('single')) {
Session.set('pageTitle', prefix + titleName);
}
}
As #Kuba Wyrobek pointed out, I needed to use the Template.layout.rendered function.
Here's a snippet that works
Template.postsList.rendered = function(){
Session.set('pageTitle', 'Listing Page')
};