Partial View not loading data - c#

I am trying to load a partial view of inside a tab but its not showing data.
I am using the following code can I not just do a loop using razor code this is in a partial view which I wish to load in from another view
#model IEnumerable<solitude.models.ProductImages>
#{
ViewData["Title"] = "ProductPicturesList";
Layout = "~/Views/Shared/_LoginAdminLte.cshtml";
}
<h2>ProductPicturesList</h2>
<table class="table">
<thead>
<tr>
<th>
Picture Title
</th>
<th>
Image
</th>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
</tr>
<td>
<a asp-action="Edit" asp-route-id="#item.ProductID">Edit</a> |
<a asp-action="Details" asp-route-id="#item.ProductID">Details</a> |
<a asp-action="Delete" asp-route-id="#item.ProductID">Delete</a>
</td>
}
</tbody>
</table>
Its cause in the main list I am using a view model but I want to show a list of pictures above the form upload what would my best way of doing this be as obv it is not returning anyresults I am using a controller for my main page.
#model solitude.models.Models.ViewModels.ProductImageVm
#*
For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
*#
#Html.PartialAsync("_ProductPicturesList.cshtml")
<div class="form-group">
<form asp-controller="Products" asp-action="FileUpload" asp-route-returnurl="#ViewData["ReturnUrl"]" enctype="multipart/form-data" method="post" class="form-horizontal" role="form">
<input asp-for="Title" />
<input asp-for="ProductId" type="hidden" />
<input asp-for="Image" />
<input type="submit" />
</form>
Edit 2
My Product Images as a class should this be changed
public class ProductImages
{
[Key]
public int ProductImageId { get; set; }
public int ProductID { get; set; }
public string ProductImageTitle { get; set; }
public string ProductImageUploadUrl { get; set; }
public string ProductImageRealPath { get; set; }
public string ServerIpAddress { get; set; }
public string ProductImageAltTag { get; set; }
public int DisplayOrder { get; set; }
public string Image { set; get; }
}
}

Your partial view is strongly typed to a collection of ProductImages. But in your main view when you are calling this partial view, you are not passing the model (which is the collection of ProductImage objects) to this partial view. If you are not explcitily passing the model, it will try to use the model for the parent view. In your case your parent view is strongly ProductImageVm view model class. So it is not maching with what the partial view is expecting.
The solution is to pass a valid collection of ProductImages. If your view model has a collection property of that type you can do that
#await Html.PartialAsync("_ProductPicturesList.cshtml",Model.Images)
Assuming Images of type IEnumerable<solitude.models.ProductImages>
Ideally it is not a great idea to mix entity classes with view models. So i would create a view model class for the ProductImage partial view and use that as the property
public class ProductImg
{
public string Title { set;get;}
public string FileName { set;get;}
// or path as needed
}
public class EditProductImageVm
{
public string Title { set;get;} //for the new item
public IFormFile Image { set;get; } //for the new item
public IEnumerable<ProductImg> Images { set;get;}
}
Now make sure main view is not strongly typed to EditProductImageVm and your partial view is strongly typed to IEnumerable<ProductImg>. Also you need to await the call to PartialAsync method
#model YourNameSpaceGoesHere.EditProductImageVm
<div>
#await Html.PartialAsync("_ProductPicturesList.cshtml",Model.Images);
</div>
<form asp-controller="Products" asp-action="FileUpload" enctype="multipart/form-data"
method="post" >
<input asp-for="Title" />
<input asp-for="Image" />
<input type="submit" />
</form>
And your partial view will be
#model IEnumerable<YourNameSpaceGoesHere.ProductImg>
<h3>Images</h3>
<table class="table table-condensed table-bordered">
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
<!-- adjust the below img src path as needed -->
<img src="#Url.Content("~/uploads/"+item.FileName)"/>
</td>
</tr>
}
</table>

Related

Return the same ViewData twice from controller to View result in NULL

Hello guys I am still really new to Asp.net and C#. I am writing a management system overseeing office activities. I ran into a problem where I am passing a ViewModel Object that contains a list, Case and int to the view from the controller. If the newly created case has the same ID, it would return the same ViewModel Object back to the POST controller, but when the object got passed from view to the post controller,every list Object is null except for primitive types and the Case. I dont understand why the same object is passed around but when it passes to the controller from the view all the list that was just used to populate the html are null, is this because of injection or just how model binding works? THanks a lot
The View just has a for loop displaying every item in the TopTenList. it has the #model binding statement on the top.
Heres the View
#model LabManager.InputModels.CaseInputModel
#inject LabManager.Data.ApplicationDbContext _dbContext
#using LabManager.ModelExtensions
#{
ViewData["Title"] = "Overview";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#section Stylesheet{
<link rel="stylesheet" href="~/css/Case/index.css" />
}
<div id="home-view">
<div class="card">
<div class="card-header">
<a class="card-link" data-toggle="collapse" href="#topTen">
Top Ten Priority Cases
</a>
</div>
<div id="topTen" class="collapse show" data-parent="#home-view">
<div class="card-body">
<table class="table">
<thead>
<tr>bunch of names</tr>
</thead>
<tbody>
#{int i = 1;}
#foreach (var item in Model.TopTenList)
{
show somethings.
}
Heres the controller.
public ActionResult Create(int setPriority)
{
CaseInputModel input = new CaseInputModel {
SelectedPriority = setPriority,
};
input.SetTopTenList(_dbContext);
return View(input);
}
// POST: Takes info from the input model and creates a new case in the db
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CaseInputModel inputs)
{
//if case id already exist in the database, return view()
if (DbContext.Cases.Any(m => m.CaseId == inputs.NewCase.CaseId))
{
inputs.ErrorMessage = "Error: Case ID already exists";
return View(inputs);
}
Hers the Model:
public class Case
{
[Required]
[RegularExpression("[^0-9]")]
public int Id { get; set; }
public int CaseId { get; set; }
public Status Status { get; set; }
public string Notes { get; set; }
}
Heres the CaseInputModel(ViewModel Object)
public class CaseInputModel
{
public CaseInputModel()
{
NewCase = new Case();
RegularCaseList = new List<Case>();
}
public string ErrorMessage { get; set; }
public int SelectedPriority { get; set; }
public Case NewCase { get; set; }
public List<Case> RegularCaseList { get; set; }
public List<Case> TopTenList { get; set; }
public void SetTopTenList(ApplicationDbContext dbContext)
{
TopTenList = PriorityNodeExtensions.ToSortedList(dbContext.TopTens,
dbContext);
}
Firsly,you need know that for each property of the complex type, model binding looks through the sources for the name pattern prefix.property_name. If nothing is found, it looks for just property_name without the prefix.
Reference:
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-3.1#complex-types
Here is a simple demo about how to pass the data to the controller:
View:
#model InputModel
<form asp-action="Create">
<table class="table">
<thead>
<tr>bunch of names</tr>
</thead>
<tbody>
<tr>
<td>SelectedPriority</td>
<td><input asp-for="#Model.SelectedPriority" /></td>
</tr>
<tr>
<td>New Case</td>
<td><input asp-for="#Model.NewCase.Id" /></td>
<td><input asp-for="#Model.NewCase.CaseId" /></td>
<td><input asp-for="#Model.NewCase.Notes" /></td>
</tr>
#{int i = 0;}
#foreach (var item in Model.TopTenList)
{
<tr>
<td>TopTenList_#i</td>
<td><input name="TopTenList[#i].Id" asp-for="#item.Id" /></td>
<td><input name="TopTenList[#i].CaseId" asp-for="#item.CaseId" /></td>
<td><input name="TopTenList[#i].Notes" asp-for="#item.Notes" /></td>
</tr>
i++;
}
</tbody>
</table>
<input type="submit" value="create" />
</form>
Result:
Update:
return view with model it could render the model successfully,but you could not render the TopTenList.Be sure you have create the input for the model properties and debug the action to check if you have passed the TopTenList to the post method.If you only render the data by using #Model.Property it would not pass to the backend.
View:
#model CaseInputModel
<form asp-action="Create">
<table class="table">
<thead>
<tr>bunch of names</tr>
</thead>
<tbody>
#if (Model.ErrorMessage != null)
{
#Model.ErrorMessage
}
<tr>
<td>SelectedPriority</td>
<td><input asp-for="#Model.SelectedPriority" /></td>
</tr>
<tr>
<td>New Case</td>
<td><input asp-for="#Model.NewCase.Id" /></td>
<td><input asp-for="#Model.NewCase.CaseId" /></td>
<td><input asp-for="#Model.NewCase.Notes" /></td>
</tr>
#{int i = 0;}
#foreach (var item in Model.TopTenList)
{
<tr>
<td>
#item.Id
<input name="TopTenList[#i].Id" asp-for="#item.Id" hidden />
</td>
<td>
#item.CaseId
<input name="TopTenList[#i].CaseId" asp-for="#item.CaseId" hidden />
</td>
<td>
#item.Notes
<input name="TopTenList[#i].Notes" asp-for="#item.Notes" hidden />
</td>
</tr>
i++;
}
</tbody>
</table>
<input type="submit" value="create" />
</form>
Result:

Can't pass an enumerable model to a controller?

I'm a bit confused because I thought this a very straight-forward thing, it's possibly something simple tripping me up.
I have a view:
#model IEnumerable<CarViewModel>
#using (Html.BeginForm("SummarySaveAll", "VroomVroom", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div>
<table>
<thead>
<tr>
<th width="1">
#Html.DisplayNameFor(model => model.Driver)
</th>
<th width="1">
#Html.DisplayNameFor(model => model.Colour.Name)
</th>
</tr>
</thead>
<tbody>
#foreach (var element in Model)
{
<tr>
<td width="1">
#Html.DisplayFor(m => element.Driver)
</td>
<td width="1">
#Html.DropDownListFor(m => element.Colour, element.Colours, "Unknown")
</td>
</tr>
}
</tbody>
</table>
<div>
<input type="submit" value="Save Changes" class="btn" />
#Html.ActionLink("Cancel Changes", "Index", null, new { #class = "btn" })
</div>
</div>
}
and the list/enumerable of CarViewModel is supposed to bounce back to the VroomVroom controller, action SummarySaveAll which it does - but the viewmodel on the page doesn't get passed back to it:
[HttpPost]
public ActionResult SummarySaveAll(IEnumerable<CarViewModel> summaries)
{
// Want to do stuff with summaries but it's always null
return View();
}
I tried to encapsulate the List in another ViewModel and cycle through elements using a for i loop but that wouldn't pass back to the controller either.
Surely it's possible to send a List or IEnumerable of models back to a controller?
My CarVM:
public class CarViewModel
{
[MaxLength(150)]
[Display(AutoGenerateField = true, Name = "Entered By")]
public string Driver { get; set; }
[Display(AutoGenerateField = true)]
public Colour Colour { get; set; }
[Key]
[Display(AutoGenerateField = false)]
public int Id { get; set; }
[Display(AutoGenerateField = false)]
public bool IsDeleted { get; set; } = false;
public IEnumerable<SelectListItem> Colours { get; set; }
public CarViewModel() { }
public CarViewModel(Model CarModel summaryModel, CarPropertyCollection propertyCollection)
{
Driver = summaryModel.Driver;
Id = summaryModel.Id;
IsDeleted = summaryModel.IsDeleted;
Colour = summaryModel.Colour == null ? null :
propertyCollection.Colours.Where(x => x.Id == summaryModel.Colour.Id).FirstOrDefault();
Colours = propertyCollection.Colours.Select(x => new SelectListItem { Value = x.Id.ToString(), Text = x.Name });
}
}
}
Must stress that Colour is a custom class but only has Id and Name properties
Colours doesn't relate to a specific car, it relates to cars in general, so rather than using a collection as your view model, create a wrapper:
class EditCarsViewModel
{
public IEnumerable<SelectListItem> Colours { get; set; }
public IList<CarViewModel> Cars { get; set; }
}
Then your view:
#model EditCarsViewModel
#for (int i = 0; i < Model.Cars.Length; i++)
{
<td>
#Html.DropDownListFor(m => Model.Cars[i].Colour, Model.Colours, "Unknown")
</td>
}
Any other CarViewModel properties will need their own input as well. HiddenFor can be used if they should be readonly:
#model EditCarsViewModel
#for (int i = 0; i < Model.Cars.Length; i++)
{
#Html.HiddenFor(m => Model.Cars[i].Id)
#Html.HiddenFor(m => Model.Cars[i].Driver)
<!-- etc. -->
<td>
#Html.DropDownListFor(m => Model.Cars[i].Colour.Id, Model.Colours, "Unknown")
</td>
}
And your controller:
[HttpPost]
public ActionResult SummarySaveAll(EditCarViewModel model)
{
// model.Cars should be populated
return View();
}
Note that an indexable collection, such as IList<T> should be used, as the form field names need to include the index to differentiate the items.
Edit by OP
The Colour class consists of a [Key] int Id property and a string Name property. For DropDownList items I had to make sure the Id property was specified on the m => Model.Cars[i].Colour.Id line otherwise that particular prop was coming back as null even though other items were coming through fine.
try
[HttpPost]
public ActionResult SummarySaveAll(IList<CarViewModel> summaries)
{
// Want to do stuff with summaries but it's always null
return View(summaries);
}
I've also added this model as a param for your view
This how you do it:
First my View which posts back to a controller named Home and an action named ListView:
#model List<MyModel>
#{
ViewData["Title"] = "Using a list as model";
}
<h1>#ViewData["Title"]</h1>
#using (Html.BeginForm("ListView", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div>
<table>
<thead>
<tr>
<th width="1">
Name
</th>
<th width="1">
Description
</th>
</tr>
</thead>
<tbody>
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td width="1">
#Html.DisplayFor(m => Model[i].Name)
</td>
<td width="1">
#Html.TextBoxFor(m => Model[i].Description)
</td>
</tr>
}
</tbody>
</table>
<div>
<input type="submit" value="Save Changes" class="btn" />
#Html.ActionLink("Cancel Changes", "Index", null, new { #class = "btn" })
</div>
</div>
}
Notice how I used an indexer to render the controls [i]
This is my model:
public class MyModel
{
public string Name { get; set; }
public string Description { get; set; }
}
This is my controller action:
[HttpPost]
public IActionResult ListView(IEnumerable<MyModel> model)
{
return View(model);
}
And this is the result:

Model item bind in List

I have passed the model to partial view,And then i need to bind some text fields to the model in the view.
#model SRINews.Domain.NewsTotal
#using (Html.BeginForm("UpdateNewsItem", "Home", FormMethod.Post))
{
<table class="table table-borderless table-cart" id="mytable" data-addclass-on-smdown="table-sm">
<tbody>
#foreach (var item in Model.items)
{
<tr class="newsRow" id="#item.ItemId">
<td class="cart-img nostretch">
<img src="#item.ImageUrl" alt="">
</td>
</tr>
<tr>
<td>
<input type="text" class="form-control" placeholder="Personalized Name">
//#Html.TextboxFor(x=>x)
// I want to bind PersonalizedName to model
</td>
</tr>
<tr>
<td>
<input type="text" class="form-control" placeholder="Country">
// I want to bind Country to model
</td>
</tr>
}
</tbody>
</table>
<input type="submit" class="btn btn-primary" value="Personal Details" />
}
Model
public class Items
{
public int ItemId { get; set; }
public string ItemCode { get; set; }
public string PersonalizedName {get;set;}
public string Country {get;set;}
}
public class NewsTotal
{
public int BaseItem { get; set; }
public string BaseName {get;set;}
public List<Items> items { get; } = new List<Items>();
}
Public ActionResult UpdateNewsItem(NewsTotal nTotal)
{
return View();
}
You want to use a traditional for loop so you can use the index to bind to your List<T> in the model, you'll also need to make items mutable, so you'll need to have a set for it as well or else you won't be able to submit anything:
//You'll need to make this mutable, so it can post the edited values
public List<Items> items { get; set; } = new List<Items>();
Then in your View:
#for(int i = 0; i < Model.items.Count; i++)
{
#Html.HiddenFor(x => Model.items[i].ItemId)
#Html.HiddenFor(x => Model.items[i].ItemCode)
<tr class="shoppingCartRow" id="#Model.items[i].ItemId">
<td class="cart-img nostretch">
<img src="#Model.items[i].ImageUrl" alt="">
</td>
</tr>
<tr>
<td>
#Html.TextboxFor(x=> Model.items[i].PersonalizedName, new { #placeholder = "Personalized Name"})
</td>
</tr>
<tr>
<td>
#Html.TextboxFor(x=> Model.items[i].Country, new { #placeholder = "Country"})
</td>
</tr>
}

asp.net mvc insert data in database and refresh partial

Hi I have a Comment Model and i want to add some comments to database using partial view ...what i want is to refresh the partial view without refreshing all the view ..
What i get now is that when i insert data to database the date get stored in database but i have to refresh the page to see it.. the partial view do not refresh instantally.
here is my code :
//model
public partial class Commentaire
{
public int CommentaireId { get; set; }
public string TxtCommentaire { get; set; }
public System.DateTime DateCommentaire { get; set; }
}
the View Model :
public class CommentaireViewModel
{
public Commentaire NVcommentaire { get; set; }
public IEnumerable<Commentaire> Commentaires { get; set; }
}
the Index View :
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js" type="text/javascript"></script>
#using (Ajax.BeginForm("Index_AddItem", new AjaxOptions { UpdateTargetId = "productList" }))
{
<fieldset>
<legend>Commentaire</legend>
<div class="editor-label">
#Html.LabelFor(model => model.NVcommentaire.TxtCommentaire)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.NVcommentaire.TxtCommentaire)
#Html.ValidationMessageFor(model => model.NVcommentaire.TxtCommentaire)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.NVcommentaire.DateCommentaire)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.NVcommentaire.DateCommentaire)
#Html.ValidationMessageFor(model => model.NVcommentaire.DateCommentaire)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<div id='productList'>
#{ Html.RenderPartial("ProductListControl", Model); }
</div>
}
teh partialview :
<table class="table table-striped table-hover display" cellspacing="0" id="OrderTable">
<thead>
<tr>
<th>txt</th>
<th>date</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Commentaires)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.TxtCommentaire)</td>
<td>#Html.DisplayFor(modelItem => item.DateCommentaire)</td>
<td>
#Html.ActionLink("Modifier", "Edit", new { id = item.CommentaireId }) |
</td>
</tr>
}
</tbody>
</table>
and finally the controller :
public ActionResult Index()
{
CommentaireViewModel viewModel = new CommentaireViewModel
{
NVcommentaire = new Commentaire(),
Commentaires = db.Commentaires
};
return View(viewModel);
}
public ActionResult Index_AddItem(CommentaireViewModel viewModel)
{
db.Commentaires.Add(viewModel.NVcommentaire);
db.SaveChanges();
return PartialView("ProductListControl", db.Commentaires);
}
I think that your ajax request fails because of exception caused by expected Model type for partial view and received one mismatch.
In you partial View you have: #foreach (var item in Model.Commentaires) which means that your Model has a Commentaires property.
In your return statement of controller action you have: return PartialView("ProductListControl", db.Commentaires); which means that you are passing IEnumerable<Commentaire> as a Model to you view. This contradicts with what type of Model you have in partial view.
Possible solution:
Change your Model type for partial view to IEnumerable<Commentaire>
Change foreach in partial view to #foreach (var item in Model)
Change #{ Html.RenderPartial("ProductListControl", Model); } code in view to #{ Html.RenderPartial("ProductListControl", Model.Commentaires); }

C# Entity Overview Table of one to many checkbox

I can't seem to find how to build a proper solution for a one to many checkbox.
So what do i have :
I have an article and an admin user can set user rights to that article.
So in my article right page you have an overview with all the users.
My article:
public partial class Artikel
{
public Artikel()
{
this.ArtikelLinks = new HashSet<ArtikelLink>();
this.ArtikelRights = new HashSet<ArtikelRight>();
}
public int id { get; set; }
public string code { get; set; }
public string naam { get; set; }
public virtual ICollection<ArtikelLink> ArtikelLinks { get; set; }
public virtual ICollection<ArtikelRight> ArtikelRights { get; set; }
}
My rights class
public partial class ArtikelRight
{
public int id { get; set; }
public System.Guid userId { get; set; }
public int artikelId { get; set; }
public bool hasRight { get; set; }
public virtual Artikel Artikel { get; set; }
}
How do i build my view? i tried several ways but i can't seem to save my data.
this is my rights view at the moment:
#using (Html.BeginForm()) {
<table width="90%" align="center" class="table table-striped">
<thead>
<tr>
<th align="left">Gebruiker</th>
<th align="left">Heeft toegang</th>
</tr>
</thead>
#Html.EditorFor(x => Model.ArtikelRights, "ArtikelRight")
</table>
<br />
<div class="pull-right">
<input type="submit" value="Opslaan" class="btn btn-sm beige" />
</div>
}
And this is my partial Artikel right view:
#model IEnumerable<GtiProducts.Models.ArtikelRight>
#foreach (var item in Model) {
<tr>
<td>
#Membership.GetUser(item.userId).UserName
</td>
<td>
#Html.EditorFor(x => item.hasRight)
</td>
</tr>
}
My save action is:
public ActionResult Rights(Artikel art)
{
repo.SaveChanges();
return View(art);
}
When i debug my art.ArtikelRights is null.
How can i fix this and what's the best solution to do this with the entity framework?
Rename the partial to make it an EditorTemplate- /Views/Shared/EditorTemplates/ArtikelRight.cshtml, then make the following modifications
#model GtiProducts.Models.ArtikelRight // not IEnumerable
<tr>
<td>
#Html.CheckBoxFor(m => m.hasRight)</td>
#Html.LabelFor(m => m.hasRight, "....") // the users name?
#Html.HiddenFor(m > m.id) // or what ever property you need to identify the ArtikelRight.cshtml
</td>
</tr>
Then in the main view
<tbody>
#Html.EditorFor(x => Model.ArtikelRights)
</tbody>
This will correctly name you controls with indexers so they can be bound to your model on post back, for example
<input type="checkbox" name="ArtikelRights[0].hasRight" ...>
<input type="checkbox" name="ArtikelRights[1].hasRight" ...>
Side note: You should not be using #Membership.GetUser(item.userId).UserName in the view. Instead create a view model with only those properties you need in the view (which appears to be id and hasright, and include a property for the users display name (and populate it in the controller).

Categories

Resources