I'm trying to implement a functionality where user can like and unlike product using javascript I followed this Laravel - Favourite / Un-Favourite button but it doesn't work for me, the button can't be clicked, any idea on how I can fix this?
Routes
Route::get('product/like/{id}', ['as' => 'product.like', 'uses' => 'LikeController#likeProduct']);
Route::get('product/{product}/unlike','LikeController#destroy')->name('product.unlike');
Javascript
<script>
function addToFavourites(productid, userid) {
var user_id = userid;
var product_id = productid;
$.ajax({
type: 'post',
url: 'product/like/{id}',
data: {
'user_id': user_id,
'product_id': product_id,
},
success: function () {
// hide add button
$('#addfavourites' + product_id).hide();
// show delete button
$('#deletefavourite' + product_id).show();
},
error: function (XMLHttpRequest) {
// handle error
}
});
}
Blade file
#if($product->isLiked)
<div id="addfavourites{{$product->id}}" onClick="addToFavourites({{$product->id}}, {{ Auth::user()->id }})"> unlike </div>
#else
<div id="deletefavourite{{$product->id}}" onClick="deleteFromFavourites({{$product->id}}, {{ Auth::user()->id }})" > like </div>
#endif
Make your routes accept a post method because you're posting through Ajax
Route::post('product/like/{id}', ['as' => 'product.like', 'uses' => 'LikeController#likeProduct']);
Route::post('product/{product}/unlike', 'LikeController#destroy')->name('product.unlike');
And use ES6 template string and add a CSRF token header to the Ajax request
function addToFavourites(productid, userid) {
// Redundant
// var user_id = userid;
// var product_id = productid;
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
$.ajax({
method: 'post',
url: `/product/like/${productid}`, // Use ES6 template and point to the url from root /
data: {
'user_id': userid,
'product_id': productid,
},
success: function () {
// hide add button
$('#addfavourites' + productid).hide();
// show delete button
$('#deletefavourite' + productid).show();
},
error: function (XMLHttpRequest) {
// handle error
}
});
}
Hope this helps
Related
I have an Sdk to call which returns an applicationID that I need to store in my django application so that if the user is approved, then he can move to the next page. I am trying to save in JS sessionStorage than pass the data to another html page where I'm using ajax to pass it to my django views. The problem is that I need to have this data unique for the user that is logged in.
How can I link this data to the current user logged in and so on?
const widget = window.getidWebSdk.init({
apiUrl: '',
sdkKey: '',
containerId: "targetContainer",
flowName: '',
onComplete: (data) => {
console.log(JSON.stringify(data))
let appId = data;
console.log(data)
sessionStorage.setItem("appId", JSON.stringify(appId))
window.location.href = "/kycsubmit";
return;
},
onFail: ({ code, message}) => { console.log("something went wrong: " + message )},
});
second html page:
let application = JSON.parse(sessionStorage.getItem("appId"));
let kycStatus = application.applicationId
$.ajax({
type: "GET",
dataType: "json",
url: `api/application/${kycStatus}`,
headers: {
'Content-Type': 'application/json',
'X-API-Key': '',
},
success: function(data) {
document.getElementById("test").innerHTML = data.overallResult.status
document.getElementById("test-1").innerHTML = application.applicationId
console.log(data.overallResult.status)
}
})
I have a function for adding likes on the page
blade.php
<a href="/article/{{ $article->id }}?type=heart" class="comments-sub-header__item like-button">
<div class="comments-sub-header__item-icon-count">
{{ $article->like_heart }}
</div>
<a href="/article/{{ $article->id }}?type=finger" class="comments-sub-header__item like-button">
<div class="comments-sub-header__item-icon-count">
{{ $article->like_finger }}
</div>
js
$(function() {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content'),
},
});
$('.like-button').on('click', function(event) {
event.preventDefault();
let href = $(this).attr('href');
$.ajax({
url: href,
type: 'POST',
success: function() {
window.location.reload();
},
});
});
});
But when I click on the like to update the data, I reload the page using window.location.reload();
Can this somehow be done without reloading the page?
This is how adding likes is implemented, they are added to cookies and stored for 24 hours
web routes
Route::post('article/{id}', 'App\Http\Controllers\ArticleController#postLike');
Article controller
public function postLike($id, Request $request) {
$article = Article::find($id);
if(!$article){
return abort(404);
}
$type = $request->input('type');
if ($article->hasLikedToday($type)) {
return response()
->json([
'message' => 'You have already liked the Article '.$article->id.' with '.$type.'.',
]);
}
$cookie = $article->setLikeCookie($type);
$article->increment("like_{$type}");
return response()
->json([
'message' => 'Liked the Article '.$article->id.' with '.$type.'.',
'cookie_json' => $cookie->getValue(),
])
->withCookie($cookie);
}
Article model
public function hasLikedToday(string $type)
{
$articleLikesJson = Cookie::get('article_likes', '{}');
$articleLikes = json_decode($articleLikesJson, true);
if (!array_key_exists($this->id, $articleLikes)) {
return false;
}
if (!array_key_exists($type, $articleLikes[$this->id])) {
return false;
}
$likeDatetime = Carbon::createFromFormat('Y-m-d H:i:s', $articleLikes[$this->id][$type]);
return ! $likeDatetime->addDay()->lt(now());
}
public function setLikeCookie(string $type)
{
$articleLikesJson = Cookie::get('article_likes', '[]');
$articleLikes = json_decode($articleLikesJson, true);
$articleLikes[$this->id][$type] = now()->format('Y-m-d H:i:s');
$articleLikesJson = json_encode($articleLikes);
return cookie()->forever('article_likes', $articleLikesJson);
}
Assuming those DIVs hold the number of hearts, if the response of the target page is the new number of hearts then:
success: function(data) {
targetElement.find(".comments-sub-header__item-icon-count").html(data)
}
elsewhere if you want to add +1 to current number regardless of server response:
success: function() {
var current= parseInt(targetElement.find(".comments-sub-header__item-icon-count").html());
targetElement.find(".comments-sub-header__item-icon-count").html(current+1)
}
Footnote: as the ajax request is nested inside the click function, the targetElement in my codes is the clicked element. You may get it in defferent ways e.g.
$('.like-button').on('click', function(event) {
var targetElement=$(this);
....
}
$(function() {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content'),
},
});
$('.like-button').on('click', function(event) {
event.preventDefault();
let href = $(this).attr('href');
$.ajax({
url: href,
type: 'POST',
success: function(response) {
$(this).parent(".comments-sub-header__item-icon-count").html(
parseInt($(this).parent(".comments-sub-header__item-icon-count").html()) + 1
)
// or return like or heart count from server
$(this).parent(".comments-sub-header__item-icon-count").html(response)
},
});
});
});
This should work for you
$(function () {
$.ajaxSetup({
headers: {
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"),
},
});
$(".like-button").on("click", function (event) {
event.preventDefault();
const likeBtn = $(this);
$.ajax({
url: likeBtn.attr("href"),
type: "POST",
success: function () {
let currentCount = likeBtn.next().text();
likeBtn.next().text(parseInt(currentCount) + 1);
},
});
});
});
You can simply add the new count to the response from your controller.
return response()
->json([
'message' => 'Liked the Article '.$article->id.' with '.$type.'.',
'cookie_json' => $cookie->getValue(),
'new_count' => $article->{"like_{$type}"},
])
->withCookie($cookie);
Now you can use the updated count as new_count from the database.
$.ajax({
url: href,
type: 'POST',
success: function (response) {
$(this).next().text(response.new_count)
},
});
Goal:
A user will have a list of games in a table with text boxes for each team's score. I want the user to be able to change the score of a single game, click Save (Model function updates the record), and continue saving more games while never leaving the page.
How:
After a Laravel Blade view has been rendered, I want to execute a Model function from a Javascript function on-button-click, but stay on the same page.
admin.blade.php (Javascript section in Head tag)
/* Save game from inline list on Admin page */
function inlineSaveAdmin(gameId) {
var homeScoreTxt = document.getElementById("homeScoreTxtBox");
var homeScore = homeScoreTxt.value;
var awayScoreTxt = document.getElementById("awayScoreTxtBox");
var awayScore = awayScoreTxt.value;
{{ App\Models\Game::inlineSave(gameId, homeScore, awayScore) }}
}
admin.blade.php (body of view)
<button type="button" onclick="inlineSaveAdmin({{ $game->id }});" class="btn btn-outline-success">Save</button>
So far, the Model function only executes when the page loads, not when I click the button. That is the main problem I wish to solve. Thanks for any help!
(and yes, I believe that I will need to create identical Javascript functions for each gameId that exists to be able to reference the correct homeScoreTxtBox{{ game->id }} since I don't think I could otherwise dynamically pull the text box IDs based on the Javascript function's input parameter)
1.make an ajax function on that blade file
2.call that ajax on click pass the id and updated data
3.define a route for that ajax function in web.php and
4.make a controller function on that route.
Code:
$(document).ready(function() {
$("#button").on('click', function() {
**//get id and score**
var homeScoreTxt = document.getElementById("homeScoreTxtBox");
var homeScore = homeScoreTxt.value;
var awayScoreTxt = document.getElementById("awayScoreTxtBox");
var awayScore = awayScoreTxt.value;
var game_id = gameId;
$.ajax({
type: 'POST',
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
url: '{{ route('update') }}',
//all the data you need to pass to controller function
data: {
'id': gameId,
'homescore': homeScore,
'awayscore' : awayScore
},
// dataType: 'json',
success: function(data) {
//data returned from php
// update the values
if (data) {
homeScoreTxt.value=data.homeScore,
awayScoreTxt.value=data.homeScore
}
},
fail: function() {
alert('NO');
}
});
});
});
web.php
Route::post('update', 'UpdateController#update')->name('update');
Update the values in the controller function by simple model queries.
Send updated data like this:
$response = [
'homeScore' => $homeScore,
'awayScore' => $awayScore
];
return response()->json($response);
I have followed Daniyal Ishaq's answer, and I think I'm getting closer, but I'm getting an error from the Ajax call.
Error:
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
(jquery-3.5.1.js:10099) xhr.send( options.hasContent && options.data || null );
Per Google debugger, it appears to be after/inside this call:
(jquery-3.5.1.js:9682) transport.send( requestHeaders, done );
I did some debugging, and a "status" variable is getting set to 500. Then, "isSuccess" is set to False when it gets to this line:
(jquery-3.5.1.js:9723) isSuccess = status >= 200 && status < 300 || status === 304;
That line that sets isSuccess is inside the following function, but I cannot seem to find where it's getting called from to trace where status is getting set exactly.
(jquery-3.5.1.js:9696) function done( status, nativeStatusText, responses, headers ) {
The last line I can find before the error appears is 5233:
(jquery-3.5.1.js:5233) jQuery.event.dispatch.apply( elem, arguments ) : undefined;
Shortly before that line, it is here, where event.rnamespace = undefined, and handleObj.namespace = "" (I don't know if this is relevant):
(jquery-3.5.1.js:5422) if ( !event.rnamespace || handleObj.namespace === false ||
Shortly after that, "ret" is still undefined after this line: (again, I don't know what this does, but it seems important?)
ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
handleObj.handler ).apply( matched.elem, args );
Then on 5446, it returns event.result, which is undefined.
return event.result;
That is where my debugging skills hit a dead end with jQuery. So now I ask for more help.
Ajax function in blade:
$(document).ready(function() {
#foreach($games as $game)
$("#SaveBtn{{ $game->id }}").on('click', function() {
var gameId = "{{ $game->id }}";
var saveBtn = document.getElementById("SaveBtn{{ $game->id }}");
var homeScoreTxt = document.getElementById("homeScoreTxtBox{{ $game->id }}");
var homeScore = homeScoreTxt.value;
var awayScoreTxt = document.getElementById("awayScoreTxtBox{{ $game->id }}");
var awayScore = awayScoreTxt.value;
$.ajax({
url: "{{ route('inlineSave') }}",
type: "POST",
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
//all the data you need to pass to controller function
data: {
'gameId' : {{ $game-> id }},
'homeScore': homeScore,
'awayScore' : awayScore
},
dataType: "json",
traditional: true,
success: function(data) {
//data returned from php
// update the values
if (data) {
homeScoreTxt.value = data.homeScore;
awayScoreTxt.value = data.awayScore;
saveBtn.innerText = 'Resave';
alert('Success!');
}
},
error: function() {
alert('An error has occurred!');
}
});
});
#endforeach
});
Resulting HTML for Ajax function:
$(document).ready(function() {
$("#SaveBtn11870").on('click', function() {
var gameId = "11870";
var saveBtn = document.getElementById("SaveBtn11870");
var homeScoreTxt = document.getElementById("homeScoreTxtBox11870");
var homeScore = homeScoreTxt.value;
var awayScoreTxt = document.getElementById("awayScoreTxtBox11870");
var awayScore = awayScoreTxt.value;
$.ajax({
url: "http://mbcathletics/admin",
type: "POST",
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
//all the data you need to pass to controller function
data: {
'gameId' : 11870,
'homeScore': homeScore,
'awayScore' : awayScore
},
dataType: "json",
traditional: true,
success: function(data) {
//data returned from php
// update the values
if (data) {
homeScoreTxt.value = data.homeScore;
awayScoreTxt.value = data.awayScore;
saveBtn.innerText = 'Resave';
alert('Success!');
}
},
error: function() {
alert('An error has occurred!');
}
});
});
... many more of the same function for different button IDs ...
});
Button in blade: (calls its respective function successfully)
<button id="SaveBtn{{ $game->id }}" type="button" class="btn btn-outline-success">Save</button>
Route in web.php: (remember, I do not want to leave the page, I just want it to execute the Controller function... I don't know what to put in the first parameter - the URL)
Route::post('/admin', [App\Http\Controllers\HomeController::class, 'inlineSave'])->name('inlineSave');
Controller function: (it doesn't really do anything right now, I'm just trying to test connectivity before I do the heavy lifting)
public static function inlineSave()
{
$game = Game::find($gameId);
$score = $game->home_score;
$game->home_score = $score;
$response = [
'homeScore' => $homeScore,
'awayScore' => $awayScore
];
return response()->json($response);
}
Thank you! I am sorry for the detail, but it's the only I know how to help.
I'm having trouble trying to show the view I want after calling the controller method from Ajax.
This is the JavaScript function where I call the controller Method 'create_pedido' with an Ajax post.
$('.small-box').on('click', function(e) {
e.preventDefault();
let camarero_id = document.getElementById('id_camarero').value;
let mesa_id = e.currentTarget.parentElement.attributes.idMesa.value;
let mesa_estado = e.currentTarget.parentElement.attributes.disponible.value;
console.log('ID Mesa: ' + mesa_id);
console.log('Disponible: ' + mesa_estado);
console.log('ID Camarero: ' + camarero_id);
if (mesa_estado == 1) {
console.log('Crear')
$.ajax({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
type: "POST",
url: 'create_pedido',
data: { mesa: mesa_id, camarero: camarero_id }
}).done(function(msg) {
console.log('Success');
});
} else {
console.log('Editar')
}
});
This is the controller method which does everything but returning the view where it should go after.
public function create(Request $request)
{
$mesa = Mesa::find($request->mesa);
$camarero = Trabajador::find($request->camarero);
$alimentos = Alimento::all();
$categorias = Categoria::all();
Log::channel('stderr')->info($mesa);
Log::channel('stderr')->info($camarero);
return view('pedido.create', compact('mesa', 'camarero','categorias', 'alimentos'));
}
Instead of going to the 'pedidos.create' view after the 'Log::channel...' stay in the same view where it was called.
Here are my routes:
Route::resource('/', 'IndexController');
Route::resource('inicio', 'IndexController');
Route::resource('trabajador', 'TrabajadorController');
Route::resource('pedido', 'PedidoController');
Route::post('create_pedido','PedidoController#create');
Route::resource('alimento', 'AlimentoController');
Route::resource('orden', 'OrdenController');
Route::resource('mesa', 'MesaController');
Route::post('mesa_changestate', 'MesaController#change_state');
Its likely returning to the same view because you are not telling it the base URL of the app. Laravel has a nice method to help here, which will prepend the route with the appropriate base:
$.ajax({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
type: "POST",
url: '{{url("create_pedido")}}', // use blade to add in the Laravel url() method here
data: { mesa: mesa_id, camarero: camarero_id }
}).
Also for the return statement in the controller create method - is the folder 'pedido' or 'pedidos' - make sure to call the right view :)
I managed to retrieve a dynamic element ID from inside a foreach and send it to a controller this way:
#using (Html.BeginForm("DeleteConfirmed", "Gifts", FormMethod.Post, new { #class = "form-content", id = "formDiv" }))
{
foreach (var item in Model.productList)
{
<input type="button" value="Delete" onclick="DeleteButtonClicked(this)" data-assigned-id="#item.ID" />
}
}
and here's the relevant script, pointing to the controller's ActionResult method in charge for item deletion:
function DeleteButtonClicked(elem) {
var itemID = $(elem).data('assigned-id');
if (confirm('sure?')) {
window.location.href = "/Gifts/DeleteConfirmed/" + itemID;
}}
Now, this works just fine, as itemID is correctly retrieved.
As I would like to add a #Html.AntiForgeryToken() to the form, the idea is to change the MVC controller's Actionmethod into a JsonResult adding a little Ajax to the script, allowing me to pass both itemID and token.
Something like:
function DeleteButtonClicked(elem) {
event.preventDefault();
var form = $('#formDiv');
var token = $('input[name="__RequestVerificationToken"]', form).val();
var itemID = $(elem).data('assigned-id');
if (confirm('sure?')) {
$.ajax({
type: 'POST',
datatype: 'json',
url: '#Url.Action("DeleteConfirmed", "Gifts")',
data: {
__RequestVerificationToken: token,
id: itemID
},
cache: false,
success: function (data) { window.location.href = "/Gifts/UserProfile?userID=" + data; },
error: function (data) { window.location.href = '#Url.Action("InternalServerError", "Error")'; }
});
}
dynamic }Some
but I have no idea on how to add the 'event' to the element (this => elem) in <input type="button" value="Delete" onclick="DeleteButtonClicked(this)" data-assigned-id="#item.ID" /> that I am using to identify the item inside the foreach loop, in order to pass it to the script.
Above script obviously fails as there's no 'event' (provided this would end to be the only mistake, which I'm not sure at all).
Some help is needed. Thanks in advance for your time and consideration.
What you want to do is use jQuery to create an event handler:
$('input[type="button"]').on('click', function(event) {
event.preventDefault();
var form = $('#formDiv');
var token = $('input[name="__RequestVerificationToken"]', form).val();
var itemID = $(this).data('assigned-id');
if (confirm('sure?')) {
$.ajax({
type: 'POST',
datatype: 'json',
url: '#Url.Action("DeleteConfirmed", "Gifts")',
data: {
__RequestVerificationToken: token,
id: itemID
},
cache: false,
success: function (data) { window.location.href = "/Gifts/UserProfile?userID=" + data; },
error: function (data) { window.location.href = '#Url.Action("InternalServerError", "Error")'; }
});
}
});
Just make sure you render this script after your buttons are rendered. Preferably using the $(document).onReady technique.
Try the 'on' event handler attachment (http://api.jquery.com/on/). The outer function is shorthand for DOM ready.
$(function() {
$('.some-container').on('click', '.delete-btn', DeleteButtonClicked);
})