I have #Html.PagedListPager(Model, page => Url.Action("GetTabData", new { page })
and inside my js file I have ready to use myTab variable which I need to send together with page in above example.
How can I do that?
Update:
I'm using js variable to determine which tab is user click and based on that value I'm quering data. Now I have implemeted pagination which uses above generated link. With this in place my ajax call for sending activeTab is broken, I need to send this value together with page inside above Url.Action.
This is js variable which I use to send over ajax to determine which tab is user click
$(function () {
var activeTab = null;
$('#tabs .tabLink').click(function (event) {
var activeTab = $(this).attr('href').split('-')[1];
GetTabData(activeTab);
});
GetTabData(ommitted on purpse)
});
I don't get the question clearly, but I am taking a guess here. Don't know if this is what you are looking for.
Note - You have GetTabData in both your javascript as well as cshtml code, I am hoping this is just coincidence, because the js function cannot be invoked via #Url.Action in this manner.
If you need to send two values as part of your URL, you could do it either in a RESTful way or have querystrings.
Option 1 -
Url.Action("GetTabData", new { page=2, tab="blah" })
Your corresponding controller action would look like
public ActionResult GetTabData(int page, string tab)
{
...
}
Option 2 -
create a querystring and append it to the URL
/GetTabData?page=2&tab=blah
In this case the controller action would look like this
public ActionResult GetTabData()
{
var params = Request.QueryString;
...
}
Related
I'm trying to create a page for admins to create new users. When these users are created they will also have a role that they are assigned, and each of those roles will have a list of associated rights. The role will be selected from a dropdownlist of roles. After the role has been selected, but before the user has been created I want the list of associated roles to be displayed on the side of the form so they can see if the rights associated with that role are what they want.
I'm not entirely sure how to approach this problem without reloading the entire page after selecting a role. I'm sure how to have a refresh on a div when the information changes
You can use AJAX to achieve this.
Here is a simple solution to start with. First, create an action method in your controller which accepts the roleId value and get's the rights associated with that role and return that as JSON array.
In the below example, I am simply hard coding 2 rights. You can replace this implementation with however you want to get the data(may be from a database) using the roleId param value.
public JsonResult RoleDetails(int roleId)
{
// Hard coded data.
// Replace with data from your db using roleId value
var rightList = new List<string> { "Admin", "Editor" };
return Json(rightList);
}
So when called with a request URL like /Home/RoleDetails?roleId=2, this action method will return data like this(JSON array).
["Admin","Editor"]
Now, Render your select element with the Roles as options. We shall store the path to the action method in a data attribute on the SELECT element. For example, your rendered HTML should be like this.
<select id="selectedRole" data-url="/Home/RoleDetails">
<option>Select one</option>
<option value="1">Role A</option>
<option value="2">Role B</option>
<option value="3">Role C</option>
</select>
<div id="role-details"></div>
We also rendered a div to show the details.
I am assuming you know how to render a SELECT element. If you do not, please refer
Select Tag Helper in ASP.NET Core MVC post
You can use the Url.Action helper to generate the correct path to the RoleDetails action method, for example,
<select id="selectedRole" data-url="#Url.Action("RoleDetails","Home")">
Now you can use JavaScript to listen to the change event of the SELECT element, read the selected option value, make an ajax call to the action method we created above, get the data ,which is a JSON array, and update the UI using that.
Here is a working sample using jQuery for DOM manipulations.
$(function () {
// Listen to "change" event on SELECT
$("#selectedRole").change(function () {
//Get the value of selected option
var roleId = $(this).val();
var url = $(this).data("url") + "?roleId=" + roleId;
//Make the AJAX call
$.getJSON(url).then(function (data) {
var list = "";
// Loop through the JSON array and build HTML for a list of P tags.
$.each(data, function (index, item) {
list += "<p>" + item + "</p>";
});
// Update the Div content
$("#role-details").html(list);
}).fail(function (e) {
$("#role-details").html("Error getting data");
console.warn(e);
});
});
})
I need to get the selected value of a dropdownlist when I click an ActionLink.
Here is the dropdownlist I am binding from controller.
#Html.DropDownList("resource", new SelectList(ViewBag.ResourceList, ViewBag.SelectedResource), "Resource", new { #class = "span6", #style = "width:14%; color:black;"})
And ActionLink with the function without [HttpPost]
#Html.ActionLink("Export Data", "ExportData");
I have tried from Request.Form["resource"] but it gives me null all the time
public ActionResult ExportData()
{
var req = Request.Form["resource"];
}
I just need to get the text value whatever is in the DropDownList inside my ExportData Function.
The action link basically renders an a tag, and the a tag will look something roughly like this;
Export Data
Because links issue a GET request, any parameters need to be passed via:
Export Data
Request.Form will always be empty in a get request because a POST request populates the form collection. But either way, with a GET or POST request, you can pass the data as a parameter of the action method like:
public ActionResult ExportData(string resource)
So either put a <form> around the data you want to post to the server and change the hyperlink to a button to initiate the post, or use javascript to append "?resource=VAL" to the end of the hyperlink HREF attribute, where VAL is the value from the dropdown.
EDIT: in the few scenarios I had to do this before, what I'll normally do is on the link, add a data attribute (in C# API, use data_ for data attributes):
#Html.ActionLink("Export Data", "ExportData", new { data_url = "ExportData" })
The reason why I use a data attribute is to preserve the original URL and that way I don't have to do string manipulation. Using jQuery, on resource value change, you can update the URL easily:
$("#resource").on("change", function(e) {
var val = $(this).val();
var href = $("#linkid").data("url") + "?resource=" + val;
$("#linkid").attr("href", href);
});
Anytime the dropdown changes, it updates the link url.
You should try the GetValues() method:
public ActionResult ExportData()
{
var req = Request.Form.GetValues("resource");
}
Question background:
I am implementing some basic 'shopping cart' logic to an MVC app. Currently when I click a link - denoted as 'Add To Cart' on the screen shot below this calls to an 'AddToCart' method in the 'ProductController' as shown:
Product.cshtml code:
#Html.ActionLink("Add To Cart", "AddToCart")
'AddToCart' method in the ProductController:
public void AddToCart()
{
//Logic to add item to the cart.
}
The issue:
Not an issue as such but currently when I click the 'Add To Cart' button on the ActionLink on the ProductDetail.cshtml view the page calls the 'AddToCart' method on the ProductController and gives a blank view on the page - as shown below. I want the view to stay on 'ProductDetail.cshtml' and just call the 'AddToCart' method, how do I do this?
Basically #Html.ActionLink() or <a></a> tag uses get request to locate the page. Hence whenever you clicked it, you request to your AddToCart action method in ProductController and if that action method returns null or void so a blank or empty page is shown as you experienced (because or #Html.ActionLink() get request by Default).
So if you want to add your value to cart then call AddToCart method using ajax i.e:
HTML:
#Html.ActionLink("Add To Cart", "AddToCart", null, new { id="myLink"})
Jquery or Javascript:
$("#myLink").click(function(e){
e.preventDefault();
$.ajax({
url:$(this).attr("href"), // comma here instead of semicolon
success: function(){
alert("Value Added"); // or any other indication if you want to show
}
});
});
'AddToCart' method in the ProductController:
public void AddToCart()
{
//Logic to add item to the cart.
}
Now this time when the call goes to AddToCart method it goes by using ajax hence the whole page will not redirect or change, but its an asynchronous call which execute the AddToCart action method in your ProductController and the current page will remains same. Hence the product will also added to cart and page will not change to blank.
Hope this helps.
The answer of Syed Muhammad Zeeshan is what you are looking for, however you may return an EmptyResult.
public ActionResult AddToCart()
{
//Logic to add item to the cart.
return new EmptyResult();
}
According to this it has no impact on your code ASP.Net MVC Controller Actions that return void
But maybe sometime you want to return data and then you could do something like this:
if (a)
{
return JSon(data);
}
else
{
return new EmptyResult();
}
As many people mentioned here you will need to use AJAX if your using asp.net MVC to hit a controller POST function without having to leave your view.
A good use case for this is if you want to upload a file without refreshing the page and save that on the server.
All of the
return new EmptyResult();
Wont work, they will still redirect you.
Here is how you do it, in your view have the follow form as an example:
<form enctype="multipart/form-data" id="my-form">
<p>
The CSV you want to upload:
</p>
<input type="file" class="file-upload" name="FileUpload" />
</div>
<div>
<button type="submit" class="btn btn-default" name="Submit" value="Upload">Upload</button>
</div>
</form>
Then in the JavaScript side you need to add this to your view with within Script tags.
$("#my-form").on('submit', function (event) {
event.preventDefault();
// create form data
var formData = new FormData();
//grab the file that was provided by the user
var file = $('.file-upload')[0].files[0];
// Loop through each of the selected files.
formData.append('file', file);
if (file) {
// Perform the ajax post
$.ajax({
url: '/YourController/UploadCsv',
data: formData,
processData: false,
contentType: false,
type: 'POST',
success: function (data) {
alert(data);
}
});
}
});
Your controller might look something like this to process this type of file:
[HttpPost]
public void UploadCsv()
{
var listOfObjects = new List<ObjectModel>();
var FileUpload = Request.Files[0]; //Uploaded file
//check we have a file
if (FileUpload.ContentLength > 0)
{
//Workout our file path
string fileName = Path.GetFileName(FileUpload.FileName);
string path = Path.Combine(Server.MapPath("~/App_Data/"), fileName);
//Try and upload
try
{
//save the file
FileUpload.SaveAs(path);
var sr = new StreamReader(FileUpload.InputStream);
string csvData = sr.ReadToEnd();
foreach (string r in csvData.Split('\n').Skip(1))
{
var row = r;
if (!string.IsNullOrEmpty(row))
{
//do something with your data
var dataArray = row.Split(',');
}
}
}
catch (Exception ex)
{
//Catch errors
//log an error
}
}
else
{
//log an error
}
}
There are many ways to accomplish what you want, but some of them require a lot more advanced knowledge of things like JavaScript than you seem aware of.
When you write ASP.NET MVC applications, you are required to have more intimate knowledge of how browsers interact with the web server. This happens over a protocol called HTTP. It's a simple protocol on the surface, but it has many subtle details that you need to understand to successfully write ASP.NET MVC apps. You also need to know more about Html, CSS, and JavaScript.
In your case, you are creating an anchor tag (<a href="..."/>), which when click upon, instructs the browser to navigate to the url in the href. That is why you get a different page.
If you don't want that, there are a number of ways change your application. The first would be, instead of using an ActionLink, you instead simply have a form and post values back to your current controller. And call your "add to cart" code from your post action method.
Another way would be have your AddToCart method look at the referrer header (again, part of that more subtle knowledge of http) and redirect back to that page after it has processed its work.
Yet another way would be to use Ajax, as suggested by Syed, in which data is sent to your controller asynchronously by the browser. This requires that you learn more about JavaScript.
Another option is to use an embedded iframe and have your "add to cart" be it's own page within that iframe. I wouldn't necessarily suggest that approach, but it's a possibility.
Controller should return ActionResult. In this case a redirect to the caller page.
using System.Web.Mvc;
using System.Web.Mvc.Html;
public ActionResult Index()
{
HtmlHelper helper = new HtmlHelper(new ViewContext(ControllerContext, new WebFormView(ControllerContext, "Index"), new ViewDataDictionary(), new TempDataDictionary(), new System.IO.StringWriter()), new ViewPage());
helper.RenderAction("Index2");
return View();
}
public void Index2(/*your arg*/)
{
//your code
}
I was struggling with this and couldn't get it working with ajax.
Eventually got a working solution by making my controller method return type ActionResult rather than void and returning a RedirectToAction() and inputting the action relating to the view I wanted to remain on when calling the controller method.
public ActionResult Method()
{
// logic
return RedirectToAction("ActionName");
}
I have a datatable, on that datatable i set a Html.ActionLink. When I click that action link, I want to send an id of the item to a javascript function and have a new datatable appear below with all of its content that belongs to the selected item in the datatable above. So for example if I click a students name in a table, I want all the students Grades and Test to appear below in a separate datatable. I've never worked with javascript much so I'm not sure how I can do this. If someone can please point me in the right direction or give some tips I'd appreciate it.
original first datatable:
#foreach (var item in ((List<Epic>) ViewData["selectedestimate"]))
{
<tr>
<td>
#* #Html.ActionLink(#item.Name, "action", "controller", new {id = item})*#
#item.Name
</td>
Javascript to call:
<script type="text/javascript">
function StoryClick(story) {
$.get("#Url.Action("action", "controller")", function (response) {
$('#stories').accordion({ collapsible: true });
});
}
</script>
ActionController:
public List<EpicDetails> getEpicDetails(int id)
{
return eRepository.getItemsById(id).tolist();
}
Or do I need an ActionResult?
public Actionresult Details(int id)
{
}
I realize that I'm not even close right now, but its just b/c I'm not sure what steps to take to do this.
Eventually I would make a accordion and put the table in the accordion.
In situations like this I like to actually keep the <a> the ActionLink generates, and just add JavaScript to enhance the behavior of the link. So your view wouldn't really change (I did add a class so that we can bind an event handler to it later):
#Html.ActionLink(#item.Name, "action", "controller", new {id = item, #class = "item-link" })
Then write some jQuery (it looks like you already have a dependency on jQuery. If not, I can revise the answer to use vanilla JavaScript) to bind an event handler to links with class item-link:
<script type="text/javascript">
$(document).ready(function () {
$("a.item-link").click(function (event) {
event.preventDefault(); // Stop the browser from redirecting as it normally would
$.get(this.href, function (response) {
// Do whatever you want with the data.
});
});
});
</script>
And, yes, your action method in the controller should return an ActionResult. It's hard for me to say what type of ActionResult you should return without actually knowing what type of data you want to consume on the client, but if you wanted to inject HTML onto the page, you could write something like this:
public ActionResult Details(int id)
{
var itemDetails = /* Get details about the item */;
return PartialView("Details", itemDetails);
}
Then in your JavaScript you would write:
$("a.item-link").click(function (event) {
event.preventDefault(); // Stop the browser from redirecting as it normally would
$.get(this.href, function (response) {
$("element_to_populate").html(response);
});
});
Where element_to_populate would be a selector that points to where you want to inject the HTML.
I would highly recommend using javascript templating (I prefer handlebars.js) on the client side and returning your student data as a JsonResult. This will keep your bandwidth usage to a minimum.
But, because you seem more comfortable with razor, you could use that for all your templates, return plain html from your controller/view, and then use this javascript instead
<script type="text/javascript">
$(function() {
$("a.item-link").click(function (event) {
event.preventDefault(); // Stop the browser from redirecting as it normally would
$("#gradesContainer").load(this.href, function (response) {
//Do whatever you want, but load will already have filled up
//#gradesContainer with the html returned from your grades view
});
});
});
</script>
In your main page, below the student list, you would just need to add
<div id="gradesContainer"></div>
Your other controller would look like this
public ActionResult TestGrades(int id) {
var model = getTestGradesModel(id);
return View(model);
}
If you were returning JSON for client-side javascript templating it would look like
public ActionResult TestGrades(int id) {
var model = getTestGradesModel(id);
return new JsonResult() {Data = model}; //no view here!
}
This is my method:
$(document).ready(function () {
$('td.clickableCell').click(function () {
var currentObject = null;
currentObject = $(this).text();
#Html.ActionLink("GetThis", "Get", new {theName = currentObject} )
});
});
but it says that currentObject doesn't exist in the current context. How to resolve this?
Instead of #Html.ActionLink you should use the jQuery.get function. `#Html.ActionLink is run on the server whereas the javascript is run on the client.
$(document).ready(function () {
$('td.clickableCell').click(function () {
var currentObject = $(this).text();
$.get('#Url.Action("GetThis", "Get")', {theName : currentObject});
});
});
The Url.Action is rendered on the server and will give you the appropriate url. The $.get will run a get request on the client.
Keep in mind, if this javascript is in a .js file, the Url.Action will not be run. In that case you may simply want to replace it with /Get/GetThis or render the url in a hidden field on the page and get the value of the hidden field in your .js file.
You need an action method that looks like this in order to access the parameter:
public ActionResult GetThis(string theName)
{
// manipulate theName
return View();
}
currentObject is a JavaScript String object that you are trying to pass into server side code. If you need to do this on the client side,
$(function () {
$('td.clickableCell').click(function () {
var currentObject = $(this).text();
// find the anchor element that you need to change,
// then change the property on it to the value
// of currentObject
$('a').attr('title', currentObject);
});
});
Alternatively, it's possible that you need to send the value to the server in some way. If the JavaScript above is within a Razor view, then
$(function () {
$('td.clickableCell').click(function () {
var currentObject = $(this).text();
// make a HTTP GET request and pass currentObject as a queryparam
window.location = '#Url.Action("Action", "Controller")' + '?theName=' + encodeURIComponent(currentObject);
});
});
The '#Url.Action("Action", "Controller")' portion will have been evaluated on the server-side and been resolved by the UrlHelper to the URL to route to that controller action. We put this value in single quotes as we need to use it on the client side in a JavaScript variable. Then we add currentObject as a query parameter (and encode it at the same time).
You're mixing client-side code with server-side code. This line is being executed on the server before anything is sent to the client:
#Html.ActionLink("GetThis", "Get", new {theName = currentObject} )
That line, by itself, references something which doesn't exist. currentObject won't exist until it's created in JavaScript on the client. That JavaScript code, from the perspective of the server, is nothing more than text.