Small Question,
I am having the following in my c# mvc project code:
div class="card-body">
#if (User.IsInRole("Secretaris"))
{
<table class="table">
<tr>
<th>#Html.DisplayNameFor(model => model.FirstName)</th>
</tr>
#foreach (var item in Model)
{
if (User.IsInRole("Secretaris"))
{
<tr>
<td>#Html.DisplayFor(modelitem => item.FirstName)</td>
</tr>
}
}
</table>
}
</div>
When I run the code everything works fine but when I go into the foreach it still gives all the names and not only the names that have the role "Secretaris". Hope someone can help me in what I am doing wrong.
Thanks in advance.
Roel Knippen
Since your Model has a .Firstname property I assume your model represents a person, likely a user.
#User represents the currently logged in user - NOT the user from your model.
You will need to include role information in your model if you want to work with users/permissions in your model. Something like
if (modelItem.Roles.Contains("Sec...")) { }
To show the table to begin with, you check to see if the User has the Secretaris role. So it either will or will not show, depending on that role.
However, then within the foreach loop you check to see if the user is in that role again, which is redundant because that code won't even run if it's not due to your enclosing if statement.
I'm assuming you want to run some kind of check on the "item" object that is an element of the Model.
div class="card-body">
#if (User.IsInRole("Secretaris"))
{
<table class="table">
<tr>
<th>#Html.DisplayNameFor(model => model.FirstName)</th>
</tr>
#foreach (var item in Model)
{
if (item.role == "Secretaris")
{
<tr>
<td>#Html.DisplayFor(modelitem => item.FirstName)</td>
</tr>
}
}
</table>
}
</div>
Maybe something like this where you check if the item.role is secretaris?
Related
What I wanted to do was seeming simple but I am having a bit of trouble. I've created the following class:
// SelectedItem.cs
public class SelectedItem
{
public Brand Brand { get; set; }
public string Model { get; set; }
}
In my view page I've created a list of SelectedItem:
// Chart.cshtml
#model Prediction.Models.Chart.ManualSelection
#{
#using Prediction.Models.Chart
List<SelectedItem> selectedItems = new List<SelectedItem>();
}
And I'm trying to add to this list from the html in my view. I've created a table that has its rows populated by a foreach loop.
// Also Chart.cshtml, below the previous code section where I created the list
<table class="table table-borderless table-hover table-striped table-sm mb-0">
<tbody>
#foreach (var item in Model.PhoneInfo)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Brand)
</td>
<td>
#Html.DisplayFor(modelItem => item.Model)
</td>
<td>
<button type="button" class="btn btn-sm btn-link">Select</button>
</td>
</tr>
}
</tbody>
</table>
Essentially what I'm trying to do is when the button is clicked, I want that specific item.Brand and item.Model to be added to List<SelectedItem> selectedItems.
Unfortunately this is impossible. Like it was mentioned in the comment, this list exists only in the scope of rendering view on the server, before sending to the client's browser. In order to save your item.Brand and item.Model you need to use JavaScript, that will be executed in browser on client side or you can send this information back to the server and process it there.
I have written the following Code to implement sorting. I want to maintain the sortingData(OrderDirection,SortField) between two requests for Sorting. Somehow I'm not able to achieve that.
//In .cshtml Page
#{
SortingPagingInfo info = (SortingPagingInfo)ViewData["SortingPagingInfo"];
}
<form method="post">
#Html.Hidden("SortField", info.SortField)
#Html.Hidden("SortDirection", info.SortDirection)
#Html.Hidden("PageCount", info.PageCount)
#Html.Hidden("PageSize", info.PageSize)
#Html.Hidden("CurrentPageIndex", info.CurrentPageIndex)
<table class="table">
<thead>
<tr>
<th>
<a asp-page="./Page" asp-route-sortData="#info">
#Html.DisplayNameFor(model => model.Tabl[0].Col1)
</a>
</th>
<th>
<a asp-page="./Page" asp-route-sortData="#info">
#Html.DisplayNameFor(model => model.Tabl[0].Col2)
</a>
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Table)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Col1)
</td>
<td>
#Html.DisplayFor(modelItem => item.Col2)
</td>
</tr>
}
</tbody>
</table>
</form>
-----In cshtml.cs Page
The get Request is as below:
public async Task OnGetAsync(SortingPagingInfo sortData)
{
SortingPagingInfo info = new SortingPagingInfo();
if (string.IsNullOrEmpty(sortData.SortDirection))
{
info.SortField = "Col1";
info.SortDirection = "OrderBy";
info.PageSize = 10;
info.PageCount = Convert.ToInt32(Math.Ceiling((double)(_context.Tab.Count()
/ info.PageSize)));
info.CurrentPageIndex = 0;
this.sortPageData = info;
ViewData["SortingPagingInfo"] = info;
}
else
{
info = (SortingPagingInfo)ViewData["SortingPagingInfo"];
}
tab1= await _context.Tabl.OrderByDynamic(info.SortField, info.SortDirection).ToListAsync();
}
I'm trying to pass the object and maintain it in ViewData so that It could be accessed. But everytime, only null value is returned. Is there any better way for implementing Sorting with Razor Pages or If this could be made to work?
You cannot simply dump an object as a route data param on a link. What will actually happen here is that Razor will simply call ToString on your info instance. You have a few options:
Break out each property and pass it individually:
<a asp-page="./Page" asp-route-sortField="#info.SortField" asp-route=sortDirection="#info.SortDirection" asp-route-pageCount="#info.PageCount" asp-route-pageSize="#info.PageSize" asp-route-currentPageIndex="#info.CurrentPageIndex">
Serialize the object and then deserialize it in your action:
<a asp-page="./Page" asp-route-sortData="#JsonConvert.SerializeObject(info)">
Then, your action would need to be changed to accept a string:
public async Task OnGetAsync(string sortData)
And inside the action, you'd then deserialize that string back into an instance of SortingPagingInfo:
var info = JsonCovert.DeserializeObject<SortingPagingInfo>(sortData);
Make your links submit buttons instead (you can still style them as links) and have them actually submit a form with all this data:
<form asp-page-handler="./Page" method="get">
#Html.Hidden("SortField", info.SortField)
#Html.Hidden("SortDirection", info.SortDirection)
#Html.Hidden("PageCount", info.PageCount)
#Html.Hidden("PageSize", info.PageSize)
#Html.Hidden("CurrentPageIndex", info.CurrentPageIndex)
<button type="submit" class="btn btn-link">
#Html.DisplayNameFor(model => model.Tabl[0].Col1)
</button>
</form>
However, you currently have a form that wraps your entire table, and you cannot have forms within forms. As a result, you would need to restructure your HTML to ensure that each of these links was outside of any other forms on the page.
If someone loads say three or four entries, when they search and they select entry two to edit, when they click the open button, how would I make it go to say /entry/2 or if they opened entry three it would say /entry/3 ?
I'm new to ASP.NET MVC so I'm really not sure what I am doing:
#model IEnumerable<Savvy_CRM_MVC.Models.RootObject>
#{
Layout = null;
}
#foreach (var m in Model)
{
}
<table class="mui-table">
<thead>
<tr>
<th>Case ID</th>
<th>Forename</th>
<th>Surname</th>
<th>Postcode</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var m in Model)
{
<tr>
<td>#m.Caseid</td>
<td>#m.Forename</td>
<td>#m.Surname</td>
<td>#m.Postcode</td>
<td><button>Open Case</button></td>
</tr>
}
</tbody>
</table>
When clicking the button, I want the url to change and it to load the data of that entry to the page in a format that will be decided later.
Your best bet is to change the button into a hyperlink:
#foreach (var m in Model)
{
<tr>
<td>#m.Caseid</td>
<td>#m.Forename</td>
<td>#m.Surname</td>
<td>#m.Postcode</td>
<td><a href="/entry/"+#m.CaseId>Open Case</a></td>
</tr>
}
This will generate a GET request to the "entry/{id}" route.
Hope this helps
Not even sure if I asked the question the right way. Been looking at this for about an hour and its too simple to take to long. Trouble is I am too simple to know the answer or even how to correctly phrase a search to find the answer.
I have a history of jobs completed for a site set up.
Controller:
public async Task<IActionResult> JobSiteHistory(int id, int? page)
{
var jobs = from j in _context.Job
.Include(j => j.Site)
.Include(j=>j.WaterBody)
.Where(j=>j.Site.SiteID==id)
.OrderByDescending(j=>j.BookingDate)
select j;
int pageSize = 9;
return View(await PaginatedList<Job>.CreateAsync(jobs.AsNoTracking(), page ?? 1, pageSize));
}
This is returning the correct records all good.
I then have a view set up:
<h2> Site Jobs History</h2>
<p>
<a asp-action="Create">Add New Job</a>
</p>
<table class="table">
<thead>
<tr>
<th>Booking Date</th>
<th>Job Number</th>
<th>Waterbody</th>
<th>Job Description</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>#Html.DisplayFor(modelItem => item.BookingDate)</td>
<td>#Html.DisplayFor(modelItem => item.JobNumber)</td>
<td>#Html.DisplayFor(modelItem => item.WaterBody.WBName)</td>
<td>#item.JobDescription.Substring(0, Math.Min(item.JobDescription.Length, 30))</td>
<td>
<a asp-action="Edit" asp-route-id="#item.JobID">Edit</a> |
<a asp-action="Details" asp-route-id="#item.JobID">Details</a> |
<a asp-action="Delete" asp-route-id="#item.JobID">Delete</a> |
</td>
</tr>
}
</tbody>
</table>
This is working wellish so far.
All I want to do is add something like:
#Html.DisplayFor(ModelItem=>item.Site.SiteName)
To the <h2> element. I know this wont work as typed, thanks for thinking that.
I just cant see a way to add it. I considered ViewData, but may be using it wrong as I cant get it to populate with SiteName.
Is there a way to do this or am I thinking all ass about as usual?
The easiest change would be to use this:
<h2>#Html.DisplayFor(m => m[0].Site.SiteName);</h2>
Other options:
Is there any reason why you can't use the ViewBag?
In controller:
ViewBag.SiteName = Site.name
In view:
<h2>#ViewBag.SiteName</h2>
If you must use your model to pass the whole site object then change your view model that you pass to the view.
You are currently returning a list of jobs with the site object for each job, but it looks like you only need it once.
I would change your view model to be something like:
public class SiteJobsHistoryModel
{
public Site Site { get; set;}
public PaginatedList<Job> Jobs { get; set; }
}
Then you don't have to include the site on your query, and just retrieve it once from the database:
var site = _context.Site.Single(j => j.Site.SiteID==id);
var jobs = from j in _context.Job
//.Include(j => j.Site) -- this can be removed
.Include(j=>j.WaterBody)
.Where(j=>j.Site.SiteID==id)
.OrderByDescending(j=>j.BookingDate)
select j;
return View(new SiteJobsHistoryModel
{
Site = site,
Jobs = await PaginatedList<Job>.CreateAsync(jobs.AsNoTracking(), page ?? 1, pageSize)
});
Then for the title in <h2> tag you can use:
#Html.DisplayFor(ModelItem=>model.Site.SiteName)
And your foreach loop becomes:
#foreach (var item in Model.Jobs)
I have this view based on a list of a model where I create strongly-typed checkboxes for each items of the model based on a boolean.
Here's my view:
#using MyApp.Models
#model IList<MyApp.Models.ObjInfo>
#{
ViewBag.Title = "Obj Inventory";
}
<h2>Search Inventory</h2>
<p>
#using (Html.BeginForm())
{
(Many search filters which are non-relevant)
<p>
Send Items: #Html.ActionLink("Click Here", "SendItems")
</p>
}
<table>
<tr>
<th>
Obj Name
</th>
<th>
Number In Stock
</th>
(...)
<th>
Select Item
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.OtherObj.m_Name)
</td>
(...)
<td>
#Html.CheckBoxFor(modelItem => item.m_IsSelected)
</td>
</tr>
}
</table>
The whole process works fine and I can actually generate a view with checkboxes for each item of my list of model.
Now my question is that I want to create a list which would regroup only the items in the list which are checked and send them to the controller. How could I do that? Can anyone help me or suggest me a way to work?
Thank you!
* EDIT *
Here is the HttpPost Method used to get the List of items as mentioned below:
//
// GET: /Inventory/SendItems
[HttpPost]
public ActionResult SendItems(IList<ObjInfo> listToSend)
{
m_ListObjToSend = new List<ObjInfo>();
foreach (var item in listToSend.Where(item => item.m_IsSelected))
{
m_ListObjToSend .Add(item);
}
return View(m_ListObjToSend );
}
However I have encountered many problems:
This method does NOT work if I put the [HttpPost] attribute (it will show as "Not Found");
The list I am supposed to receive is null;
Each hiddenfield linked with the checkbox has default value as false even if the checked value shows true;
I am using an actionlink because I do not want to use a button, there is already one that is doing another job.
I am open for any comments / help available, thank you!
If you use the CheckBoxFor helper to generate checkboxes you will notice that it generates an additional hidden field along with each checkbox. This means that all values will be sent to the controller and you will have to filter in your controller those that are checked.
Also I would recommend you using indexes to ensure proper model binding. You just need to use an IList<ObjInfo> or ObjInfo[] which is trivially easy achievable by calling .ToList() or .ToArray() extension methods on your view model before passing it to the view:
#using MyApp.Models
#model IList<ObjInfo>
...
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(x => x[i].OtherObj.m_Name)
</td>
(...)
<td>
#Html.CheckBoxFor(x => x[i].m_IsSelected)
</td>
</tr>
}
...
And now your controller action could directly take the list of items:
[HttpPost]
public ActionResult SomeAction(IEnumerable<ObjInfo> model)
{
...
}
and if you wanted to find the selected values, you could simply get them through LINQ:
[HttpPost]
public ActionResult SomeAction(IEnumerable<ObjInfo> model)
{
var selectedItems = model.Where(x => x.m_IsSelected);
...
}
Remark: m_Name and m_IsSelected is a disastrously bad naming convention for a properties in C#.
UPDATE:
Another issue you have with your code is that your Html.BeginForm doesn't contain any input field. It has only a single ActionLink which obviously only does a GET request. If you want to submit the values you should wrap your entire table with the form and use a submit button and not some action links:
#using MyApp.Models
#model IList<ObjInfo>
#{
ViewBag.Title = "Obj Inventory";
}
<h2>Search Inventory</h2>
<p>
#using (Html.BeginForm("SendItems", null, FormMethod.Post))
{
(Many search filters which are non-relevant)
<table>
<tr>
<th>Obj Name</th>
<th>Number In Stock</th>
(...)
<th>Select Item</th>
</tr>
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>
<!--
This will not be sent to your controller because it's only a label.
You will need a corresponding hidden field if you want to get that value back
-->
#Html.DisplayFor(x => x[i].OtherObj.m_Name)
</td>
(...)
<td>
#Html.CheckBoxFor(x => x[i].m_IsSelected)
</td>
</tr>
}
</table>
<p>
Send Items: <button type="submit">Click Here</button>
</p>
}
</p>
So really, 2 things you should learn:
The naming convention that the default model binder expects when binding to a list
How to use a javascript debugging tool (such as FireBug and/or Chrome Developper Toolbar) which will allow you to inspect all the values that are sent to your server and immediately recognized whether you respected the convention you learned in 1.