MVC Button Click performs action without redirecting - asp.net-mvc

I have a table where users are allowed to "tick" or "cross" out a row. Ticking a row changes the status value to "Approved" and crossing it changes it to "Disapproved". I'm currently using the Edit scaffold to perform it. How do I do this without having the user being redirected to the view. I just want the user to click it and the page refreshes, with the status value being updated.
I'm not sure what code to post here either since I don't know how to write it. If any part of my program is required, please let me know. I'll include it here. Thank you :>

Add css classes to the 2 buttons "approve-btn" and "reject-btn".
Create javascript function to approve and reject and bind them to
the 2 classes
Create 2 backend functions
Make ajax calls from the JS functions to your backend functions passing the id of the row item
In the "success:" of the ajax call manage the change of the status to show "approved" or "rejected"
To make ajax call you can use the following example (although there are tons of example on google). Since you're modifying data you should use POST call and since it is a POST call, you should add a RequestVerificationToken to prevent CSRF attacks.
function Approve(id){
securityToken = $('[name=__RequestVerificationToken]').val();
$.ajax({
url: '/YourControllerName/Approve/' + itemId,
type: 'POST',
data: {
"__RequestVerificationToken": securityToken
},
success: function (data) {
if (data == 'success')
//use jQuery to show the approved message;
else
alert("something went wrong");
},
error: function (request, err) {
alert("something went wrong");
}
});
}
The Token should be created in the View adding this line:
#Html.AntiForgeryToken()

Related

My Stimulus checkbox script in Rails 7 doesn't work

I'm learning stimulus and trying to get add a checkbox feature where you can mark an order as complete from the show page without using a form. I followed this tutorial, but am not getting the correct results. The checkbox does nothing when clicked and unchecks when refreshed; however if I manually set the complete attribute to true, the checkbox is automatically checked when loading the page, as it should.
I have a model "Order" with a boolean attribute "complete". Here's my show.html.erb section
<tr data-controller="todo" data-todo-update-url="<%= order_path(#order.id) %>">
<td>
<div>
<input type="checkbox"
data-action="todo#toggle"
data-target="todo.completed"
<% if #order.complete %> checked <% end %> >
</div>
</td>
</tr>
Here's my stimulus todo_controller
import { Controller } from "#hotwired/stimulus"
export default class extends Controller {
static targets = [ "completed" ]
toggle(event) {
// Inside the toggle(event) function, let’s start by getting the value of the checkbox,
// and put it into a FormData object
let formData = new FormData()
formData.append("#order[complete]", this.completedTarget.completed);
// Let’s post that data to the "update-url" value we set on the Todo row.
// We’ll set the method to PATCH so that it gets routed to our todo#update on our controller.
// The credentials and headers included ensure we send the session cookie and the CSRF protection token and
// and prevent an ActionController::InvalidAuthenticityToken error.
fetch(this.data.get("update-url"), {
body: formData,
method: 'PATCH',
dataType: 'script',
credentials: "include",
headers: {
"X-CSRF-Token": getMetaValue("csrf-token")
}
})
// We can take the Response object and verify that our request was successful.
// If there was an error, we’ll revert the checkbox change.
.then(function(response) {
if (response.status != 204) {
event.target.complete = !event.target.complete
}
})
}
}
Can someone tell me where my code is going wrong?
This is more a long comment than a solution but few things I see :
You create an empty form and append a single input with name
"#order[complete]" though your Stimulus controller is Javascript
and has no knowledge of # the such way you use in Ruby. Also params
names are usually model[field] then I think you don't need the #.
"order[complete]" should be fine.
Also you grab the value from a specific target for the aforementionned value with this.completedTarget.completed. Should you not rather pick the value of the input field ? and rather grab this.completedTarget.value or maybe the checked status this.completedTarget.checked
You are getting the URL to your fetch from a data attribute. I am not a stimulus expert but it doesn't look like anything Stimulus related. As of now you have written it this.data.get("update-url") but in regular javascript, something like this.element.dataset.todoUpdateUrl should work.
And just to be sure there is no confusion to about where you will
call this , just declare it at the top of your Stimulus methd like
this : var backUrl = this.element.dataset.todoUpdateUrl. And fill the url to your fetch as just fetch(backUrl,...
you pass the formData directly to your fetch body. If this doesn't work, try to stringify it and extract the entries like : JSON.stringify(Object.fromEntries(formData)). Also I am not too sure about the dataType: 'script', you may just omit that alltogether.
There may be other problems that I don't see. Also when you are dealing with JS, don't only look to your Rails console, especially if nothing hits the backend. Open the developper / inspect tool in your browser and monitor the console there, you should see all the XHR (async) requests made to your app.
If nothing happens, then your fetch is not firing and there need to be more investigations made..

Rails Frontend Trying to save autogenerated data to database without form

I'm new to ruby on rails. I'm trying to save data that is generated by itself to the database. i have looked into and found I was meant to use ajax, however all the videos/forums i have seen are example of ajax that use form and not refreshing page. i want to save data automatically without pressing submit.
Assume that the project is fresh project with postgresql as the database. I have created a database that can hold geo points by using postgis. i have created another page where it has map implemented where i can manully pin location. I want to save the manuuly pinned location to the database.
function onMapClick(e) {
alert("You clicked the map at " + e.latlng);
}
mymap.on('click', onMapClick);
var popup = L.popup();
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent("You clicked the map at " + e.latlng.toString())
.openOn(mymap);
}
mymap.on('click', onMapClick);
The e.latlng holds the geopoint, but i dont know how to save it the database if the user clicks anywhere on the map.
You don't need submit form to use ajax.
Basically what you want is add event listener to the map, and when user click then send ajax request to the controller.
For example, let's say that your map is inside div with id my-map.
If you use jQuery you can write something like this:
$('#my-map').on('click', function() {
# add your logic here
$.ajax({
url: 'your-url',
type: 'POST',
dataType: 'json',
contentType: "application/json; charset=utf-8",
data: JSON.stringify({
'let': data you want to send to backend
})
}
Hope it works!
EDIT:
After I looked your code I found that you can not have jQuery in your project so you can not use jQuery ajax. You need use vanilla javascript. So instead this snippet above, you can write this.
var xhttp = new XMLHttpRequest();
const params = { saving_location: { geoPoints: e.latlng } }
xhttp.onreadystatechange = function() {//Call a function when the state changes.
if(xhttp.readyState == 4 && xhttp.status == 200) {
alert(http.responseText);
}
}
xhttp.open("POST", "/saving_locations", true);
xhttp.setRequestHeader('Content-Type', 'application/json', 'Accept', 'application/json');
xhttp.send(JSON.stringify(params));
Also add protect_from_forgery with: :null_session in your application controller and skip_before_action :verify_authenticity_token in your Saving Location controller.(under before_action).
Here is good blog post why you need this https://blog.nvisium.com/understanding-protectfromforgery
Please notice that you wan't save your database, because your geoPoints type in database is type of point and you send string to rails controller. I never work with points in rails so I can not help you here.(You can always add two columns in db, one for longitude and one for latitude and then store numbers instead point)

How to handle session expire in partially loaded divs in mvc

I have 2 divs on a page, based on the click id on left i load the content in the right div.
But when session expires, i am expecting the page to redirect to Login, but it does not behave tht way.
some times the button wont work or some times the login screen loads in the right div.
Any suggestions to handle this session expire?
By default, the IIS simply returns the login-page with an HTTP status code 200 when the session is expired. This makes your ajax not see it as an error.
So you need to do a check in your controller action to see whether the Session has expired, and if it has, you can return an HttpStatusCodeResult(HttpStatusCode.Unauthorized).
After that, in your ajax, you can use somthing like this:
$.ajax({
//...
error: function(data, textStatus, xhr) {
if(xhr.status == "401"){ window.location.href = "/login";
}
}

What do I do with low Scores in reCAPTCHA v3?

I have set up reCAPTCHA v3 on my ASP.NET MVC project. Everything is working fine and is passing back data properly.
So the code below depends on another dll I have, but basically, the response is returned in the form of an object that shows everything that the JSON request passes back, as documented by https://developers.google.com/recaptcha/docs/v3
It all works.
But now that I know the response was successful, and I have a score, what do I do? What happens if the score is .3 or below? Some people recommend having v2 also set up for secondary validation (i.e. the 'choose all the stop signs in this picture' or 'type the word you see'). Is that really the only 'good' option?
Obviously the code isn't perfect yet. I'll probably handle the solution in the AJAX call rather than the controller, but still. What should I do if the score is low?
I read this article
reCaptcha v3 handle score callback
and it helped a little bit, but I'm still struggling to understand. I don't necessarily need code (although it would never hurt) but just suggestions on what to do.
VIEW:
<script src="https://www.google.com/recaptcha/api.js?render=#Session["reCAPTCHA"]"></script>
grecaptcha.ready(function () {
grecaptcha.execute('#Session["reCAPTCHA"]', { action: 'homepage' }).then(function (token) {
$.ajax({
type: "POST",
url: "Home/Method",
data: JSON.stringify({token: token }),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
console.log('Passed the token successfully');
},
failure: function (response) {
alert(response.d);
}
});
});
});
CONTROLLER:
[HttpPost]
public void ReCaptchaValidator(string token)
{
ReCaptcha reCaptcha = new ReCaptcha();
Models.ReCaptcha response = new Models.ReCaptcha();
response = reCaptcha.ValidateCaptcha(token);
//response returns JSON object including sucess and score
if (response.Success)
{
//WHAT DO I DO HERE????
}
}
Ended up getting the answer from another forum. Basically, the answer is "anything you want". There is no right or wrong when handing a successful response.
So what could be done, is if the response is successful and CAPTCHA doesn't throw a flag, do nothing. But if CAPTCHA is unhappy, you could display an alert or a banner that says 'could not process', or you could even add in CAPTCA version 2, which would make them do the picture test or the 'I am not a robot' checkbox, etc.

Confused about passing parameters from JavaScript Ajax call back to controller to render a form

Scenario: I click on some objects,table rows,etc on my page and I get their IDs for example I click on Providers list and get provider_id. And then I click on a button on the page:
Now I have a service that accepts those parameters and passes back to me a JSON which I want to show it in a Table form in my next page. So this button click is responsible for that.
So the page I am gonna show that Table in it is Pharmacy/Patients so I have a
PatientsController#index method.
Now in JS side I am doing an Ajax call like this:
// provider_id is global var and coming from the clicks on other parts of the page.
//so we have some value like 234 for it.
$('.personlistbtn').click(function(e) {
$.ajax({
type: 'GET',
data : { 'provider' : provider_id, 'therapeutic_class' : 'all' },
url: '/pharmacy/patients',
async: false,
success: function (data) {
// not sure what to write in here really.
},
error: function () {
// show some oops error
}
}
});
});
So that makes the call to /pharmacy/patients
Now I am confused how to handle it from there?
PatientsController: Maybe something like this?
def index
if request.xhr?
#my_json = MyNetHTTPFunction.getMeBackJSON(params)
end
end
MyNetHTTPFunction.getMeBackJSON(params) is just a method I have written that accepts the query params I am passing to it ( which hopefully are coming from Ajax call right? and queries the web-service and returns me back the JSON I need to use in my View.

Resources