I am new to the MVC framework as well as ASP.NET so I apologize for any sloppy code.
I am creating an application that requires an Admin to set the roles of new users that register. When the Admin logs into the application, they are automatically directed to an Admin page that displays a list of users that have registered with the app. The admin has the ability to choose the role for the new user.
Here is my controller for the admin:
public class AdminTestController : Controller
{
private UsersContext db = new UsersContext();
// GET: /AdminTest/
[Authorize(Roles = "Admin")]
public ActionResult Index()
{
var model = db.UserProfiles.ToList();
return View(model);
}
[HttpPost]
public ActionResult Submit(string userName, string selectedRole)
{
Roles.AddUserToRole(userName,selectedRole);
return View("Index");
}
Here is the corresponding view:
#model IEnumerable<WAP.Models.UserProfile>
#{
ViewBag.Title = "Index";
}
...
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.UserId)
</td>
<td>
</td>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.UserId }) |
#Html.ActionLink("Details", "Details", new { id=item.UserId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.UserId })
#using (Html.BeginForm("Submit", "AdminTest", FormMethod.Post))
{
<select name ="selectedRole">
<option value="Null"></option>
<option value="Manager">Manager</option>
<option value="Agent">Agent</option>
</select>
<input id="SubmitChange" type="submit" value="Submit" />
<input id="userName" type ="text" value= "#item.UserName" name= "userName" hidden ="hidden" />
}
</td>
</tr>
}
Thank you in advance for taking the time to look at this question and any help that you can provide.
You can use Html.DropDownList helper for this. Firstly you will need to prepare your roles collection in controller to populate it. Here is the sample code:
[Authorize(Roles = "Admin")]
public ActionResult Index()
{
var model = db.UserProfiles.ToList();
var rolesCollection = new List<string> {"Null", "Manager", "Agent"};
ViewBag.Roles = new SelectList(rolesCollection);
return View(model);
}
Then in your view:
#using (Html.BeginForm("Submit", "AdminTest", FormMethod.Post))
{
#Html.Hidden("userName", item.UserName)
#Html.DropDownList("selectedRole", (SelectList)ViewBag.Roles)
<input id="SubmitChange" type="submit" value="Submit" />
}
You can also use Html.RadioButton helper in such way:
#using (Html.BeginForm("Submit", "AdminTest", FormMethod.Post))
{
#Html.Hidden("userName", item.UserName)
#Html.RadioButton("selectedRole", "Null", true)
#Html.RadioButton("selectedRole", "Manager")
#Html.RadioButton("selectedRole", "Agent")
<input id="SubmitChange" type="submit" value="Submit" />
}
If you want to make multiple roles selected at the same time, I suggest using some jQuery plugin, such as jQuery.chosen and Html.ListBox helper.
Use EditorTemplates for all enums (Create "Role" enum):
#model Enum
#Html.DropDownListFor(m => Enum.GetValues(Model.GetType()).Cast<Enum>().Select(m =>
new SelecteListItem {Selected = "your logic", Text = "", Value = ""}))
Or use custom partial view by current enum.
Related
I'm new with asp.net MVC and studying with some example code.
I'm referrence the sample from https://github.com/LaunchCodeEducation/cheese-mvc.
And I want to add new action and view for the example. Such as add a delete link/button to delete a cheese from cheese/index page directly:
But it seems there have something wrong with the SingleRemove action and view I wrote.
The action I use as below:
[HttpGet]
public IActionResult SingleRemove()
{
ViewBag.cheeses = context.Cheeses.ToList();
return View();
}
[HttpPost]
public IActionResult SingleRemove(int cheeseId)
{
Cheese theCheese = context.Cheeses.Single(c => c.ID == cheeseId);
//context.Cheeses.Remove(theCheese);
//context.SaveChanges();
return View(theCheese);
}
And the view I use as :
#model CheeseMVC.Models.Cheese
<h1>Going to be removed cheese information as below</h1>
<form >
<ul>
<li>Cheese ID: #Model.ID</li>
<li>Cheese Name: #Model.Name</li>
<li>Cheese Type: #Model.Type</li>
<li>Cheese Description: #Model.Description</li>
</ul>
<input type="submit" value="Remove Selected Cheese" />
</form>
Could you please guide how to set the action and model correctly?
And how to let a pop-up window to double confirm the deletion on a cheese?
If you create a Controller and select "Controller with views, using Entity Framework" it will create all of that for you based on your model. You could then go back and make it pretty with ccs to use buttons instead of links ect.. I am not sure what your view is doing in the above code. Your view should be
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Description</th>
<th></th>
</tr>
</thead>
foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.Name)</td>
<td>#Html.DisplayFor(modelItem => item.Type)</td>
<td>#Html.DisplayFor(modelItem => item.Description)</td>
<td>
#using (Html.BeginForm("Delete", "controller name goes here", FormMethod.Post))
{
#Html.AntiForgeryToken()
<input type="hidden" id="id" value="#item.ID" />
<input onclick="return confirm('Are you sure you want to delete the Cheese?');" type="submit" value="Delete" class="btn btn-primary " />
}
</td>
</tr>
}
</table>
Then in your Controller you would have something like this:
public ActionResult Delete(int? ID)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Cheese cheese = db.Cheese.Find(id);
if (cheese == null)
{
return HttpNotFound();
}
return View(Index);
}
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult Delete(int ID)
{
var cheese = db.cheese.Find(id);
if (cheese != null)
{
Cheese cheese = db.Cheese.Find(id);
db.Cheese.Remove(cheese);
db.SaveChanges();
return RedirectToAction("Index");
}
return RedirectToAction("Index", "Cheese");
}
Make sure your are using:
using System.Data.Entity;
using System.Linq;
using System.Net;
And also not sure what your context is but this is where the db.Cheese comes from:
private ApplicationDbContext db = new ApplicationDbContext();
I am encountering the following error:
A data source must be bound before this operation can be performed.
I have a text box, the user enters a name, and clicks the submit button. The name is added to a list. I have researched the error but everything I try gives me the same error. Any insight on what I am doing wrong will be appreciated.
Model:
namespace RangeTest.Models
{
public class UserNameModel
{
[Key]
public int Id { get; set; }
[Display(Name = "Names Added List")]
public string FullName { get; set; }
}
}
Controller:
public ActionResult Admin()
{
return View();
}
[HttpPost]
public ActionResult Admin(UserNameModel model, IEnumerable<RangeTest.Models.UserNameModel> t)
{
List<UserNameModel> userList = new List<UserNameModel>();
model.FullName = t.FirstOrDefault().FullName;
userList.Add(model);
return View(userList.ToList());
}
View (Admin.cshtml):
#model IEnumerable<RangeTest.Models.UserNameModel>
#{
ViewBag.Title = "";
}
<div class="container"></div>
<div class="jumbotron">
#using (Html.BeginForm("Admin", "UserNames", FormMethod.Post, new { #id = "WebGridForm" }))
{
#Html.ValidationSummary(true)
WebGrid dataGrid = new WebGrid(Model, canPage: false, canSort: false);
//Func<bool, MvcHtmlString> func =
//(b) => b ? MvcHtmlString.Create("checked=\"checked\"") : MvcHtmlString.Empty;
<div class="table-bordered">
<div class="Title">Admin - Add names to range list</div><br />
<table id="TblAdd"class="table">
<tr>
#{
RangeTest.Models.UserNameModel t = new RangeTest.Models.UserNameModel();
}
#Html.Partial("_AddDynTable", t)
</table>
</div>
<div class="table-responsive">
<div class="Title">Names Added to Range List</div>
<table class="table">
<tr>
<td >
#dataGrid.GetHtml(columns: dataGrid.Columns(dataGrid.Column(format: #<text>#item</text>),
dataGrid.Column("", format: (item) => Html.ActionLink("Delete", "Delete", new { id = item.id }))))
</td>
</tr>
</table>
</div>
}
</div>
Partial View (_addDynTable.cshtml)
#model RangeTest.Models.UserNameModel
<tr>
<td>
Enter Full Name: #Html.TextBoxFor(m => m.FullName)
#*<input type="button" value="Create" id="ClickToAdd" />*#<input class="CreateBtn" type="submit" value="Add to List" /></td>
</tr>
Can you link info about the error please?
One thing I can see is your t object is null when you pass it to the partial view
RangeTest.Models.UserNameModel t = new RangeTest.Models.UserNameModel();
A new UserNameModel with no data in it, if your error is there that might be the problem
So.. i have a class that has has a List.
I pass it to the view like the code below:
[HttpGet]
[Authorize(Roles="user")]
[CustomChecker]
public ActionResult Index(int? page, int id=0)
{
EmployeeContext emp = new EmployeeContext();
student st = emp.students.Single(x=>x.id ==id);
#ViewBag.id = st.id;
return View(st.subjSel.ToPagedList(page ?? 1, 4));
}
And then the View will receive it like this:
#using PagedList;
#using PagedList.Mvc;
#model PagedList<MvcApplication6.Models.subject>
<div style="font-family:Arial">
<fieldset>
<legend><h3>Open Classes</h3></legend>
#using (Html.BeginForm("Test", "Enrollment"))
{
<input type="hidden" name="id" value="#ViewBag.id" />
<table border="1">
<tr>
<th>#Html.LabelFor(model => model[0].subj)</th>
<th>#Html.LabelFor(model => model[0].days)</th>
<th>#Html.LabelFor(model => model[0].cstart)</th>
<th>#Html.LabelFor(model => model[0].cend)</th>
<th>#Html.LabelFor(model => model[0].professor)</th>
<th>#Html.LabelFor(model => model[0].units)</th>
<th>#Html.CheckBox("test") Select all</th>
</tr>
#for (int i = 0; i < Model.Count; i++)
{
<tr>
#Html.HiddenFor(model => model[i].id)
<td>
#Html.DisplayFor(m => m[i].subj)
#Html.HiddenFor(m => m[i].subj)
</td>
<td>
#Html.DisplayFor(m => m[i].days)
#Html.HiddenFor(m => m[i].days)
</td>
<td>
#Html.DisplayFor(m => m[i].cstart)
#Html.HiddenFor(m => m[i].cstart)
</td>
<td>
#Html.DisplayFor(m => m[i].cend)
#Html.HiddenFor(m => m[i].cend)
</td>
<td>
#Html.DisplayFor(m => m[i].professor)
#Html.HiddenFor(m => m[i].professor)
</td>
<td>
#Html.DisplayFor(m => m[i].units)
#Html.HiddenFor(m => m[i].units)
</td>
<td>
#Html.CheckBoxFor(m => m[i].isSelected)
</td>
</tr>
}
</table>
<br />
<br />
<table>
<tr><td align="center" width="500px"></td></tr>
<tr>
<td align="center" width="500px">
<input type="submit" value="submit" /> | <input type="button" value="clear" />
</td>
</tr>
</table>
<br />
<br />
}
</fieldset>
</div>
#Html.PagedListPager(Model, page => Url.Action("Index", "Enrollment", new { page, id = Request.QueryString["id"] }))
My problem is that it will be rendered like this [0].subj and that won't allow me to bind because it should be something like name[0].subj.
I've been experimenting and trying new methods, are there any ways for me to bind them properly? i want to use Html Helpers and as much as possible, i don't want to re-implement a custom one just for this part.
This is the function where they are supposed to be bound. This class has a List of students (the one that i converted to IPagedList)
[HttpPost]
[Authorize(Roles="user")]
public ActionResult Test(student st)
And this is how my View looks like. I am using CheckBoxFor for selections.
Extra question:
How come my navigation looks so ugly?
The model is the view is #model PagedList<subject> which means the parameter in the must be
[HttpPost]
public ActionResult Test(IEnumerable<subject> model)
If you also need the student ID property, then include an additional parameter
[HttpPost]
public ActionResult Test(IEnumerable<subject> model, int ID)
Since you GET method has a parameter int ID and assuming your using the default routing then the ID will be added to the forms action attribute i.e. it will render action="/Enrollment/Test/2" assuming the value of the ID is 2. If not, you can add this as a route parameter
#using (Html.BeginForm("Test", "Enrollment", new { ID = ViewBag.id }))
Alternatively you can use a view model
public class StudentVM
{
public int ID { get; set; }
public PagedList<student> Students { get; set; }
}
and in the GET method
public ActionResult Index(int? page, int id=0)
{
EmployeeContext emp = new EmployeeContext();
student st = emp.students.Single(x=>x.id ==id);
StudentVM model = new StudentVM()
{
ID = id,
Students = st.subjSel.ToPagedList(page ?? 1, 4)
};
return View(model);
}
and base you view on the view model and post it back to public ActionResult Test(StudentVM model)
To get your list of subject back, you should apply a suitable prefix to it. Since the name of property of Student that contains List<subject> is subjSel and you want to get values in your action in a Student object, so you should set prefix like this, before for statement:
#{ViewData.TemplateInfo.HtmlFieldPrefix = "subjSel";}
So fileds of model will be rendered with names like subjSel[0].fieldname.
Then in your post action you will receive subjects in subjSel property of st.
Additional Resource:
Model Binding to a List
For your extra question:
Check if your rendered code has suitable style and your style tags related to paging has been rendered in page.
I have a list of categories that I want to edit. I can successfully pass them into the controller.
public ActionResult Edit()
{
if (!IsAdmin) return RedirectToAction("Index", "Home");
ViewBag.IsAdmin = IsAdmin;
var categories = _model.Categories.ToList();
return View(categories);
}
Then I have a view to edit the names of the categories.
#using MyStructures.Models
#model List<MyStructures.Models.Category>
#using (Html.BeginForm("Update", "Categories", FormMethod.Post))
{
foreach (var category in Model)
{
var categoryToEdit = #category;
<table>
<tr>
<td>Category Number: #category.Id</td>
<td>#Html.EditorFor(x => categoryToEdit.Name)</td>
<td colspan="2">
<input type="submit" value="Save"
formaction=#Href("~/Categories/Update/") />
</td>
</tr>
</table>
}
<input type="submit" value="Cancel"
formaction=#Href("~/Home/Index/") />
}
How do I pass the updated value to the "Update" controller? The value of Category obj is null.
[HttpPost]
public ActionResult Update(Category obj)
{
var existing = _model.Categories.Find(obj.Id);
existing.Name = obj.Name;
_model.SaveChanges();
return RedirectToAction("Index", "Home");
}
So I just finished working the project at http://msdn.microsoft.com/en-us/data/gg685489. I am trying to create a CRUD for use with an external database that already exists and I am just using the project as a guide for my own work. I have been able to change certain variable names and connections to connect with the database that I am trying to work with and I have even displayed the data I want using the index method. I have also been able to get the create method working and I am able to create new data in the DB I am working with. Unfortunatlty I have not been able to pull data in when using the Edit or Delete methods or save/delete after I have made the changes I want.
For example when I press the edit button it should pull the record that I clicked on so that I could edit a particular records information but instead it threw an error saying that this variable cannot accept a null value which is why I changed the int to a string in the edit delete methods. This part is solved
I think the issue has something to do with the Edit and Delete methods not pulling the recordset when I tell it to. Does anyone have any idea why it's not pulling the record sets when I edit/delete or saving my changes after I tell it to save?
I have posted my PaController class which has my CRUD methods in it for diagnosis as well as my iamp_mapping class file which has the 3 fields I need to work with. I am 90% sure I am doing something wrong with the controller, but if you need anymore code/information about the issue please leave me a note I will be checking back A LOT because I am soo stuck! Thanks so much for your help!
PaController
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using DBFirstMVC.Models;
using System.Data;
namespace DBFirstMVC.Controllers
{
public class PaController : Controller
{
PaEntities db = new PaEntities();
//
// GET: /Pa/
public ActionResult Index()
{
using (var db = new PaEntities())
{
return View(db.iamp_mapping.ToList());
}
}
//
// GET: /Pa/Details/5
public ActionResult Details(int id)
{
return View();
}
//
// GET: /Pa/Create
public ActionResult Create()
{
return View();
}
//
// POST: /Pa/Create
[HttpPost]
public ActionResult Create(iamp_mapping IAMP)
{
try
{
using (var db = new PaEntities())
{
db.iamp_mapping.Add(IAMP);
db.SaveChanges();
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
//
// GET: /Pa/Edit/5
public ActionResult Edit(string id)
{
using (var db = new PaEntities())
{
return View(db.iamp_mapping.Find(id));
}
}
//
// POST: /Pa/Edit/5
[HttpPost]
public ActionResult Edit(string id, iamp_mapping IAMP)
{
try
{
using (var db = new PaEntities())
{
db.Entry(IAMP).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch
{
return View();
}
}
//
// GET: /Pa/Delete/5
public ActionResult Delete(string id)
{
using (var db = new PaEntities())
{
return View(db.iamp_mapping.Find(id));
}
}
//
// POST: /Pa/Delete/5
[HttpPost]
public ActionResult Delete(string id, iamp_mapping IAMP)
{
try
{
using (var db = new PaEntities())
{
db.Entry(IAMP).State = EntityState.Deleted;
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch
{
return View();
}
}
}
}
iamp_mapping
using System;
using System.Collections.Generic;
namespace DBFirstMVC.Models
{
public partial class iamp_mapping
{
public string PA { get; set; }
public string MAJOR_PROGRAM { get; set; }
public string INVESTMENT_AREA { get; set; }
}
}
Edit View Code
#model DBFirstMVC.Models.iamp_mapping
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>iamp_mapping</legend>
<div class="editor-label">
#Html.LabelFor(model => model.PA)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.PA)
#Html.ValidationMessageFor(model => model.PA)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.MAJOR_PROGRAM)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MAJOR_PROGRAM)
#Html.ValidationMessageFor(model => model.MAJOR_PROGRAM)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.INVESTMENT_AREA)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.INVESTMENT_AREA)
#Html.ValidationMessageFor(model => model.INVESTMENT_AREA)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Index.cshtml
#model IEnumerable<DBFirstMVC.Models.iamp_mapping>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
PA
</th>
<th>
MAJOR PROGRAM
</th>
<th>
INVESTMENT AREA
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.PA)
</td>
<td>
#Html.DisplayFor(modelItem => item.MAJOR_PROGRAM)
</td>
<td>
#Html.DisplayFor(modelItem => item.INVESTMENT_AREA)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.PA }) |
#Html.ActionLink("Details", "Details", new { id=item.PA }) |
#Html.ActionLink("Delete", "Delete", new { id=item.PA })
</td>
</tr>
}
The ActionLink is what I had to fix, when I first posted the question it looked like this
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ })
I changed it to the actual primary key PA and deleted the comments. This fixed the problem of data not filling in when trying to edit a row!
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.PA }) |
#Html.ActionLink("Details", "Details", new { id=item.PA }) |
#Html.ActionLink("Delete", "Delete", new { id=item.PA })
</td>
</tr>
}
Delete.cshtml
#model DBFirstMVC.Models.iamp_mapping
#{
ViewBag.Title = "Delete";
}
<h2>Delete</h2>
<h3>Are you sure you want to delete this?</h3>
<fieldset>
<legend>iamp_mapping</legend>
<div class="display-label">PA</div>
<div class="display-field">
#Html.DisplayFor(model => model.PA)
</div>
<div class="display-label">MAJOR_PROGRAM</div>
<div class="display-field">
#Html.DisplayFor(model => model.MAJOR_PROGRAM)
</div>
<div class="display-label">INVESTMENT_AREA</div>
<div class="display-field">
#Html.DisplayFor(model => model.INVESTMENT_AREA)
</div>
</fieldset>
#using (Html.BeginForm()) {
<p>
<input type="submit" value="Delete" /> |
#Html.ActionLink("Back to List", "Index")
</p>
}
Global.asax.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace DBFirstMVC
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Pa", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
}
Your problem here appears that when you click on the edit button/link the Edit action will expect an id of type int (if you use int), however this can be switched as optional if you use int?...which is how the MVC behaves...your best bet here is to try using a url say something like http://urwebsite.com/Pa/Edit?id=[use a valid id]...with this you will have a id in your Edit action...
Update
I think the issue you are having here is that the Id value in the Edit action method is null...and the MVC engine is unable to recognize an id value from your Url pattern
So, the page where you have the edit link/button you can use something like
Edit
If you have a for loop where you are displaying a collection of records...
#foreach(var item in Model)
{
Edit
}
Also make sure that you have a routing configured for url pattern /Pa/Edit/{id}
You may wanna read this Article if you want to know more about routing in mvc.
This will ensure that when a Get request is made to the edit action method, the id value will be recognized
Hope this helps...
My link wouldnt pick the id up for some reason, so I found a solution from one course.
He added the fifth argument "null" to the function like this
<td>#Html.ActionLink("Edit", "Edit", "Account", new {id = account.Id }, null)</td>
(text, action, controller, route value, null or HTML atribute)
this fixed it for me.