In View:
<table>
#foreach(String key in map.Keys)
{
<tr>
<td>
#key
<td>
<tr>
}
<table>
How can I pass the value of key to page2 and retrieve it in page2?
You can add values to the query string using an overload of Url.Action:
Url.Action("page2", "page1", new { key = key })
This would add the value of key to the URL query string, denoted by the key "key".
Besides using passing value with Url.Action, You can also use Html.ActionLink.
#Html.ActionLink("some text here", "actionName", "controllerName", new { id = "1" }, null)
It gives you a link like the following.
some text here
Related
Before I start, I'm relatively new to coding and was due to start my first junior role in the upcoming future, so my current skill level is pretty basic.
I'm creating a personal C# ASP.NET MVC web application to track music events that users go to, and the artists that are playing at those events, and which of the artists a user sees. This is all done by the user manually adding events from an Indexed view of all events within the database (SQL Server) which is show in the image linked below.
https://i.stack.imgur.com/HOUGG.png
The controller action:
public ActionResult GetEvents()
{
return View(_trackerService.GetEvents());
}
The markup for the view:
#model IEnumerable<Tracker.Data.tbl_events>
#{
ViewBag.Title = "GetEvents";
}
<h2>GetEvents</h2>
<p>
#Html.ActionLink("Create New", "CreateEvent")
</p>
<table class="table">
<tr><h2>Upcoming Events</h2>
<th>
#Html.DisplayNameFor(model => model.Event_ID)
</th><th>
#Html.DisplayNameFor(model => model.Event_Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Event_Date)
</th>
<th>
#Html.DisplayNameFor(model => model.Event_Location)
</th>
</tr>
#foreach (var item in Model.Where( x => x.Event_Date >= System.DateTime.Now).OrderBy(x => x.Event_Date))
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Event_ID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Event_Name)
</td>
<td>
#Convert.ToString(string.Format("{0:dd/MM/yyyy}", item.Event_Date)) #*Converts the DateTime data type that ASP.NET uses by default into a string with the format of Date Only (https://stackoverflow.com/a/34990313/12764653)*#
</td>
<td>
#Html.DisplayFor(modelItem => item.Event_Location)
</td>
<td>
#*#Html.ActionLink("Edit", "Edit", new { id = item.Event_ID }) | *#
#Html.ActionLink("Details", "GetEventDetails", new { Event_ID = item.Event_ID }) |
#Html.ActionLink("Lineup", "../Event/GetLineup", new { Event_ID = item.Event_ID, Event_Name = item.Event_Name }) |
#if ((System.Web.HttpContext.Current.User != null) && System.Web.HttpContext.Current.User.Identity.IsAuthenticated)
{#*Checks to see if there is a current user*#
#Html.ActionLink("Add to my Events", "../Event/AddToUser", new { User_ID = Convert.ToString(System.Web.HttpContext.Current.User.Identity.GetUserId()).GetHashCode(), Event_ID = item.Event_ID })}
#*#Html.ActionLink("Delete", "Delete", new { id = item.Event_ID })*#
</td>
</tr>
}
</table>
Once a user adds an event to their profile, it creates an entry into the database with the fields (Linked Below):
https://i.stack.imgur.com/YQqHT.png
The controller method for the 'Add To My Events' function:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult AddToUser(tbl_eventhistory _event)
{
try
{
_trackerService.AddToUser(_event);
return RedirectToAction("GetEvents");
}
catch
{
return View();
}
}
I understand that with the way it is currently, this would most likely have to be done either in the controller or View, and it would be done to check 'tbl_eventhistory' to see if an entry exists with the current users User_ID and a specific Event_ID, however I'm unsure of how to actually do this so any help would be greatly appreciated.
EDIT: The table shown is an indexed view of 'tbl_events'. When a user adds an event to their profile from this view, it creates an entry in a different table called 'tbl_eventhistory' using the parameters for that specific event, which is related through a foreign key on the Event_ID (tbl_event's PK). When an event is added to tbl_eventhistory by the user, I want to remove the link 'Add To User' from the view for that specific event only.
You already have a Delete link in your Razor view - that seems to be good enough:
#Html.ActionLink("Delete", "../Event/DeleteEvent", new { id = item.Event_ID })
And then your EventController would look something anlog these lines:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult DeleteEvent(int id)
{
try
{
_trackerService.DeleteForUser(HttpContext.Current.User.Identity.GetUserId(), _event); // this is where you verify whether currently logged in user has access and delete their associated event.
//depending on your data access framework of choice you should aim to fire a query like so:
//DELETE FROM table WHERE User_Id = :user_id AND Event_Id = :id
//that would cover you for users deleing only items belonging to them
return RedirectToAction("GetEvents");
}
catch
{
return View();
}
}
now to point out two security issues with your AddEvent code:
#Html.ActionLink("Add to my Events", "../Event/AddToUser", new { User_ID = Convert.ToString(System.Web.HttpContext.Current.User.Identity.GetUserId()).GetHashCode(), Event_ID = item.Event_ID })}
GetHashCode() is not what you think it is: using it as identifier is unsecure because collisions on default implementation are very likely
there's no need to even pass the user Id back from client: your controllers have access to HttpContext object so you should be able to just grab that User_ID on server side
and to attempt to answer your questions in title:
How to check if an entry exists in a separate SQL Server table,
I don't believe you need it for purposes of deleting events - see comments in the code above
and change which function is linked depending on whether it exists or not
I an not sure if I get this part. I feel you might be able to answer both of these yourself after having a look through this other SO question on SQL JOIN. Otherwise I (and the community here) will need more details on your actual use case and goal
Managed to finally find a solution. Please forgive me if the terminology is incorrect.
I already had a function to return an indexed view of a specific users events (DAO Class code below)
public IList<tbl_eventhistory> GetUserEvents(string User_ID)
{
IQueryable<tbl_eventhistory> _eventHistory;
_eventHistory = from tbl_eventhistory in _context.tbl_eventhistory where tbl_eventhistory.User_ID == User_ID select tbl_eventhistory;
return _eventHistory.ToList<tbl_eventhistory>();
}
Thinking how to use this data in a view with a different bound Model, I thought about using a ViewBag and came across this: "How to check if a List in a ViewBag contains string". From this answer I modified my 'GetEvents' function to the following:
public ActionResult GetEvents()
{
List<string> UsersEvents = new List<string>();
foreach (var item in _trackerService.GetUserEvents(User_ID))
{
UsersEvents.Add(item.Event_ID.ToString());
}
ViewBag.MyEvents = _trackerService.GetUserEvents(User_ID);
ViewBag.MyEvents = UsersEvents;
return View(_trackerService.GetEvents());
}
If my understanding is correct, this creates a List which is populated by calling the 'GetUserEvents' action and loops through each entry in the result. Moving this list to a ViewBag then allows me to modify the 'GetEvents' ActionLinks to include an additional IF statement inside the existing statement checking whether a user is logged in:
#if ((System.Web.HttpContext.Current.User != null) && System.Web.HttpContext.Current.User.Identity.IsAuthenticated)
if (((IList<string>)ViewBag.MyEvents).Contains(item.Event_ID.ToString())){
<p>TEST</p> }
else{
#Html.ActionLink("Add to my Events", "../Event/AddToUser", new { User_ID = Convert.ToString(System.Web.HttpContext.Current.User.Identity.GetUserId()).GetHashCode(), Event_ID = item.Event_ID })}
}
Which resulted in "Test" being shown instead of the link to 'Add To User' where appropriate (shown below with the final event not being present in 'tbl_eventhistory'):
http://icecream.me/b459c9d17854b081a0804692f67c3aa3
I understand this is probably the furthest from being the most efficient way of doing it, but I'm just glad it now works after a LONG time trying to figure it out.
In my controller I do have this endpoint:
async Task<FileResult> DownloadSelection(AssignedDatabaseSelection selection)
And my HTML looks like:
#if (Model.AssignedDatabaseSelections.Any())
{
<table>
#foreach (var selection in Model.AssignedDatabaseSelections)
{
<tr>
<td>#selection.DisplayName</td>
<td width="10%">
#Html.ActionLink(Strings.CsvLabel, "DownloadSelection", "Home", selection, null)
</td>
</tr>
}
</table>
}
Now I wanted to add another parameter to my controller method:
async Task<FileResult> DownloadSelection(AssignedDatabaseSelection selection, DownloadFormat format)
And
#if (Model.AssignedDatabaseSelections.Any())
{
<table>
#foreach (var selection in Model.AssignedDatabaseSelections)
{
<tr>
<td>#selection.DisplayName</td>
<td width="10%">
#Html.ActionLink(Strings.CsvLabel, "DownloadSelection", "Home", new {selection = selection, Format = DownloadFormat.CSV}, null)
</td>
<td width="10%">
#Html.ActionLink(Strings.ExcelLabel, "DownloadSelection", "Home", new { selection = selection, Format = DownloadFormat.CSV }, null)
</td>
</tr>
}
</table>
}
When I make an inspect elements I got this:
Excel
Now, the format is filled, but the selection is always null. What am I missing?
In you first example, the 3rd parameter is a complex object (typeof AssignedDatabaseSelections) and the method will correctly serialize each property of your object to a query string. Note that it only works because your object contains only simple properties.
In the second example, your creating a new object containing a complex object. The ActionLink() method (and all methods that generate route/query string values) call the .ToString() method on each property in the object (hence you get the name of the class) and do not do recursion.
If you want to pass back all properties of the AssignedDatabaseSelections object plus another property, you need to generate a new object containing each property of AssignedDatabaseSelections, for example (assuming it contains properties ID and Name)
#Html.ActionLink(Strings.CsvLabel, "DownloadSelection", "Home",
new { ID = selection.ID, Name = selection.Name, ......, Format = DownloadFormat.CSV }, null)
Note that if AssignedDatabaseSelections contains a lot of properties and/or larger values, you risk exceeding the query string limit and throwing an exception. A better approach is just to pass the ID property of the AssignedDatabaseSelection and get the object again in the GET method if you need other properties of it.
#Html.ActionLink(Strings.CsvLabel, "DownloadSelection", "Home",
new { ID = selection.ID, Format = DownloadFormat.CSV }, null)
Here is an example of how to create a link with properties from an object. I am including the code of creating a new Employee within the view but this can/should be in the model:
#{ var emp = new Employee { Name = "Tom", Age = 44 }; }
#Html.ActionLink("Test", "Test2", "Account", new {Name = emp.Name, Age = emp.Age }, null)
Please note how I am passing each property of the Employee into the anonymous object for the route values.
The above link will work with this action. The DefaultModeBinder will take the names from the query string in the link and assign it to the properties of the Employee and pass it to the action below:
public ActionResult Test2(Employee emp)
{
return null;
}
new to this type of work , need your help
in my view .cshtml ----
<table class>
<thead>
<tr>
<th >#Html.CheckBox("IsAllRowSelected")</th>
<th >
#Html.DropDownListFor(m => m._dropDownForcolumn2, new List<SelectListItem>
{ new SelectListItem{Text="option1", Value="option1"},
new SelectListItem{Text="option2", Value="option2"},
new SelectListItem{Text="option3", Value="option3"}
}, new {#id="dropDownForcolumn2" })
</th>
<th>#Html.Label(" column 3 ")</th>
<th>#Html.Label("column 4")</th>
<th>#Html.Label("column 5")</th>
</tr>
</thead>
<tbody>
#foreach (Models.MyModelClass item in Model._List)
{
<tr>
<td>#Html.CheckBox("IsEachRowSelected")</td>
<td>#item.Option1Values</td>
#*//#item.option2values;
#item.option3vlaues;*#
<td>#item.column3value</td>
<td>#item.column4value</td>
<td>#item.column5value</td>
</tr>
}
1 .cant post back to controller again to get only this column values . its just a small table in a huge page
2 i already have the other values in item
now only option1 values are coming in the column , the requirement is to bind 2nd colmn with header dropdown and 2nd option selected then this will show #item.option2values and 3rd option selected then #item.option3values will be shown
other columns will not be changed or touched .
somthing like this
<td>
if(dropdownvalue = option1)
#item.Option1Values
elseif(dropdownvalue == option2 )
#item.option2values
elseif(dropdownvalue == option2 )
#item.option3vlaues
</td>
ajax , jquery is allowed but whole page post or partial view post is not allowed
do it like this
put onload method in table tag
function load()
{
$('.MyProperty2').hide();
$('.MyProperty3').hide();
}
function Filldropdown(){
var value = $('#dropdownID :selected').text();
if (value == 'option1') {
$('.MyProperty1').show();
$('.MyProperty2').hide();
$('.MyProperty3').hide();
} else if (value == 'option2') {
$('.MyProperty2').show();
$('.MyProperty1').hide();
$('.MyProperty3').hide();
} else if (value == 'option3') {
$('.MyProperty3').show();
$('.MyProperty2').hide();
$('.MyProperty1').hide();
}
}
Say I have a table that begins like this:
#foreach (var item in Model)
{
<tr>
<td>
#Html.ActionLink(item.Name, "Things", new { someId = item.Id})
</td>
And it takes me to the view for that specific item. Once I am in that separate .cshtml file, how can I reference back to the original "item.Name" string that appeared in this ActionLink?
* SOLUTION EDIT *
The solution ended up looking like this.
The ActionLink:
#Html.ActionLink(item.Name, "Things", new { someId = item.Id, someName = item.Name})
The Action:
public ActionResult Things(Guid? someId, string someName)
...
ViewBag.currentName = someName;
The other View:
<h2>#ViewBag.currentName</h2>
You can add an extra parameter with the name of the item
#Html.ActionLink(item.Name, "Things", new { someId = item.Id, name = item.Name})
You will need to modify your controller and model too for this approach.
I think you don't have to reference to first ActionLink argument. What might interest you is an object which is a Model for this view. When you click the link you will navigate to action where you pass the someId - id of current model object. Then probably you should find an object by its id in your storage. It will have .Name property that was used as ActionLink argument.
However if you just need exactly the same string you've used as a first argument in ActionLink which can be any string you should duplicate it in route values. Your Things action should accept not only someId argument but also this string. After you should pass this string via Model or ViewBag/ViewDictionary to your view.
I have a webgrid in mvc3. It has the column delete. Upon clicking it, I want to run a Javascript function to redirect the user to a controller's action by passing a row id as the parameter to the Javascript function.
How would I do this? The column is not an Htmlactionlink.
Assuming this is the way you have the rows:-
<tr id="thisRowId">
.
.
.
<td>
<a id="deleteBtn" data-rowId="thisRowId">delete</a>
</td>
<tr>
Have a generic function for your delete click
$('#deleteBtn').click(function(){
var id = $(this).data('rowId'); // or use $(this).closest('tr').attr('id');
$.ajax({
url: "controller/action",
type: 'Delete', // assuming your action is marked with HttpDelete Attribute or do not need this option if action is marked with HttpGet attribute
data: {'id' : "'" + id "'"} // pass in id here
success : yoursuccessfunction
});
};
As documented here, this is an example of removing a table row from a WebGrid following an AJAX request. WebGrid doesn't make it easy to identify a particular item in the table. The issue is how to identify the row to be deleted. In the example, the MvcHtmlString is used to inject a span tag into the column. It contains an id value that is subsequently used to identify the row to be removed from the table.
<div id="ssGrid">
#{
var grid = new WebGrid(canPage: false, canSort: false);
grid.Bind(
source: Model,
columnNames: new[] { "Location", "Number" }
);
}
#grid.GetHtml(
tableStyle: "webGrid",
headerStyle: "header",
alternatingRowStyle: "alt",
columns: grid.Columns(
grid.Column("Location", "Location"),
grid.Column("Number", "Number"),
grid.Column(
format: (item) =>
new MvcHtmlString(string.Format("<span id='ssGrid{0}'>{1}</span>",
item.SecondarySystemId,
#Ajax.RouteLink("Delete",
"Detail", // route name
new { action = "DeleteSecondarySystem", actionId = item.SecondarySystemId },
new AjaxOptions {
OnComplete = "removeRow('ssGrid" + item.SecondarySystemId + "')"
}
)
)
)
)
)
)
</div>
<script>
function removeRow(rowId) {
$("#" + rowId).closest("tr").remove();
}
</script>
You can try attaching a jQuery click handler to the element like so:
HTML:
<tr>
<td>
<a id="your id">delete</a>
</td>
<tr>
Javascript:
$("tr a").click(function() {
//your code
});