I have a Person DB table that has a surname, name, and IdCity fields.
In the View, I've implemented an autocomplete field to let the user digit the city.
How should I design the ViewModel for handling the IdCity and how can I pass it to the controller? It is a string but I need to pass the id and eventually insert it in the DB if it does not exist there yet.
Receive the city name in your view action, then look if a city exists for this name. If it does, use its ID. If it doesn't create a new one, and use that ID:
public ActionResult Update(PersonModel model)
{
var city = _cityRepository.GetCityByName(model.CityName);
if (city == null) _cityRepository.Add(city);
// At this point city.Id contains your city Id
var person = new Person
{
...
CityId = city.Id,
...
};
// Proceed to save your Person object
}
Related
I don't know if this is something that is possible or not, but I'm having trouble finding the correct words to search for answers.
Let's say I have a "Location" model with these two properties. City corresponds to a column in the table with a foreign key to a lookup table of all cities in the local area. CityOther is a free-text field that allows users to enter any city name they want.
public int? City { get; set; }
public string CityOther { get; set; }
I have a Kendo ComboBox that's configured something like this:
#(Html.Kendo().ComboBoxFor(m => m.CityOther)
.DataTextField("Text")
.DataValueField("Value")
.Placeholder("Select City")
.Filter(FilterType.Contains)
//.DataSource(d => ...)
)
When I serialize the form, I see two inputs with the names CityOther and CityOther_input.
When an existing city from the picklist is chosen, the values look like this:
{ name: 'CityOther' , value: '3' }
{ name: 'CityOther_input', value: 'Springfield' }
When a free-text city (one not from the list) is entered, the values then look like this:
{ name: 'CityOther' , value: 'Orlando' }
{ name: 'CityOther_input', value: 'Orlando' }
I would like to configure it so that:
a value selected from the existing list would end up in the City field (which means I would change it to .ComboBoxFor(m => m.City)).
a free-text value entered would end up in the CityOther field, rather than XYZ_input.
Is this possible?
Would model binding fail if the value in City (a nullable int property) was a string value?
I have categories table, see its design:
And I want to display this nested categories inside dropdown list like this image:
Can anybody help me to find a solution?
You should retrieve all your categories, ordered by ParentId and then by Name if you need that. You should do this either in your controller or better trough a Service layer that talks to your repository or EntityFramework Datacontext. You didn't specify your data access strategy.
Then in your controller, you'll have to iterate over all categories and create each item taking into account the parent relationship. For example, if you iterate over "top categories" you could then add all child categories of the current category. If you have more than 1 level nesting, you would have to use recursion.
Pseudo-code written directly here, probably won't compile as-is:
//Assume you want to produce a SelectList to use in your View, let's use a view model like the following
public class CategoryViewModelItem
{
public string Value {get;set;}
public string Text {get;set;}
}
In your controller/service layer:
List<CategoryViewModelItem> items = new List<CategoryViewModelItem>();
//get all of them from DB
List<Category> allCategories = dataContext.Categories.ToList();
//get parent categories
List<Category> parentCategories = allCategories.Where(c => c.ParentId == null)
.OrderBy(c => c.Title);
foreach(var cat in parentCategories)
{
//add the parent category to the item list
items.Add(new CategoryViewModelItem { Value = cat.Id, Text = cat.Title });
//now get all its children (separate function in case you need recursion)
GetSubTree(allCategories, cat, items);
}
private void GetSubTree(IList<Category> allCats, Category parent, IList<CategoryViewModelItem> items)
{
var subCats = allCats.Where(c => c.ParentId == parentId);
foreach(var cat in subCats)
{
//add this category
items.Add(new CategoryViewModelItem { Value = cat.Id, Text = parent.Title + " >> " + cat.Title });
//recursive call in case your have a hierarchy more than 1 level deep
GetSubTree(allCats, cat, items);
}
}
Then to render your SelectList, you could send the SelectList as your model (or part of it) for your view:
//in your controller's action
SelectList select = new SelectList(items, "Value", "Text");
return View(select);
And use that in your View:
#Html.DropDownList("Categories", #Model)
I'm not sure I understand the best way of doing this.
If I have a model with a large number of fields, then do I have to explicitelly list every one of them in a whitelist under TryUpdateModel, or can I just pass the ForCollection.
The following code doesn't save my edits, is my only alternative to list all my fields one by one?
public ActionResult Edit(int id, FormCollection form)
{
var jobToUpdate = db.Jobs
.Include(x => x.JobNotes)
.Where(x => x.JobID == id)
.SingleOrDefault();
if (TryUpdateModel(jobToUpdate, form))
{
db.Entry(jobToUpdate).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Details", new { id = model.Job.JobID });
}
return RedirectToAction("Details", new { id = model.Job.JobID })
}
Secondly, what is the best way to get a list of just the fields that have changed. If the only field that the user changes is the FirstName field, I'd like to record that in an audit log.
Thanks for your help!
If there are fields on your model that aren't in the form and you don't want users to change then you can use an exclude list. The choice to use an include or exclude list will depend which is largest. An include list is more secure as if you forget to include something it can't be changed. Not using an include, or exclude list will leave you vulnerable to model stuffing where users can post extra values to change details they shouldn't be able to.
public ActionResult Edit(int id, FormCollection form)
{
var jobToUpdate = db.Jobs
.Include(x => x.JobNotes)
.Where(x => x.JobID == id)
.SingleOrDefault();
if (TryUpdateModel(jobToUpdate, String.Empty, null, new [] {"SecretField"}, form))
{
db.SaveChanges();
return RedirectToAction("Details", new { id = model.Job.JobID });
}
// Model not saved - send them back to edit page for corrections
return View(jobToUpdate);
}
If the model is not saved you should not redirect. Show them the same page and make sure your edit view shows model errors.
The most likely reason your code is not saving the model is you're trying to insert a value that is not valid.
I have a create method in my controller and this opens up a view. On the view the user can enter data to populate the model. Some of that data comes from select lists. These select lists are populated from the database.
What I would like to know is should I:
a) Get data for the select lists in the controller, populate a field like this:
public IEnumerable<SelectListItem> Statuses { get { return GetStatusType(); } }
pass Statuses to the model and then do a for () to loop through statuses and create a select list and options HTML
b) Do nothing in the controller and in the view have the following in the model:
<select id="Q_StatusID" name="Q.StatusID">#Html.Raw(
SelectHelper.Status(false, #Model.PageMeta.StatusID))</select>
Where the SelectHelper is C# code that gets all the select list and options HTML.
c) Some better way:
I would go with the first one. some thing like this
a helper method
public List<SelectListItem> getAllSelectList(List<Items> lists)
{
List<SelectListItem> selectListItems = new List<SelectListItem>();
foreach (Term term in lists)
{
selectListItems.Add(new SelectListItem() { Text = term.yourselectfield, Value = term.your value });
}
return selectListItems;
}
on your controller
//assuming you GetStatusType() method will return list of objects
ViewData.selectlist=getAllSelectList(GetStatusType());
on your view, if you are using Razor
#Html.DropDownList("selectlist", null, "Choose")
or
<%: Html.DropDownList("selectlist", null, "Choose") %>
Create a static Look-up class & static method for your GetStatusType there.
Cache all the status types after first time loading from your database.
Call GetAllStatusType from the view to display.
I would create a view model, which has a IEnumerable< Statuses > property. Then in your view to display the select element :
#Html.DropDownListFor(model => model.PageMeta.StatusID, new SelectList(Model.Statuses, "Id", "Name"), "Some default which has no value")
Where Id and Name are set to the appropriate properties in your Statuses model.
When typing in my URL
SalaryFor/Microsoft/Balmer
i need to display salary for user with name Balmer
and if I type in URL
SalaryFor/Microsoft
i need to display salary for all employee in Microsoft company
Is it possible?
because when I use this link
SalaryFor/Microsoft/Balmer
all works fine
public ActionResult Salary(string company, string person)
both company and person contains values
but this link does not work
SalaryFor/Microsoft
public ActionResult SalaryFor(string company, string person)
both values contains null
my route in global.asax is
routes.MapRoute("Salary",
"{controller}/{action}/{company}/{position}",
new
{
controller = "Salary",
action = "SalaryFor",
company = "",
test = ""
});
O maybe I am making something wrong?
Thanks,
Alexander.
You need to map a second route.
routes.MapRoute("Salary2",
"{controller}/{action}/{company}",
new
{
controller = "Salary",
action = "SalaryFor",
company = ""
});