Iterate through List using ASP.NET Razor - c#

I am testing if I iterate correctly trough my list of results. To do this i created a PartialView which creates a new Div with the word 'Nice'. As you can see in the image I know for sure that there are 100 results but it seems that the Foreach loop doesn't work. Should I iterate in a different way?
Webresult:
Code:
Controller (FeedController.cs)
public ActionResult Index()
{
return View();
}
public ActionResult _Feed()
{
return PartialView(getStatusses());
}
private List<LinqToTwitter.Status> getStatusses()
{
//Code to get tweets
}
View (Index.cshtml)
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<div>
#{
Html.Action("_Feed");
}
</div>
PartialView (_Feed.cshtml)
#model List<LinqToTwitter.Status>
<div id="FeedPosts">
#foreach (var item in Model)
{
<div>
Nice
</div>
}
</div>

When calling Html.Action(), you need to instruct Razor to append the results of the action to the output using the # symbol:
#Html.Action("_Feed");
Otherwise, it simply returns an MvcHtmlString that you don't use.
This applies even when the call is made inside a code block (#{...}):
#{
#Html.Action("_Feed");
}
See MSDN

Related

ViewComponent fired but no output appears on page

I've got a navbar at the top of my ASP.Net CORE site and I'm trying to create dynamic menu options. So far, I'm just trying to replace some of the options on the _layout page with a ViewComponent like I've done in a few other projects.
<header>
<nav class="navTop">
<a class="logo" asp-controller="Home" asp-action="Index">
<img src="~/Images/logo.svg" />
</a>
#{ await Component.InvokeAsync("MainMenu"); }
#{ await Html.RenderPartialAsync("_LoginPartial"); }
</nav>
</header>
I have my Component View in the folder
Views > Shared > Components > MainMenu > Default.cshtml
Which looks like this:
#model IEnumerable<MyProject.Models.Home.MainMenuItem>
#foreach (var item in Model)
{
#Html.ActionLink(item.DisplayText, item.Action, item.Controller)
}
And the ViewComponent InvokeAsync looks like:
public async Task<IViewComponentResult> InvokeAsync()
{
var x = new DataAccess.Menus.Menu(_context).GetMainMenuItems(User.Identity.Name);
return View(x);
}
And the DataAccess method is simply returning a List of MyProject.Models.Home.MainMenuItem as expected, which has everything populated fine when I step through, but nothing is resulting on the page in the in place of #{ await Component.InvokeAsync("MainMenu"); }!
I've done this before in a few other projects and never had a problem - so I don't know why this could be going wrong. Any ideas?!
It's getting to the view and I can even see it stepping through the foreach loop with the correct populated properties so why does nothing show up on the page?
You should write it like this
#{ #await Component.InvokeAsync("MainMenu") }
Or directly
#await Component.InvokeAsync("MainMenu")

I don't understand why I have null MVC AJAX

I have Get and Post partial Action. Get take me a list of image which I have in ma app.
[HttpGet]
public PartialViewResult ViewImageFileList()
{
IEnumerable<string> allImages = Directory.EnumerateFiles(Server.MapPath("~/Images/NBAlogoImg/"));
return PartialView(allImages);
}
Post delete image which I extra.
[HttpPost]
public PartialViewResult ViewImageFileList(string imageNameType)
{
var fileToDeletePath = Path.Combine(Server.MapPath("~/Images/NBAlogoImg/"), imageNameType);
if (System.IO.File.Exists(fileToDeletePath))
{
fileOperations.Delete(fileToDeletePath);
}
return PartialView();
}
My .chhtml of my partial view
#model IEnumerable<string>
<div class="name-block-style">
Логотипы которые имеются
</div>
<div id=team-logo-wrapper-images>
<ul>
#foreach (var fullPath in Model)
{
var fileName = Path.GetFileName(fullPath);
<li>
<div class="box-name-image">
<p class="image-name-type">#fileName</p>
<img src="#Url.Content(string.Format("~/Images/NBAlogoImg/{0}", fileName))"
class="logo-images" alt="Логотип команды"
title="Логотип команды" />
</div>
</li>
}
</ul>
<div id="delete-image-form" class="form-group">
#using (Ajax.BeginForm(
"ViewImageFileList",
"Team",
new AjaxOptions() { HttpMethod = "POST", OnComplete = "reloadPage()" }))
{
<label>Введите имя с указание типа изображения</label>
<input type="text" class="form-group" name="imageNameType" id="imageNameType" />
<input type="submit" value="Удалить" class="btn btn-primary" />
}
</div>
<script>
function reloadPage() {
location.reload();
}
</script>
My problem is Null references when I write the deleting image and submit it(i do it by ajax). I have this error Null reference but when I click to continue, the image deleted and my script to reload page work.
I want to understand why I take the null and how I can fix it, because it stops my app always when I delete an image.
The problem is that when you POST after you delete the image you don't populate the model of the partial view, as you do correctly in ViewImageFileList. This has a result when the View Engine try to build the view that you would send after the POST to the client, to get a null reference exception when try to perform the foreach on a null reference.
That being said, the thing you need is to pass to the PartialView all the images. So just add before the return statement in the action method you POST this:
var allImages = Directory.EnumerateFiles(Server.MapPath("~/Images/NBAlogoImg/"));
return PatialView(allImages);
When you browsing images you return view with model passed
return PartialView(allImages); //allImages is a model
But when you deleting images you return view without any model
return PartialView(); //need to pass a model
So after deleting you would like to redirect to ViewImageFileList to browse
all images
[HttpPost]
public RedirectToRouteResult ViewImageFileList(string imageNameType)
{
var fileToDeletePath = Path.Combine(Server.MapPath("~/Images/NBAlogoImg/"), imageNameType);
if (System.IO.File.Exists(fileToDeletePath))
{
fileOperations.Delete(fileToDeletePath);
}
return RedirectToAction("ViewImageFileList");
}
or retrieve images in delete action once again and pass the list to view
[HttpPost]
public PartialViewResult ViewImageFileList(string imageNameType)
{
var fileToDeletePath = Path.Combine(Server.MapPath("~/Images/NBAlogoImg/"), imageNameType);
if (System.IO.File.Exists(fileToDeletePath))
{
fileOperations.Delete(fileToDeletePath);
}
IEnumerable<string> allImages = Directory.EnumerateFiles(Server.MapPath("~/Images/NBAlogoImg/"));
return PartialView(allImages);
}

PagedList.MVC - Page buttons(links) don't work as expected

I wanted to implement a simple pagination, and PagedList.MVC NuGet package sounded like the best solution for me. HOWEVER, when I click on generated buttons to go to 2nd, 3rd, etc. page, 1st one remains active, and all that happens is refresh of the first page, but I obviously want it to navigate to the expected page...
I followed these two tutorials to see if I've done everything right:
Github
Microsoft
My controller:
public ActionResult Index(int? pageNumber)
{
var modelList = _employeeService.GetEmployeeViewToPagedList(pageNumber);
return View(modelList);
}
The service method that gets called (I know that "ToPagedList()" is usually called from the controller, but the current state is a result of trying everything, and the fact that I get "DbContext disposed" error if I modify to return something like "View(modelList.ToPagedList(pageNumber, pageSize))" from the controller):
public IPagedList<EmployeeView> GetEmployeeViewToPagedList(int? pageNumber)
{
using (var _unitOfWork = UnitOfWork.GetUnitOfWork())
{
var list = (IQueryable<EmployeeView>)_unitOfWork.context.EmployeeViews.OrderByDescending(x => x.Id);
return list.ToPagedList((pageNumber ?? 1), 10);
}
}
My view:
#model PagedList.IPagedList<Company.DAL.Views.EmployeeView>
#using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#{
ViewBag.Title = "Index";
}
<h2>List of all employees</h2>
<p>
#Html.ActionLink("Add new employee", "AddNewEmployee")
</p>
#if (Model != null && Model.Count() > 0)
{
<table class="table">
... all needed <tr>'s, <th>'s, <td>'s ...
</table>
<br/>
#Html.PagedListPager(Model, page => Url.Action("Index", new { page, pageSize =
Model.PageSize }))
}
I am trying to figure this out for days now, and the closest I got was this question, but I am not sure where to find that JS function, so I could try that as well.
EDIT:
Generated HTML:
<div class="pagination-container">
<ul class="pagination">
<li class="active"><a>1</a></li>
<li>2</li>
<li>3</li>
<li class="PagedList-skipToNext">»</li>
</ul>
</div>
I decided to post an answer here, since I solved the problem, and somebody else might find this useful.
So, in the controller, my Index method looks like this:
public ActionResult Index(int? pageNumber)
{
//some logic
}
As you can see, it accepts an int variable named pageNumber as a parameter.
But then there's this on my view:
#Html.PagedListPager(Model, page => Url.Action("Index", new { page, pageSize = Model.PageSize }))
SO, here I am passing a variable named page to my Index method.
That's the mistake! Variable in the method parameter list has to be named page as well.

Best way to handle add/view/delete on one page

What I want to do
I am very new to MVC.
I'm trying to create a page that allows users to perform the following actions on the same page:
View the list (table)
Add a new item (Filling the form and clicking the Add button should update the table)
Delete an item from the list (Clicking the Delete button in a row should update the table)
A simple example looks like this but I actually have two lists on one page (Fees and Costs):
Question
What would be the best way to achieve this?
Should I go with Dylan Beattie's method posted here which would look something like this?
public ActionResult MyAction(string submitButton, MyViewModel form)
{
switch (submitButton)
{
case "AddFee":
return (AddFee(form));
case "AddCost":
return (AddCost(form));
case "RemoveFee":
return (RemoveFee(form));
case "RemoveCost":
return (RemoveCost(form));
}
}
public ActionResult AddFee(MyViewModel form)
{
Fee newFee = ....; // Get entered data from `form`
_repository.InsertFee(newFee);
return View("Create"); //Back to the original page
}
Or is there any other recommended methods to handle this such as using JavaScript?
You could create the table as a partial view and re render this via ajax.
Wrap the partial view in a div and Wrap the form in #using (Ajax.BeginForm(.... and target the wrapper div. Your controller action that is targeted by the ajax request will need to return a partial view.
Here is a simple example
public class HomeController : Controller
{
public ActionResult Index()
{
MYvm vm = new MYvm() { id = 1, name = "This is my View Model" };
return View(vm);
}
public ActionResult DA(MYvm vm)
{
vm.name = "CHANGED";
return PartialView("Part", vm);
}
View:
#model MvcApplication1.Controllers.HomeController.MYvm
#{
ViewBag.Title = "Home Page";
}
#using (Ajax.BeginForm("DA", "Home", new AjaxOptions() { UpdateTargetId = "cont", HttpMethod = "Get" }))
{
<div>
Id: #Html.EditorFor(model => model.id)
</div>
<div>
Name: #Html.EditorFor(model => model.name)
</div>
<input type="submit" value="SubmitForm" />
}
<div id="cont">
#{Html.RenderPartial("part", Model);}
</div>
Partial View
#model MvcApplication1.Controllers.HomeController.MYvm
#{
ViewBag.Title = "part";
}
<h2>part</h2>
#Model.name
Should I go with [previous SO answer]
No. That answer was for a different scenario where the question had a form with two submit buttons that wanted to do two different actions (and wasn't even the accepted answer to that question).
Your sample screenshot indicates that some javascript/jquery and ajax would solve the issue cleanly.
As you're new to MVC, try to keep it relatively simple. Break up the page into separate parts:
the containing page
the edit form
the list with remove
the edit/list work independently and should be written in a way that they could be put on any other page - the page is just there to contain them and doesn't do much else (obviously your real page will contain more, but add those parts as separate components as well).
1 Create actions for your list and edit forms that return partialviews - just the parts that are needed for that view (self-contained)
controller:
[HttpGet]
public ActionResult AddCost()
{
var model = new Cost();
return PartialView(model);
}
[HttpPost]
public void AddCost(Cost model)
{
if (ModelState.IsValid) {
db.SaveCost(model);...
}
}
form Views/Home/AddCost.cshtml:
#using (Ajax.BeginForm(...
{
<div class='editor-label'>#Html.LabelFor(model=>model.Description)</div>
...etc...
}
I'll leave you to set the Ajax.BeginForm properties. But make sure the on-success calls reloadCostList() (see below)
controller
public ActionResult CostList()
{
var model = db.loadCosts(); ...
return PartialView(model);
}
list, Views/Home/CostList.cshtml
#model IEnumerable<ViewModels.Cost>
<table>
<thead>
<tr>
<th>Cost Description</th>
...
<tbody>
#foreach (var cost in Model.Costs)
{
<tr data-id='#cost.Id'>
<td>#Html.DisplayFor(x=>cost.Description)</td>
...
<td><a href='#' class='remove-button'>Remove</a></td>
}
...
2 Create an action + view for the main page with placeholder for the form and calls the list partial-action, eg:
<div id="body">
<div id="formWrapper">
#Html.Action("AddCost")
</div>
<div id="listWrapper">
#Html.Action("ListView")
</div>
</div>
if you already load the data for the page, you can pass it directly to the partial, but there's no need:
#Html.Partial("ListView", Model.Costs)
this allows you to refresh the list via an ajax call, something like:
function reloadCostList() {
$(".listWrapper").load("Home/CostList");
}
(ideally, $.ajax and add some fancy UI to indicate loading)
3 Add a remove action to your controller
[HttpPost]
public void RemoveCost(int id)
{
}
4 Wire up the Remove link
$(function() {
$(".remove-button").click(function() {
var id = $(this).closest("tr").attr("id");
$.post("/Home/RemoveCost/" + id, null, function() {
$(".listWrapper").load("Home/CostList");
// or reloadCostList(); from above
// or:
//$(".listWrapper tr[id=" + id + "]").hide();
});
});
}
rather than re-load the entire list, you could just remove the row (add some fancy UI like fade-out...)

accessing controller method on button click

I am new to C# and MVC, while I understand the Controller and the Model side. I have encountered a problem when accessing methods within a controller in order to do a simple conversion that I can then return to my View.
My Controller:
public class Exercise05Controller : Controller
{
//
// GET: /Exercise05/
public ViewResult Index()
{
return View();
}
public ActionResult GramsToOunces(double? grams)
{
ViewData["grams"] = grams;
ViewData["ounces"] = (grams * 0.035d);
if (grams < 5)
{
return RedirectToAction("Index", "Home");
}
else if (grams > 5)
{
return View("GramsToOunces");
}
return RedirectToRoute(new
{
controller = "Exercise05",
action = "Index"
});
}
}
My Index View:
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<form action="/Exercise05/GramsToOunces" method="post">
<input name="grams" type="text" placeholder="Insert grams to convert to ounces" />
<input type="submit" value="Convert Grams to Ounces" />
</form>
My GramsToOunces View:
#{
ViewBag.Title = "Index";
}
<h2>GramsToOunces</h2>
<!-- Currently nothing else here -->
I believe my issue is arising somewhere on this line; action="/Exercise05/GramsToOunces". Using debugging has shown me that the the controller processes all the information and gets ready to return the view to suddenly just not return anything. I wish to do a simple conversion and then return this as a view. Would someone be able to point me as to where I am going wrong?
As long as your view is named the same as the controller action, try:
#using (Html.BeginForm())
{
<input name="grams" type="text" placeholder="Insert grams to convert to ounces" />
<input type="submit" value="Convert Grams to Ounces" />
}
This is just a sanity check, but you are using the line return View("GramsToOunces", "ounces");. According to the docs http://msdn.microsoft.com/en-us/library/dd470743(v=vs.118).aspx, first string is the view name and second string is the master page. Do you have a master page called "ounces"? If not that could explain why no result is returning. Perhaps you want to use return View("GramsToOunces");
If you simply want to do the conversion and show the result to user. Just make it Ajaxified. Do not post the entire form.Make an ajax call and get the result and show it to user.
Assuming you have jQuery loaded to your form.
#using(Html.BeginForm("GramsToOunce","Exercise5"))
{
<input name="grams" id="gram" type="text" placeholder="Insert grams" />
<input type="submit" id="btnSubmit" value="Convert Grams to Ounces" />
<div id="result"></div>
}
<script type="text/javascript">
$(function(){
$("#btnSubmit").click(function(e){
e.preventDefault();
var url=$(this).closest("form").attr("action");
$.post(url,{ grams : $("#gram").val()} ,function(res){
$("#result").html(res);
});
});
});
</script>
Let's make sure that our action method returns the value.
[HttpPost]
public string GramsToOunces(double? grams)
{
var result=grams * 0.035d;
return result.ToString();
}
Also if you simply want to do the multiplication, just do it in the client side(javascript). No need of a server call.
According to the documentation this line:
return View("GramsToOunces", "ounces");
Tries to return the view with the specified master page. However it seems there is no master page "ounces" in your project. So most likely what you need here is simply:
return View("GramsToOunces");
Note that you do not need to pass any models whatsoever because you are already using the ViewData.

Categories

Resources