Prepopulate HtmlTextBoxFor with value from another field? - c#

I have a web page that is using a number of different partial views and different viewmodels for each partial. One of the partials is for the ability for a user to change their username. Since we are going to allow email addresses as usernames now, I would like to pre-populate the textbox with their current email address from another partial view on the page. How can this be done properly so the value will be populated in the text box and then saved when the submit to the controller happens? Here is a brief code snippet from what I'm looking at:
The view:
#Html.TextBoxFor(model => model.NewUsername, new { id = "uName_change" }) #Html.ValidationMessageFor(model => model.NewUsername)
It's worth noting here that I have tried setting the current email address into ViewData and ViewBag and putting "#value = viewdatahere" into the textbox for properties, but this didn't populate the string, it just left it blank.
I just need to know how this is done. Any help is appreciated.
Edit to add controller POST and GET methods:
[HttpGet]
[ClaimsPrincipalPermission(System.Security.Permissions.SecurityAction.Demand, Resource = Resources.User, Operation = Operations.Edit)]
public ActionResult ChangeUsername(Guid uniqueUserId)
{
User user = UserManager.GetUser(uniqueUserId);
AuthorizationHelper.ConfirmAccess(Resources.User, Operations.Edit, user);
ChangeUsername model = new ChangeUsername(){UserGuid = user.Identifier,NewUsername = user.Username};
return View(model);
}
[HttpPost]
[ClaimsPrincipalPermission(System.Security.Permissions.SecurityAction.Demand, Resource = Resources.User, Operation = Operations.Edit)]
public ActionResult ChangeUsername(ChangeUsername changeUsername)
{
User usr = UserManager.GetUser(changeUsername.UserGuid);
AuthorizationHelper.ConfirmAccess(Resources.User, Operations.Edit, usr);
if (!ModelState.IsValid)
{
return View(changeUsername);
}
//setup request
SupportUser su = (SupportUser)ViewData["SupportUser"];
RequestData rqd = new RequestData(GetApplicationIdentifer(usr), su.clientIP, su.lanID);
UserUsernameChangeRequest request = new AdminUsernameChangeRequest(rqd,usr,changeUsername.NewUsername);
UserUsernameChangeResponse response = UserManager.ChangeUserUsername(request);
if (response.Status == UserProcessorStatus.Success)
{
var message = ("User has been updated successfully");
TempData["message"] = message;
return PartialView("_NewUsername", changeUsername);
}
switch (response.Status)
{
case UserProcessorStatus.DuplicateUsername:
{
ModelState.AddModelError("NewUsername", "Duplicate username");
changeUsername.AlternateUsernames = response.AlternateUsernames;
return PartialView(changeUsername);
}
default:
{
ModelState.AddModelError("NewUsername", String.Format("An unexpected error occured. Error Code:{0}, Message:{1}", response.Status, response.Message));
return PartialView(changeUsername);
}
}
}
There it is.

The last time that I tried to do this was with MVC 3, but I do know that this technique worked then. It's a stupid difference, that's why I pointed that out first.
You were very close with your attempt at it, but you need to set the property as #Value, not #value. I never looked to hard into why the capital made a huge difference. (Like I said, it's a stupid difference.)
var val = "Dummy Value";
#Html.TextBoxFor(model => model.NewUsername, new { id = "uName_change", #Value = val })

Here's how I'd do it.... in the main page set up a viewmodel that contains the other view models like this:
public class VMMain(){
public void VMMain(){
//don't forget null CTOR as MVC requires it...
}
public void VMMain(Guid userguid, string newusername){
UserGuid = userguid;
NewUserName = newusername;
Part1 = new VM1(UserGuid, NewUserName);
Part2 = new VM2();
Part3 = new VM3();
}
public VM1 Part1 {get;set;}
public VM2 Part2 {get;set;}
public VM3 Part3 {get;set;}
Guid UserGuid {get;set;}
string NewUsername {get;set;}
}
In your main view you can bind to anyone of these viewmodels and properties within them. Just make sure you have a property available to bind.
This would change your controller to look like this:
[HttpGet]
[ClaimsPrincipalPermission(System.Security.Permissions.SecurityAction.Demand, Resource = Resources.User, Operation = Operations.Edit)]
public ActionResult ChangeUsername(Guid uniqueUserId)
{
User user = UserManager.GetUser(uniqueUserId);
AuthorizationHelper.ConfirmAccess(Resources.User, Operations.Edit, user);
var model = new VMMain(){UserGuid = user.Identifier,NewUsername = user.Username};
And now you can bind to say Partial ViewModel 1 that has that property like this:
#Html.TextBoxFor(p => model.Part1.NewUsername,
new { id = "uName_change" })
#Html.ValidationMessageFor(p => model.Part1.NewUsername)
return View(model);
}

Related

Kendo Grid changing depending on DropDownList

Before I start I'll just say that I've looked at other answers before posting and none specifically help me.
I need to create a Kendo UI grid in ASP.NET MVC that changes depending on what the users selects from a DropDownList. I will eventually be using data from a database, but currently I'm trying to learn with random hard-coded data.
I found a tutorial online that shows me how to do it with data from a sample database, but I can't set that up for reasons I cant explain. So I'm trying to adapt the code from that tutorial to work with my controllers and models. This might be set up completely wrong as I'm relatively new to ASP.NET MVC.
So here's the tutorial I'm trying to follow.
This is my controller:
public class LookupValueController : Controller
{
private List<LookupModel> tables = new
List<LookupModel>()
{ new LookupModel() { TableName = "Table1",
Description = "Table 1" },
new LookupModel() { TableName = "Table2",
Description = "Table 2" } };
private List<LookupValueModel> values = new List<LookupValueModel>()
{ new LookupValueModel() { TableName = "Table1", Description = "Value 1", LookupCode = "1" },
new LookupValueModel() { TableName = "Table2", Description = "Value 2", LookupCode = "2"} };
// GET: LookupValue
public ActionResult Index()
{
return View();
}
public ActionResult GetAllTableA()
{
try
{
var table = tables;
return Json(table, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Json(ex.Message);
}
}
public ActionResult GetAllTableB()
{
try
{
var value = values;
return Json(value, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Json(ex.Message);
}
}
}
Then my 2 models:
public class LookupValueModel
{
public string TableName { get; set; }
public string LookupCode { get; set; }
public string Description { get; set; }
}
public class LookupModel
{
public string TableName { get; set; }
public string Description { get; set; }
}
I've tried just changing the values in the view in the tutorial but it doesn't work, as I believe it isn't as simple as just changing some text.
I'm pretty stuck for how to do this and don't know where to go from here. I know this is a very long winded post with lots of code, but I would really appreciate some help.
Where am I going wrong adapting the tutorial code? What do I have to change to get it to work with hard-coded data?
That's not that hard. What you need to do is to change the DataSource's url for each Action you want. So, depending on what options user selects in the DDL, you change the DataSource. Check this demo.
What you need to change in from the above demo is that your grid's DataSource will call an url instead of a hard-coded json, right? In that url, you change the desired action:
let changeTableData = function changeTableData(option) {
let dataSource = new kendo.data.DataSource({
transport: {
url: "MyApp/" + option
}
});
$("#grid").data("kendoGrid").setDataSource(dataSource);
};
It will read the new url and fetch the data into the grid and updated it.
UPDATE
The transport url ir the url path to your action, e.g.
let url;
if (option == "A") {
url = "#Url.Action("TableA")";
}
else if (option == "B") {
url = "#Url.Action("TableB")";
}
let dataSource = new kendo.data.DataSource({
transport: {
url: url
}
});
1) Remove the grid from this view and create a new partialview and just have the grid located in that.
Now this can be one of two ways. Either an onclick via the drop down list or an onchange. Your choice
function Getdropdown()
{
var id = $("#//dropdownID").val(); //Get the dropdown value
var json = '{dropdownId: ' + id + '}';
$.ajax({
url:'#Url.Action("ViewGrid", "//Controller")',
type:'POST',
data:json,
contentType:'Application/json',
success:function(result){
$("//The Id of of the div you want the partial to be displayed in in the cshtml").html(result);
}
});
}
2) Get the value of the dropdown and pass it to a controller method that calls this new partial view, sending it the ID in a model
public ActionResult ViewGrid(int dropdownId)
{
AModel model = new AModel
{
DropDownID = dropdownId
};
return PartialView("_theGridPartial", model);
}
3) Change your grid to look like this:
#(Html.Kendo().Grid<KendoMvcApp.Models.EmployeeA>()
.Name("EmpGrid")
.Selectable()
.Columns(columns =>
{
columns.Bound(c => c.FirstName);
columns.Bound(c => c.LastName);
})
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action("GetAllEmployee", "GridDataSource", new {id = Model.DropDownID}))
)
)
4) This is the new Controller read
public ActionResult GetAllEmployee([DataSourceRequest]DataSourceRequest request, int id)
{
try
{
//Have a call that gets the table data based on the id you are passing into here. This id will be the table id you have got from your dropdown list
}
catch (Exception ex)
{
return Json(ex.Message);
}
}
This should allow you to change the table based on the dropdown.

Show buttons on a partial view based on some query

I am showing search results same as searching groups on facebook
enter image description here
I have a relationship Table named CommunityUser in Database having attributes CommunityID and UserID.
Using Partial View I want to show if User not already joined that Community/Group that it will show Join Button else if user already joined that community it will show Leave button.
I have written IsMember() function in my controller that takes two parameters, CommunityID and UserID. This will return true if that Community ID exist against that user ID.
public bool IsMember(string UserID, int CommunityID) {
var Membership = db.Users.Include(x => x.CommunityUsers).Where(s => s.Id.Equals(UserID)).Count();
if(Membership>0)
return true;
else
return false;
}
Now what I actually need is, I want to call this function in an IF condition on my view class. It is not allowing me to call this function on my view Class.
#if (){
<button>#Html.ActionLink("Leave", "LeaveCommunity", new { id = ViewBag.ComID })</button>
}
else
{
<button>#Html.ActionLink("Join", "joinCommunity", new { id = ViewBag.ComID })</button>
}
In your controller you should have a method which will return this view. So in this method you call this function
public ActionResult Index(string UserID, int CommunityID)
{
var hasMembership = IsMember(serID, CommunityID);
return View(hasMembership);
}
In the View it self then you just grab this variable hasMembership you just passed from #model.
#if (Model){
<button>#Html.ActionLink("Leave", "LeaveCommunity", new { id = ViewBag.ComID })</button>
}
else
{
<button>#Html.ActionLink("Join", "joinCommunity", new { id = ViewBag.ComID })</button>
}
Note: it might be wise to create some DTO class for passing data to a view, because you might need to pass multiple value to a view at some point. Plus the whole condition would be more readable
public SomeDTO {
public bool IsMember {get;set}
public List<Community> Communities {get;set;}
}
public ActionResult Index(string UserID, int CommunityID)
{
var hasMembership = IsMember(serID, CommunityID);
var listOfCommunities = _repo.GetComunities();
var dto = new SomeDTO
{
IsMember = hasMembership,
Communities = listOfCommunities
}
return View(dto);
}
#if (Model.IsMember){
// do or do not something
}

How can I resolve this error 'IEnumerable<SelectListItem>' in the drop down list MVC [duplicate]

This question already has answers here:
The ViewData item that has the key 'XXX' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'
(6 answers)
Closed 6 years ago.
In my MVC project I pass list of currencies to the view within the drop down list. However, once I try to post the view I get the following exception:
The ViewData item that has the key 'FromCurrencyId' is of type
'System.Int32' but must be of type 'IEnumerable'.
Currency Controller
namespace Project.Controllers
{
public class CurrencyController : Controller
{
[HttpGet]
// GET: Currency
public ActionResult Index()
{
CurrenciesClient Cur = new CurrenciesClient();
var listCurrency = Cur.findAll().ToList();
Currencies model = new Currencies();
model.FromCurrencies = new SelectList(listCurrency, "Id", "CurrencyName");
model.ToCurrencies = new SelectList(listCurrency, "Id", "CurrencyName");
return View(model);
}
[HttpPost]
public ActionResult Index(Currencies cur)
{
if (ModelState.IsValid)
{
if (cur.FromCurrencyId == cur.ToCurrencyId)
{
//do something if same currecnies and return.
ModelState.AddModelError("CurrencyCountry", "Can't make the conversion for the same value");
}
else
{
some code .....
}
}
return View(cur);
}
}
}
Currencies VM
namespace Project.ViewModels
{
public class Currencies
{
public int Id { get; set; }
[Required]
public int FromCurrencyId { get; set; }
public SelectList FromCurrencies { get; set; }
[Required]
public int ToCurrencyId { get; set; }
public SelectList ToCurrencies { get; set; }
public string CurrencyName { get; set; }
public string CurrencyCountry { get; set; }
[Required]
public decimal ConversionRate { get; set; }
public decimal Value { get; set; }
public SelectList AvailableCurrencies { get; set; }
}
}
CurrencyClient web service VM
namespace Project.ViewModels
{
public class CurrenciesClient
{
private string base_Url = "http://localhost:51646/api/";
public IEnumerable<Currencies> findAll()
{
try
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(base_Url);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("currencies").Result;
if (response.IsSuccessStatusCode)
{
var resposeData = response.Content.ReadAsStringAsync().Result;
var Currency = JsonConvert.DeserializeObject<List<Currencies>>(resposeData);
return Currency;
}
return null;
}
catch
{
return null;
}
}
}
}
Index View
model Project.ViewModels.Currencies
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm("Index", "Currency", FormMethod.Post))
{
#Html.TextBoxFor(m => m.ConversionRate, new { #size = "5" })
#Html.DropDownListFor(m => m.FromCurrencyId, Model.FromCurrencies as IEnumerable<SelectListItem>)
#Html.DropDownListFor(m => m.ToCurrencyId, Model.ToCurrencies as IEnumerable<SelectListItem>)
<button type="submit" class="btn btn-primary">Convert</button>
}
This problem is becuase you are passing null value from your dropdown.Means you are not selecting any value. Check that if you will pass some value from dropdown it will work fine.To solve this problem you need to add the same
code
Currencies model = new Currencies();
model.FromCurrencies = new SelectList(listCurrency, "Id", "CurrencyName");
model.ToCurrencies = new SelectList(listCurrency, "Id", "CurrencyName");
in your controller index post method.Because if selectListitem would be null the following code will be executed
IEnumerable<SelectListItem> selectList = o as IEnumerable<SelectListItem>;
if (selectList == null)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
MvcResources.HtmlHelper_WrongSelectDataType,
name, o.GetType().FullName, "IEnumerable<SelectListItem>"));
}
Which will throw exception.
(It would be better if you will use
ViewBag.FromCurrencies = new SelectList(listCurrency, "Id", "CurrencyName");
like this.)
The better description is given here:
The ViewData item that has the key 'XXX' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'
The whole explanation is also given in this link like how does this code work.
you will have to set the Dropdown data in the post controller as well, otherwise it will not be able to find the ViewBag values, as a result when after post action it calls the Index view, the ViewBag.FromCurrencies and ViewBag.ToCurrencies will be null which is obviously that we don't want.
For fixing the error, you will have to change you post action to be like:
[HttpPost]
public ActionResult Index(Currencies cur)
{
if (ModelState.IsValid)
{
if (cur.FromCurrencyId == cur.ToCurrencyId)
{
//do something if same currecnies and return.
ModelState.AddModelError("CurrencyCountry", "Can't make the conversion for the same value");
}
else
{
some code .....
}
}
CurrenciesClient Cur = new CurrenciesClient();
var listCurrency = Cur.findAll().ToList();
Currencies model = new Currencies();
model.FromCurrencies = new SelectList(listCurrency, "Id", "CurrencyName");
model.ToCurrencies = new SelectList(listCurrency, "Id", "CurrencyName");
return View(cur);
}
You should not return the view from your POST action or you will run into many issues. Here is the problem with that:
When you submit the form the URL is pointing to your Index. The body of your http post will have a bunch of Currencies items in it. Therefore, MVC, will submit the form to your Index method with Currencies cur parameter.
If all is well you return the same view.
If you refresh the page, your browser will simply reissue the last request and guess what, it will submit the form again. But this is not what you intended by refreshing. You wanted to get the form as it was originally presented not resubmit it.
Therefore instead of returning a view from a POST, you should always, except if AJAX was used, return a redirect.
In this case if all goes well, you may want to send the user to a success page or some other page so you should do this:
return RedirectToAction("YourActionName", "YourControllerName");
This pattern is called the PRG pattern. What this does is this:
user submits a form
If all goes well on the server side, you tell the browser to issue another request and get another page.
The browser gets the other page which may be a success page.
Now the user is on the success page. If they hit refresh they will get the success page again. They will not be submitting the same form over and over.

Set selected index of dropdown to zero after form submit in ASP.NET MVC

I am bit to new asp.net mvc and using aps.net mvc 5. I have create the below dropdown using html helpers in aps.net mvc. When i submit(post back) the form i want to set the selected index to zero. Here i am using a optionLabel "--select--". I want to set the selected value to that one ("--select--") after post back. How to achieve this. Please help. Thank you.
#Html.DropDownListFor(model => model.TestCategory, new SelectList(#ViewBag.TestCategories, "value", "text"), "-- Select --", new { #class = "form-control input-sm"})
Controller Code
[HttpGet]
public ActionResult Index()
{
var model = new LaboratoryViewModel {
medicaltestlist = new List<MedicalTest>()
};
PopTestCategory();
PopEmptyDropdown();
return View(model);
}
[HttpPost]
public ActionResult Index(LaboratoryViewModel labvm)
{
var test = PopMedicalTests().Where(x => x.TestSerial == Convert.ToInt32(labvm.TestCode)).FirstOrDefault();
if (labvm.medicaltestlist == null)
labvm.medicaltestlist = new List<MedicalTest>();
if(!labvm.medicaltestlist.Any(x=> x.TestSerial == test.TestSerial))
labvm.medicaltestlist.Add(test);
labvm.TestCategory = "";
PopTestCategory();
return View(labvm);
}
public void PopTestCategory()
{
var categorylist = new List<DropDownItem>
{
new DropDownItem{value="Medical",text="Medical"},
new DropDownItem{value="Animal",text="Animal"},
new DropDownItem{value="Food",text="Food"},
new DropDownItem{value="Water",text="Water"}
};
ViewBag.TestCategories = categorylist;
}
public class DropDownItem
{
public int id { get; set; }
public string value { get; set; }
public string text { get; set; }
}
You return the view in you post method so if you selected (say) Animal then that value will be selected when you return the view because the html helpers use the values from ModelState, not the model property. Setting labvm.TestCategory = ""; has no effect. The correct approach is to follow the PRG pattern and redirect to the GET method, however you can make this work by calling ModelState.Clear(); before setting resetting the value of TestCategory although this will clear all ModelState properties and errors and may have other side effects.
Side note: You DropDownItem class seems unnecessary. MVC already has a SelectListItem class designed to work with dropdownlists, and in any case you can replace all the code in your PopEmptyDropdown() method with
ViewBag.TestCategories = new SelectList(new List<string>() { "Medical", "Animal", "Food", "Water" });
and in the view
#Html.DropDownListFor(m => m.TestCategory, (SelectList)#ViewBag.TestCategories, "-- Select --", new { #class = "form-control input-sm"})
If you set the "value" attribute of the top item in the drop down list to something and then pass back a model containing that for the bound property it should work?

Convert Dropdownlist item's value to int

I need the value of SelectListItem to be int.
So I pull it of the database, convert it to string in the process and store to listitem's value.
public class BookAdd
{
public BookAdd()
{
public Book Book { get; set; }
DataModelContainer db = new DataModelContainer();
IEnumerable<SelectListItem> items = db.PublisherSet
.Select(i => new SelectListItem
{
Value = SqlFunctions.StringConvert((double)i.Id),
Text = i.Name
});
}
}
I then need to store the value again as int to Book.PublisherId when selected from dropdownlist. I know the code below is not complete, I figured I need somehow convert the selected item's value to int, how do I do it?
#model Bookshops.Models.BookAdd
...
#Html.DropDownListFor(model => model.Book.PublisherId, Model.items);
And finaly controler:
public ActionResult Create2()
{
var bookAdd = new BookAdd();
ViewBag.Publisher = bookAdd.items;
return View(bookAdd);
}
//
// POST: /Book/Create
[HttpPost]
public ActionResult Create2(BookAdd book)
{
if (ModelState.IsValid)
{
Book book2 = new Book();
book2.Id = book.Book.Id;
book2.AuthorId = book.Book.AuthorId;
book2.Isbn = book.Book.Isbn;
book2.Id = int.Parse(book.Book.PubId);
book2.Title = book.Book.Title;
book2.YearPublished = book.Book.YearPublished;
db.BookSet.Add(book2);
db.SaveChanges();
}
return RedirectToAction("Index");
}
I'm using MVC 3, EF 4.
Instead of building a SelectListItem, I built something like this:
public struct BookItem
{
int id;
string name;
}
Then add the following item to the model and select data into it
IEnumerable<BookItem> bookList {get; set;}
Fianally
#Html.DropDownListFor(model => model.Book.PublicherId, new SelectList(Model.bookList, "id", "name"))
When the selected Item of the DropDownList will get posted back convert it to int from string like this Convert.ToInt32(DropDownSelectedItem) or with Int32.TryParse(value, out number)
to start you can simplify the mapping code to
db
.PublisherSet
.Select(x=> new SelectListItem{Value = x.Id.ToString(), Text = x.Name})
.ToArray();
In the view you can then do
#Html.DropDownListFor(model => model.Book.PublisherId, Model.items);
and it will properly render a select element with the options and the appropriate publisher selected.
You can then use the model binding conventions to pass this back to the controller action when the user clicks the submit button.
In the end, it's all text. that's the nature of the web. we have a lot of tricks, converters and binders to turn string dictionaries into rich view models, but ultimately it's just strings.

Categories

Resources