Using IList<T> to bind values in .cshtml - asp.net-mvc

My Controler Content is:
public ActionResult Step2()
{
Step2BusinessLogic step2BusinessLogic = new Step2BusinessLogic();
Step2ViewModel step2ViewModel = step2BusinessLogic.CreateStep2ViewModel();
return View(step2ViewModel);
}
[HttpPost]
public ActionResult Step2()
{
....
}
Step2ViewModel has a property like...
public class Step2ViewModel : MultiStepBaseViewModel
{
public IList<LayoutDetail> LayoutConfig { get; set; }
}
Business Logic Class is Like....
public class Step2BusinessLogic
{
public Step2ViewModel CreateStep2ViewModel(string Id)
{
Step2ViewModel step2ViewModel = new Step2ViewModel();
step2ViewModel.MultiStepId = new Guid(Id);
step2ViewModel.LayoutConfig = GetLayout();
return createEmailStep2ViewModel;
}
public List<LayoutDetail> GetLayout()
{
List<LayoutDetail> layoutList = new List<LayoutDetail>();
LayoutDetail layout1 = new LayoutDetail();
layout1.LayoutID = 1;
layout1.LayoutTitle = "1 Column";
layout1.LayoutImg = "~/img/create/layout/layout-1.png";
layout1.LayoutImgPrev = "img/create/layout/layout-1-preview.png";
layoutList.Add(layout1);
LayoutDetail layout2 = new LayoutDetail();
layout2.LayoutID = 2;
layout2.LayoutTitle = "1:2 Column";
layout2.LayoutImg = "~/img/create/layout/layout-2.png";
layout2.LayoutImgPrev = "img/create/layout/layout-2-preview.png";
layoutList.Add(layout2);
.........(12 Items)
return layoutList;
}
}
public class LayoutDetail
{
public int LayoutID { get; set; }
public string LayoutTitle { get; set; }
public string LayoutImg { get; set; }
public string LayoutImgPrev { get; set; }
}
On My .cshtml view i want something Like this...
#using (Html.BeginForm())
{
#Html.HiddenFor(model => model.MultiStepId)
#Html.ValidationSummary(true)
#for (int i = 0; i < 12; i++)
{
<div class="grid_3 tcenter">
<div class="divhighlight">
<div style="width: 165px; margin: 6px auto 4px auto;" class="f16 bold tcenter" id="helptrigger1">#LayoutConfig.Title</div>
<a class="fancybox" rel="layouts" href="#LayoutConfig.LayoutImgPrev" title="1 Column">
<img src="#LayoutConfig.LayoutImg" alt="1 Column" width="189" height="227" vspace="5" /></a>
<div style="width:189px; margin:auto">
<button class="button gobutton" style="margin-right: 40px; width: 165px;" value="#LayoutConfig.LayoutID">Select</button></div>
</div>
</div>
}
I want to bind the values of IList<LayoutDetail> property of Step2ViewModel to the controls of .cshtml page. I tried several other thing but could not Succeed

You are missing this #Model.LayoutConfig[i]
#for (int i = 0; i < Model.LayoutConfig.Count(); i++)
{
<div class="grid_3 tcenter">
<div class="divhighlight">
<div style="width: 165px; margin: 6px auto 4px auto;" class="f16 bold tcenter" id="helptrigger1">#Model.LayoutConfig[i].LayoutTitle </div>
<a class="fancybox" rel="layouts" href="#Model.LayoutConfig[i].LayoutImgPrev" title="1 Column">
<img src="#Model.LayoutConfig[i].LayoutImg" alt="1 Column" width="189" height="227" vspace="5" /></a>
<div style="width:189px; margin:auto">
<button class="button gobutton" style="margin-right: 40px; width: 165px;" value="#Model.LayoutConfig[i].LayoutID">Select</button></div>
</div>
</div>
}

You need use
#Model.LayoutConfig[i].LayoutImg
Instead of
#LayoutConfig.LayoutImg
And of cource for other situations
#LayoutConfig.LayoutID
#LayoutConfig.LayoutImgPrev

Related

Syncfusion Blazor ListBox Template

I recently started using Syncfusion Blazor, but I'm having trouble understanding how the Template feature of the Listbox functions. I am trying to dynamically add components to a Listbox and pass the index of each new addition to the component. However, when I add a new component, all the indexes in the previous components also get updated. I need to be able to set the index for just one component.
#page "/test"
#using Syncfusion.Blazor.Buttons
#using Syncfusion.Blazor.DropDowns
<div class="container px-5">
<SfButton #onclick="AddMedia">Add Media</SfButton>
<SfListBox TValue="int[]" DataSource="#Data" TItem="ViewMedia" #ref="MediaListBoxObj">
<ListBoxFieldSettings></ListBoxFieldSettings>
<ListBoxTemplates TItem="ViewMedia">
<ItemTemplate>
<MediaCard media="new ViewMedia { ListIndex = MediaCount }" ></MediaCard>
</ItemTemplate>
</ListBoxTemplates>
<ListBoxSelectionSettings Mode="SelectionMode.Single"></ListBoxSelectionSettings>
</SfListBox>
</div>
#code {
// public ListData Model = new ListData();
SfListBox<int[], ViewMedia> MediaListBoxObj;
public List<ViewMedia> Data = new List<ViewMedia>
{
new ViewMedia{ ListIndex = 0}
};
int MediaCount = 0;
public List<ViewMedia> NewMediaItem { get; set; } = new List<ViewMedia>();
public List<ViewMedia> NewMedia()
{
NewMediaItem.Add(new ViewMedia { ListIndex = MediaCount});
return NewMediaItem;
}
private void AddMedia()
{
var newMediaItem = NewMedia();
MediaListBoxObj.AddItems(newMediaItem);
Data.Add(newMediaItem[0]);
MediaCount++;
NewMediaItem.Clear();
}
My MediaCard file:
<div class="AddMedia">
<div name="mediaAdd" class="container-fluid px-4">
<div class="form-row align-middle mb-2 pl-1">
<SfTextBox CssClass="e-filled" Placeholder="Provide a brief description" #bind-Value="media.Blurb"></SfTextBox>
</div>
<div class="form-row">
<label class="form-check-label">
Is there any blood or gore in the video?<span class="text-danger ml-1">*</span>
</label>
<div class="row">
<div class="form-check m-2 d-inline">
<SfRadioButton Label="No" Name="#media.ListIndex.ToString()" Value="0" Checked="#media.Gore"></SfRadioButton>
</div>
<div class="form-check m-2 d-inline">
<SfRadioButton Label="Yes" Name="#media.ListIndex.ToString()" Value="1" Checked="#media.Gore"></SfRadioButton>
</div>
</div>
</div>
</div>
</div>
#code {
[Parameter]
public ViewMedia media { get; set; }
}
I asked this question on the Syncfusion forums, and they got back to me with the following code which uses Observable Collection:
<div class="container px-5">
<SfButton #onclick="AddMedia">Add Media</SfButton>
<SfListBox TValue="int[]" DataSource="#Data" TItem="ViewMedia" #ref="MediaListBoxObj">
<ListBoxFieldSettings></ListBoxFieldSettings>
<ListBoxTemplates TItem="ViewMedia">
<ItemTemplate>
#{
var data = (context as ViewMedia);
<MediaCard media="new ViewMedia { ListIndex = data.ListIndex }"></MediaCard>
}
</ItemTemplate>
</ListBoxTemplates>
<ListBoxSelectionSettings Mode="SelectionMode.Single"></ListBoxSelectionSettings>
</SfListBox>
</div>
#code {
SfListBox<int[], ViewMedia> MediaListBoxObj;
public ObservableCollection<ViewMedia> Data { get; set; }
int MediaCount = 0;
protected override void OnInitialized()
{
this.Data = ViewMedia.getListData();
}
private void AddMedia()
{
MediaCount++;
this.Data.Add(new ViewMedia() { ListIndex = MediaCount });
}
public class ViewMedia
{
public int ListIndex { get; set; }
public int Gore { get; set; }
public string Blurb { get; set; }
public static ObservableCollection<ViewMedia> getListData()
{
ObservableCollection<ViewMedia> Data = new ObservableCollection<ViewMedia>();
Data.Add(new ViewMedia() { ListIndex = 0 });
return Data;
}
}
In case the above link fails, the above code can be downloaded here

mvc validationMessage is not working while its in IEnumerable<T>

I have a class :
public class Cust
{
[Required(ErrorMessage ="NameField Req")]
public string Name { get; set; }
}
I use this class :
public class CustModel
{
public IEnumerable<Cust> CustList { get; set; }
}
.Cshtml
#model WebApplication2.Models.CustModel
#Html.EditorFor(m => m.CustList)
#Html.ValidationMessageFor(val => val.CustList)
When I hit on Submit button if (ModelState.IsValid){} is working but why my error message is not displayed ?
I managed to re-produce your code, assuming your view:
Please, pay attention to the for loop:
#model MVC_SteckOverflow.Models.ViewModels.CustModel
#using MVC_SteckOverflow.Models.ViewModels
#{
ViewBag.Title = "ValidateIenumerable";
}
<style>
.field-validation-error {
color:#F00;
}
.input-validation-error {
border: 1px solid red;
background-color: #f9e2e2b3;
}
</style>
<h2>ValidateIenumerable</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>CustModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#for (int i = 0; i != Model.CustList.Count(); i++)
{
#Html.EditorFor(x => x.CustList.ToList()[i].Name)
#Html.ValidationMessageFor(x => x.CustList.ToList()[i].Name)
}
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section scripts{
#Scripts.Render("~/bundles/jqueryval")
}
according to the following actions in the Controller:
public ActionResult ValidateIenumerable()
{
CustModel custModel = new CustModel {
CustList = new List<Cust> {
new Cust{ Name = "A"},
new Cust{ Name = "B"},
new Cust{ Name = "C"},
},
};
return View(custModel);
}
[HttpPost]
public ActionResult ValidateIenumerable(CustModel custModel)
{
return View(custModel);
}
The code above, is tested, built and run successfully.
Hope this helped ... :)

Model binder returns nullreference

#for (var i = 0; i < Model.Roles.Count; i++)
{
<div class="row" style="margin-bottom: .5rem;">
<div class="col-lg-3"></div>
<div class="col-lg-3">
<li class="list-group-item" style="background: #f5f6f7; border: none;">
#Html.Hidden("Roles[" + i + "].id", Model.Roles[i].id)
<label for="Roles_#(i)_ischecked" class="label">
#Html.CheckBox("Roles[" + i + "].ischecked", Model.Roles[i].ischecked)
#Model.Roles[i].name
</label>
</li>
</div>
</div>
}
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object
but the real object is referenced through these codes
return View(new UsersNew
{
Roles = Database.Session.Query<Role>().Select(role => new RoleCheckbox
{
id = role.id,
ischecked = false,
name = role.name
}).ToList()
})
I even referenced to my View as follow s
#model EtlGui.ViewModels.UsersNew
my ViewModel looks like this
public class UsersNew
{
public IList<RoleCheckbox> Roles { get; set; }
[Required,MaxLength(128)]
public string username { get; set; }
[Required,DataType(DataType.Password)]
public string password { get; set; }
[Required,MaxLength(256),DataType(DataType.EmailAddress)]
public string email { get; set; }
}
what am i doing wrong here?

How can i take many textboxes' value on Post in MVC

i have a problem about MVC, but first I am sorry for my english :D .
Now i am trying to make a form for users and i have a critical issue when i want connect to values with database.
My Form is like this : https://i.hizliresim.com/vJ6r2p.png
Models :
[Table("Testers")]
public class Testers
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[StringLength(50),Required]
public string testerName { get; set; }
public ICollection<Scores> Scores { get; set; }
}
[Table("Technologies")]
public class Technologies
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[StringLength(50)]
public string technologyName { get; set; }
[StringLength(50)]
public string type { get; set; }
}
[Table("Scores")]
public class Scores
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[DefaultValue(0)]
public int score { get; set; }
public virtual Testers tester { get; set; }
public virtual Technologies technology { get; set; }
}
ViewModels:
public class TechnologiesView
{
public List<Technologies> Technologies { get; set; }
public Scores Scores { get; set; }
}
Controller :
public ActionResult Page2()
{
TechnologiesView allTechs = new TechnologiesView();
allTechs.Technologies = db.Technologies.ToList();
return View(allTechs);
}
View:
#model TechnologiesView
#{
ViewBag.Title = "Page2";
}
<style>
#lang {
font-size: 15px;
color: gray;
}
#tech {
font-size: 13px;
color: gray;
}
</style>
<div class="container">
<div class="row col-xs-12 bilgi" style="color:black">
#HelperMethods.Title("Kendini Skorla!")
<br />
<i>Bilgi Düzeyini 0 ile 5 puan arasında notlar mısın? (0=Hiç 5= İleri Seviye)</i>
</div>
</div>
<hr />
#using (Html.BeginForm())
{
<div class="container-fluid" style="padding-left:50px; margin:0px">
<div class="row" id="lang">
#foreach (Technologies techs in Model.Technologies)
{
if (techs.type == "lang")
{
<div class="col-md-1 col-sm-2 col-xs-6">
#(techs.technologyName)
</div>
<div class="col-md-1 col-sm-2 col-xs-6">
(#(Html.TextBoxFor(x => x.Scores.score, new
{
id = techs.ID,
name = "techID",
style = "display:inline; width:20px; height:20px; font-size:smaller; padding:0px; text-align:center",
#class = "form-control"
})))
</div>
}
}
</div>
<hr style="color:black" />
<div class="row" id="tech">
#foreach (Technologies techs in Model.Technologies)
{
if (techs.type == "tech")
{
<div class="col-md-1 col-sm-2 col-xs-6" id="tech">
#(techs.technologyName)
</div>
<div class="col-md-1 col-sm-2 col-xs-6">
#Html.HiddenFor(x=>techs.ID)
(#(Html.TextBoxFor(x => x.Scores.score, new
{
id = techs.ID,
name = "techID",
style = "display:inline; width:20px; height:20px; font-size:smaller; padding:0px; text-align:center",
#class = "form-control"
})))
</div>
}
}
</div>
<hr />
<div class="row col-xs-12" id="lang">
<span>Kullandığınız IDE’ler (yazınız)</span>
<br />
<div style="margin-bottom:10px; text-align:center">
#HelperMethods.TextArea("Ide", 3)
</div>
</div>
<div style="text-align:right; margin-bottom:10px">
#HelperMethods.Button("btnPage2")
</div>
</div>
}
Now user has to give a score to him/herself for every technologies or languages and after this i want to when user click to button "Follow the next page(it's turkish)" i will select the last saved user from maxID value in Testers and i have to connect scores with technologies and testers but i don't know how can i get textboxes' values and which technology's value is this value on post :D
You generating form controls which have no relationship at all to your model (which is also wrong anyway). Never attempt to change the name attribute when using the HtmlHelper methods (and there is no reason to change the id attribute either)
Next, you cannot use a foreach loop to generate form controls for a collection. You need a for loop or EditorTemplate to generate the correct name attributes with indexers. Refer this answer for a detailed explanation.
Then you cannot use a if block inside the loop (unless you include a hidden input for the collection indexer), because by default the DefaultModelBinder required collection indexers to start at zero and be consecutive.
First start by creating view models to represent what your want to display/edit in the view.
public class ScoreVM
{
public int ID { get; set; }
public string Name { get; set; }
public int Score { get; set; }
}
public class TechnologiesVM
{
public List<ScoreVM> Languages { get; set; }
public List<ScoreVM> Technologies { get; set; }
public string Notes { get; set; } // for your textarea control
}
Note you will probably want to add validation attributes such as a [Range] attribute for the Score property
In the GET method, initialize and populate your view model and pass it to the view
public ActionResult Page2()
{
IEnumerable<Technologies> technologies = db.Technologies;
TechnologiesVM model = new TechnologiesVM
{
Languages = technologies.Where(x => x.type == "lang")
.Select(x => new ScoreVM{ ID = x.ID, Name = x.technologyName }).ToList(),
Technologies = technologies.Where(x => x.type == "tech")
.Select(x => new ScoreVM{ ID = x.ID, Name = x.technologyName }).ToList(),
};
return View(model);
}
and in the view
#model TechnologiesVM
....
#using (Html.BeginForm())
{
....
#for (int i = 0; i < Model.Languages.Count; i++)
{
#Html.HiddenFor(m => m.Languages[i].ID)
#Html.HiddenFor(m => m.Languages[i].Name)
#Html.LabelFor(m => m.Languages[i].Score, Model.Languages[i].Name)
#Html.TextBoxFor(m => m.Languages[i].Score)
#Html.ValidationMessageFor(m => m.Languages[i].Score)
}
#for (int i = 0; i < Model.Languages.Count; i++)
{
.... // repeat above
}
#Html.LabelFor(m => m.Notes)
#Html.TextAreaFor(m => m.Notes)
#Html.ValidationMessageFor(m => m.Notes)
<input type="submit" />
}
and the POST method will be
public ActionResult Page2(TechnologiesVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
... // save the data and redirect
}

how to bind more than one model with single view?

Guys I have tried to use more than one Models with single view.
but I could not found the solution how to implement it.
I want to use One View data in Single past and the other in another part..
I have used the following code..
this is one view
#Html.Partial("Sidebar")
this is another view
<!-- start content -->
<div id="content">
<div class="post">
<h1 class="title">
Add New Post
</h1>
<p></p>
<div class="entry">
#using (Html.BeginForm())
{
<div>
<div style="display:inline;">
<div style="display: inline; float: left;">
#Html.Label("lblcategory", "Category", new { style = "Width:100px; float: left;" })
<div style="width: 150px; height: 60px; overflow-y: scroll; display: inline; float: left;">
#for (int i = 0; i < (int)TempData["Rows"]; i++)
{
for (int j = 0; j < (int)TempData["Cols"]; j++)
{
<input id="Checkbox + #i" name = "Category" type="checkbox" style="width:50px;" value="#TempData["[" + i.ToString() + "][" + j.ToString() + "]"]"/>
#TempData["[" + i.ToString() + "][" + j.ToString() + "]"]
}
}
#*#Html.LabelFor(model => model.CategoryName)*#
</div>
<div style="float:right;">
<label id="lblcategoryrequired" style="color:Red">#Html.ValidationMessageFor(model => model.CategoryName)</label>
</div>
</div>
</div>
<div>
<p style="display: inline; float: left;">
#Html.Label("lblsubjet", "Subject", new { style = "Width:100px; float: left;" })
#*#Html.TextBox("txtsubject", "", new { style = "Width:700px;" })*#
#Html.TextBoxFor(model => model.PostSubject, new { style = "Width:400px; maxlength=400;" })
<label id="lblsubjectrequired" style="color:Red">#Html.ValidationMessageFor(model => model.PostSubject)</label>
</p>
</div>
<div>
<p style="display: inline; float: left;">
#Html.Label("lblcontent", "Content", new { style = "Width:100px; float: left; Vertical-align:top;" })
#*#Html.TextArea("txtcontent","", new { style = "Width:700px; height:200px; maxlength=700;" })*#
#Html.TextAreaFor(model => model.PostContent, new { style = "Width:400px; height:200px; maxlength=400;" })
</p>
</div>
<div>
<p style="display: inline; float: left;">
#Html.Label("lblblank", "a", new { style = "Width:100px; float: left; Color:#372412" })
<input type="submit" value="Add" id="btnadd" style="width: 100px;" class="button" />
&nbsp&nbsp&nbsp&nbsp
<a id="Cancel" href="~/Home/Home"> <input type="button" value="Cancel" id="btncancel" class="button" style="width: 100px;" /></a>
</p>
</div>
</div>
#Html.ValidationSummary(true)
}
</div>
</div>
</div>
</div>
I don't understand your question 100%. But if I were to understand it then I don't think it will work the way that you need it to work (I might be mistaken). I would suggest that you move away from your partial view and just pass in one view model that you can use to populate both sections. View models are there to represent your data on a view.
I'm going to give you a basic sample that you can modify and use in your scenario. Lets say we have a customer and this customer can have 1 or many addresses. So a basic representation of these 2 models could look like this:
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public IEnumerable<Address> Addresses { get; set; }
}
public class Address
{
public int Id { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string AddressLine3 { get; set; }
}
And now on your details view you want to display the customer's details and the addresses of this customer. So we have 2 models (customer and address) that you are displaying on 1 view.
public ActionResult Details(int id)
{
Customer customer = customerRepository.GetById(id);
if (customer != null)
{
customer.Addresses = addressRepository.GetAddressesForCustomer(customer.Id);
}
// The above 2 steps can be done in 1 repository call
// Now populate your view model with the above details
// This can also be 1 or 2 lines when you use something like Auto Mapper
CustomerDetailsViewModel viewModel = new CustomerDetailsViewModel
{
viewModel.CustomerId = customer.Id,
viewModel.CustomerFirstName = customer.FirstName,
viewModel.CustomerLastName = customer.LastName,
viewModel.CustomerAddresses = customer.Addresses
};
return View(viewModel);
}
Your view model:
public class CustomerDetailsViewModel
{
public int CustomerId { get; set; }
public string CustomerFirstName { get; set; }
public string CustomerLastName { get; set; }
public IEnumerable<Address> CustomerAddresses { get; set; }
}
So now you have 1 view model populated from 2 different models. Now all that you have to do on your view is to use this view model to display data:
#model YourProject.ViewModels.Customers.CustomerDetailsViewModel
#Model.CustomerId<br />
#Model.CustomerFirstName<br />
#Model.CustomerLastName<br /><br />
#foreach (var address in #Model.CustomerAddresses)
{
<div>
#address.Id<br />
#address.AddressLine1<br />
#address.AddressLine2<br />
#address.AddressLine3<br />
</div>
}
I hope this helps.
You should use a view model that represents the data required to render your view. You could either expose the models directly on the view model (violating LoD), or delegate the calls to the view model to the underlying models (violating the DRY principle).

Resources