I am a beginner in asp mvc 3, and i develop an application ,
I first want to describe my application
so i have in my database , a table account and a table role and the relation ship between account and role is many to many, so i have a association table account_role,
i work with Entity Framework database first and i generate my POCO with EF DbContext Generator,
i want ann edit Account page dispay a chekbox of Roles
this is my code
Controller Account
public ActionResult Edit(int id)
{
accounts accounts = db.accounts
.Include(i => i.roles_accounts)
.Where(i => i.id_account == id)
.Single();
PopulateAssignedRoleData(accounts);
return View(accounts);
}
// populate Assigned RoleDATA pour afficher les checkbox
private void PopulateAssignedRoleData(accounts account)
{
//Get all role
var allRole =db.roles;
//For each role, the code checks if the role exists in the property of accountRole
// To create effective search when checking if a role is assigned to the account,
// assigned roles in are put into a collection HashSet
var accountRoles = new HashSet<int>(account.roles_accounts.Select(r => r.id_account_role));
var viewModel = new List<AssignedRoleData>();
// Property Assigned role of which is allocated account is set to true.
//The view will use this property to determine
//what check boxes to be displayed as selected.
//Finally, the list is passed to the view in a ViewBag
foreach (var role in allRole)
{
viewModel.Add(new AssignedRoleData
{
RoleId = role.id_role,
Name = role.name,
Assigned = accountRoles.Contains(role.id_role)
});
}
ViewBag.roles = viewModel;
}
//
// POST: /Account/Edit/5
[HttpPost]
public ActionResult Edit(int id, FormCollection formCollection, string [] selectedRoles)
{
var accountsToUpdate = db.accounts
.Include(i => i.roles_accounts)
.Where(i => i.id_account == id)
.Single();
if (TryUpdateModel(accountsToUpdate, "", null, new string[] { "roles_accounts" }))
{
try
{
if (String.IsNullOrWhiteSpace(accountsToUpdate.login))
{
accountsToUpdate.roles_accounts = null;
}
UpdateAccountRole(selectedRoles, accountsToUpdate);
db.Entry(accountsToUpdate).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DataException)
{
ModelState.AddModelError("", "Unable to save change");
}
}
PopulateAssignedRoleData(accountsToUpdate);
return View(accountsToUpdate);
}
// update AccountRole (liste of checkbox)
private void UpdateAccountRole(string[] selectedRoles, accounts accountToUpdate)
{
if (selectedRoles == null)
{
accountToUpdate.roles_accounts=new List<roles_accounts>();
return;
}
var selectedRolesHS = new HashSet<string>(selectedRoles);
var accountsRoles = new HashSet<int>
(accountToUpdate.roles_accounts.Select(r => r.id_account_role));
foreach(var role in db.roles_accounts)
{
if( selectedRolesHS.Contains(role.id_account_role.ToString()))
{
if(!accountsRoles.Contains(role.id_account_role))
{
accountToUpdate.roles_accounts.Add(role);
}
}
else
{
if (accountsRoles.Contains(role.id_account_role))
{
accountToUpdate.roles_accounts.Remove(role);
}
}
}
}
And i create a folder nammed ViewModels, and in this folder i create a classe AssignedRoleData To provide data to the view for the list of check boxes,
this is the AssignedRoleData
public class AssignedRoleData
{
public int RoleId { get; set; }
public string Name { get; set; }
public bool Assigned { get; set; }
and in the Edit.schtml
i put this code
<div class="editor-field">
<table>
<tr>
#{
int cnt = 0;
List<App_ERP1.ViewModels.AssignedRoleData> roles=ViewBag.roles;
foreach (var role in roles) {
if (cnt++ % 3 == 0) {
#: </tr> <tr>
}
#: <td>
<input type="checkbox"
name="selectedRoles"
value="#role.RoleId"
#(Html.Raw(role.Assigned ? "checked=\"checked\"" : "")) />
#role.RoleId #: #role.Name
#:</td>
}
#: </tr>
}
}
}
My Problem is the checkbox does not retain its state and also each time when I click the save button it removes the added roles (choose)
thanks to help me
you need to use a "for" instead of the "foreach" on the view and your attribute "name" probably needs to be something like name="selectedRoles[i]" . Tip: Don't access the database directly on the controller. Create a middle layer where you have the class AccountRoleService and here put the logic and then create another layer for accessing the database called repository (e.g AccountRoleRepository) where you actually do your LINQ to SQL stuff. If you want to reuse methods will be much easier. So controllers do little and call service classes. Service classes do logic and call repository classes to access the database. So if you want to unit test the services you will be on the right path.
for (i = 0; i< roles.Count, i++) {
if (cnt++ % 3 == 0) {
#: </tr> <tr>
}
#: <td>
<input type="checkbox"
name="selectedRoles[" + i + "]"
value="#role.RoleId"
#(Html.Raw(role.Assigned ? "checked=\"checked\"" : "")) />
#role.RoleId #: #role.Name
#:</td>
}
#: </tr>
Related
I have 5 records coming from a simple select stored procedure.
ID Name
1 RecordOne
2 RecordTwo
3 RecordThree
4 RecordFour
5. RecordFive
Requirement is to display one record at a time example:
Record One
Previous Next
Two Action links or buttons with Previous and Next text.
If user clicks Next user will see
RecordTwo
and so on,same for previous case.
My model
namespace MVCLearning.Models
{
public class VMNews
{
public List<Student> StudentDetails { get; set; }
}
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
}
}
Action
public ActionResult Index()
{
VMNews objnews = new VMNews();
objnews.StudentDetails = db.Database.SqlQuery<Student>("usp_studentdetails").ToList();
return View(objnews);
}
View
<div>
#foreach (var item in Model.SD.Take(1))
{
<h3>#item.Name</h3>
<h3>#item.Age</h3>
}
#Html.ActionLink("Next", "index", new { Model.SD[0].ID})
#Html.ActionLink("Previous", "index", new { Model.SD[0].ID })
The way I have written the view is totally wrong am not getting how and what to write on the action and what to write on the View.
What will be one of the way to achieve this.
Change you method to
public ActionResult Index(int? index)
{
int max = 5; // modify based on the actual number of records
int currentIndex = index.GetValueOrDefault();
if (currentIndex == 0)
{
ViewBag.NextIndex = 1;
}
else if (currentIndex >= max)
{
currentIndex = max;
ViewBag.PreviousIndex = currentIndex - 1;
}
else
{
ViewBag.PreviousIndex = currentIndex - 1;
ViewBag.NextIndex = currentIndex + 1;
}
VMNews objnews = new VMNews();
Student model = db.Database.SqlQuery<Student>("usp_studentdetails")
.Skip(currentIndex).Take(1).FirstOrDefault();
return View(model);
}
Note that the query has been modified to return only one Student since that is all that you require in the view. Also I have asssumed if a user enters a value greater than the number of records it will return the last record (you may in fact want to throw an error?)
The view now needs to be
#model Student
<h3>#Model.Name</h3>
<h3>#Model.Age</h3>
#if (ViewBag.PreviousIndex != null)
{
#Html.ActionLink("Previous", "Index", new { index = ViewBag.PreviousIndex })
}
#if (ViewBag.NextIndex != null)
{
#Html.ActionLink("Next", "Index", new { index = ViewBag.NextIndex })
}
I'm trying to query for a particular column & to show the item list in view properly one after another. Here is my code:
Controller:
public ActionResult ShowImage()
{
using (var context = new ImageTrialDBEntities())
{
var pathlist = (from s in context.Images
select s.ImageLink).ToList();
var model = new ImageModel();
model.ImageList = pathlist;
return View(model);
}
}
Model:
public class ImageModel
{
public string Image { get; set; }
public IList<string> ImageList { get; set; }
}
View:
<div>
#foreach (var s in Model.ImageList)
{
#Html.DisplayFor(x=>x.ImageList)
<br />
}
</div>
The list is showing like this:
I would like to show one at a time with a break in between. Please help.
Replace
#Html.DisplayFor(x=>x.ImageList)
with
#Html.DisplayFor(x=>s)
You have 2 loops in the view code. Try just printing out the variable s.
I have a MVC3 Razor form. It have a radiobutton list and some another text fields. When I press submit controller post action get the view model, which have all fields seted correctly, except RegionID.
Model:
namespace SSHS.Models.RecorderModels
{
public class CreateViewModel
{
...
public int RegionID { get; set; }
...
}
}
Controller:
namespace SSHS.Controllers
{
public class RecorderController : Controller
{
...
public ActionResult Create()
{
EntrantDBEntities db = new EntrantDBEntities();
List Regions = new List(db.Region);
List Schools = new List(db.School);
List Settlements = new List(db.settlement);
CreateViewModel newEntr = new CreateViewModel();
ViewBag.Regions = Regions;
ViewBag.Schools = Schools;
ViewBag.Settlements = Settlements;
return View(newEntr);
}
[HttpPost]
public ActionResult Create(CreateViewModel m)
{
EntrantDBEntities db = new EntrantDBEntities();
Entrant e = new Entrant()
{
FatherName = m.FatherName,
Lastname = m.LastName,
LocalAddress = m.LocalAddress,
Name = m.Name,
RegionID = m.RegionID,
PassportID = m.PassportID,
SchoolID = m.SchoolID,
SettlementID = m.SattlementID,
TaxID = m.TaxID,
};
db.Entrant.AddObject(e);
db.SaveChanges();
return RedirectToAction("Index");
}
}
View:
#model SSHS.Models.RecorderModels.CreateViewModel
#using SSHS.Models
#using (Html.BeginForm("Create", "Recorder", FormMethod.Post))
{
#foreach (Region item in ViewBag.Regions)
{
#Html.RadioButtonFor(m => m.RegionID, item.RegionID)
#Html.Label(item.RegionName) - #item.RegionID
}
...
...
}
The Create(CreateViewModel m) method gets data from all textboxes normaly, but RegionID always is 0.
How are you planning to fill radio button with int ? It have two states: checked and not. Could you tell us, what are you trying to do? Make radio group? Use bool for RadioButtonFor.
Added:
You need to write something like this: CheckboxList in MVC3.0 (in your example you will have radio buttons)
I am new to MVC3, so apologies if this is basic, but I couldn't work it out.
I have a view model that includes 3 tables, an 'Albums' table with 2 foreign keys (Artist ID and Label ID).
I have a 'Labels' controller and a Details method where I display the Label table fields. I am trying to get the distinct 'Artists' from the collection of Albums related to the Label.
At the moment, I can get the name of the artists, but one is generated for each album - I have included the code for this scenario below.
I have tried a bunch of different things, like including Artists in the collection and using the Distinct and group by functions, but to no avail. Not sure if it is doable this way, or whether, due to the indirect relationship between the tables, I need to use a different approach.
Any helps is much appreciated.
Controller:
public ActionResult Details(int id)
{
var viewModel = new LabelsDetailsVM();
viewModel.Lables = db.Labels
.Include(a => a.Albums)
.SingleOrDefault(x => x.LabelID == id);
return View(viewModel);
View:
#foreach (var artist in Model.Lables.Albums)
{
<tr>
<td>
#Html.DisplayFor(model => artist.Artist.ArtistName)
</td>
</tr>
}
View Model
public class LabelsDetailsVM
{
public Label Lables { get; set; }
public IEnumerable<Album> Albums { get; set; }
public IEnumerable<Artist> Artists { get; set; }
}
}
Here's an example that might get you on the right track:
view model:
public class LabelViewModel
{
public Label Label { get; set; }
public IEnumerable<Artist> Artists { get; set; }
}
Controller:
public class LabelController : Controller
{
public ActionResult Details(int id)
{
var label = db.Labels
.Include(l => l.Albums)
.SingleOrDefault(l => l.LabelID == id);
if (label == null)
{
return HttpNotFound();
}
var distinctArtists = label
.Albums
.Select(a => a.Artist)
.Distinct(ArtistComparer.Default);
var model = new LabelViewModel
{
Label = label,
Artists = distinctArtists
};
return View(model);
}
}
and the equality comparer used to distinguish between 2 artists used in the controller that could of course be adapted to match your requirements. In this example it considers that 2 artists represent the same entity if they have the same ID. But you could work with some other properties such as the name and so on, all depends on what you need:
public class ArtistComparer : IEqualityComparer<Artist>
{
protected ArtistComparer()
{
}
private static readonly IEqualityComparer<Artist> _default = new ArtistComparer();
public static IEqualityComparer<Artist> Default
{
get
{
return _default;
}
}
public bool Equals(Artist x, Artist y)
{
if (x != null && y != null)
{
return x.ArtistID.Equals(y.ArtistID);
}
return false;
}
public int GetHashCode(Artist obj)
{
return obj.ArtistID.GetHashCode();
}
}
View:
#model LabelViewModel
<h3>#Html.DisplayFor(x => x.Label.LabelName)</h3>
<div>Artists</div>
<table>
<thead>
<tr>
<th>artist name</th>
</tr>
</thead>
<tbody>
#foreach (var artist in Model.Artists)
{
<tr>
<td>#artist.ArtistName</td>
</tr>
}
</tbody>
</table>
I'm getting back to an MVC3 project after a 3 month hiatus. I need to display a drop down list that pulls from Database A, but saves to Database B. The property I need to persist is the NAICS/SIC code. Right now I just provide the user a text box to key in freeform text. So, I have the mechanics of that down. But instead it should provide only a valid list of codes from a source database.
The tricky thing to is I'm using a custom model binder to generate my ViewModels on the fly, so I don't have a distinct .cshtml file to customize.
[Serializable]
public class Step4ViewModel : IStepViewModel
{
public Step4ViewModel()
{
}
//load naics codes from somewhere
[Display(Name = "Describe the nature of your business.")]
public String NatureOfBusiness { get; set; }
[Display(Name="NAICS/SIC CODE")]
public String BusinessTypeCode { get; set; }
Tricky ViewModel
#using Microsoft.Web.Mvc;
#using Tangible.Models;
#model Tangible.Models.WizardViewModel
#{
var currentStep = Model.Steps[Model.CurrentStepIndex];
var progress = ((Double)(Model.CurrentStepIndex) / Model.Steps.Count) * 100;
}
<script type="text/javascript">
$(function () {
$("#progressbar").progressbar({
value: #progress
});
});
</script>
<div id="progressbar" style="height:20px;">
<span style="position:absolute;line-height:1.2em; margin-left:10px;">Step #(Model.CurrentStepIndex + 1) out of #Model.Steps.Count</span>
</div>
#Html.ValidationSummary()
#using (Html.BeginForm())
{
#Html.Serialize("wizard", Model)
#Html.Hidden("StepType", Model.Steps[Model.CurrentStepIndex].GetType())
#Html.EditorFor(x => currentStep, null, "")
if (Model.CurrentStepIndex > 0)
{
<input type="submit" value="Previous" name="prev" />
}
if (Model.CurrentStepIndex < Model.Steps.Count - 1)
{
<input type="submit" value="Save & Continue" name="next" />
}
else
{
<input type="submit" value="Finish" name="finish" />
}
#*<input type="submit" value="Save" name="Save" />*#
}
Controller
[HttpPost]
public ActionResult Index([Deserialize] WizardViewModel wizard, IStepViewModel step)
{
wizard.Steps[wizard.CurrentStepIndex] = step;
if (ModelState.IsValid)
{
//Always save.
var obj = new dr405();
//wire up to domain model;
foreach (var s in wizard.Steps)
{
Mapper.Map(s,obj,s.GetType(), typeof(dr405));
}
using (var service = new DR405Service())
{
//Do something with a service here.
service.Save(db, obj);
}
if (!string.IsNullOrEmpty(Request["next"]))
{
wizard.CurrentStepIndex++;
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
wizard.CurrentStepIndex--;
}
else
{
return View("Upload", obj);
}
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
wizard.CurrentStepIndex--;
}
return View(wizard);
}
WizardViewModel
[Serializable]
public class WizardViewModel
{
public String AccountNumber { get; set; }
public int CurrentStepIndex { get; set; }
public Boolean IsInitialized { get { return _isInitialized; } }
public IList<IStepViewModel> Steps { get; set; }
private Boolean _isInitialized = false;
public void Initialize()
{
try
{
Steps = typeof(IStepViewModel)
.Assembly.GetTypes().Where(t => !t.IsAbstract && typeof(IStepViewModel).IsAssignableFrom(t)).Select(t => (IStepViewModel)Activator.CreateInstance(t)).ToList();
_isInitialized = true;
//rewrite this. get the profile and wire them up or something.
this.AccountNumber = Tangible.Profiles.DR405Profile.CurrentUser.TangiblePropertyId;
}
catch (Exception e)
{
_isInitialized = false;
}
}
}
You can specify a template for a specific property on your view model by adding the UIHint attribute to the field. Since your view calls EditorFor on the model it will use the template you specified with UIHint.
BusinessTypeDropdown.ascx - (placed in Views/Shared/EditorTemplates
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<string>" %>
<% var businessTypes = ViewData["businessTypes"] as IEnumerable<string>; %>
<%= Html.DropDownListFor(m => m , new SelectList(businessTypes, Model))%>
In your View Model
[Serializable]
public class Step4ViewModel : IStepViewModel
{
public Step4ViewModel()
{
}
//load naics codes from somewhere
[Display(Name = "Describe the nature of your business.")]
public String NatureOfBusiness { get; set; }
[Display(Name="NAICS/SIC CODE")][UIHint("BusinessTypeDropdown")]
public String BusinessTypeCode { get; set; }
Then in your controller just set ViewData["businessTypes"] to your list of business types.
Without understanding your "tricky" view model code, it will be hard to make helpful suggestions.
However, there shouldn't be much problem here. You need to somehow create your dropdown list in yoru view, and populate it from data passed from your controller.
All the work happens in your controller. Populate your list or IEnumerable or whatever data source from your first database, then in your post handler save the selection it to your second database (the second part should not be much different from what you already have).