I know that I can set the SelectedItem in my controller, but I can't figure out how to set it in my view. I'm working on a sort of flashcard (study guide) application and I have imported about 400 test questions. Now I want to write a page for the instructor to be able to select a "Category" for each question. I'd like them to be able to update the category for all the questions on one page. My model has a question entity that contains a foreign key field to the category entity (the field is called QuestionCategory). So, my view is based on the Question entity, but I'm sending over the list of Categories (there are 14) in the ViewBag (so I don't have to send a full SelectList over with each of the 400 questions. As my view is iterating thru the items in my View, I just want to add a SelectList that contains the 14 categories in my ViewBag and then set the SelectedItem based on the value of item.QuestionCategory. I can't make it work.
Here's my controller action:
public ActionResult Index()
{
var context = new HBModel.HBEntities();
var query = from q in context.tblQuestions.Include("tblCategory") select q;
var questions = query.ToList();
ViewBag.Categories = new SelectList(context.tblCategories, "CategoryID", "CategoryName");
return View(questions);
}
Here's some of the things I've tried in the view (with associated error messages in the comments)
#model IEnumerable<HBModel.tblQuestion>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
Question
</th>
<th>
Answer
</th>
<th>
AnswerSource
</th>
<th>
Category
</th>
<th>
Action
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#item.Question
</td>
<td>
#item.Answer
</td>
<td>
#item.AnswerSource
</td>
<td>
#item.tblCategory.CategoryName
#*This one works, but cannot initialize the selected item to be current database value*#
#Html.DropDownList("Categories")
#*compile error - CS0200: Property or indexer 'System.Web.Mvc.SelectList.SelectedValue' cannot be assigned to -- it is read only*#
#*#Html.DropDownListFor(m => item.QuestionCategory, (ViewBag.Categories as SelectList).SelectedValue = item.QuestionCategory)*#
#*error {"DataBinding: 'System.Web.Mvc.SelectListItem' does not contain a property with the name 'CategoryId'."}*#
#Html.DropDownListFor(m => item.QuestionCategory, new SelectList(ViewBag.Categories, "CategoryId", "CategoryName"))
#*error - {"DataBinding: 'System.Char' does not contain a property with the name 'CategoryId'."}*#
#Html.DropDownListFor(m => item.QuestionCategory, new SelectList("Categories", "CategoryId", "CategoryName"))
)
</td>
<td style="width: 100px">
#Html.ActionLink("Edit", "Edit", new { id = item.QuestionID }) |
#Html.ActionLink("Delete", "Delete", new { id = item.QuestionID })
</td>
</tr>
}
</table>
Of course, if I can get this to work, I'll need to try and add an action to go back to the controller and update all the records, but I'll just be happy to resolve my current issue.
I would really appreciate any help on this - Thanks!
You need to explicitly create the options in the select tag, using #Html.DropDownList, as follows (taken from a working app):
#Html.DropDownListFor(model => model.IdAccountFrom, ((IEnumerable<FlatAdmin.Domain.Entities.Account>)ViewBag.AllAccounts).Select(option => new SelectListItem {
Text = (option == null ? "None" : option.AccountName),
Value = option.AccountId.ToString(),
Selected = (Model != null) && (option.AccountId == Model.IdAccountFrom)
}), "Choose...")
#Html.ValidationMessageFor(model => model.IdAccountFrom)
You obviously need to change to the properties on your #Model.
NOTE:
This code was auto-generated by the MvcScaffolding NuGet package when I scaffolded a controller.
This package requires you to use Entity Framework Code First POCO classes for your entities. These are easy to generate from an existing database using the Entity Framework Power Tools CTP.
With MVC, you need to spend some time researching the tooling that is out there to help you generate the stuff that you need. Using these tools is a great way to get started and to see how to do things. You can then tweak the output to your heart's content.
Related
We use Knockout.js and MVC in a Webapp and are having an issue, where the order merchandise total observed is 0 by Knockout.js, as displayed in the GUI.
The code seems to be working intermittently, because sometimes we do show the merchandise total in other orders. All orders use the same code and a merchandise total non zero value is always seen in the
OrderDetailClientViewModel object within the controller before being passed to the view. The view is coded in Knockout.js and plain javascript.
The "Totals" property shown below is an object (Models.OrderTotalViewModel), which contains the "MerchandiseTotal" field, which is sometimes showing a 0. I'm showing the relevant parts of the two view files below. I don't see the merchandiseTotal value being modified erroneously in the code. Only helpful segments are shown below.
Any suggestions to fix this intermittent behavior would be greatly appreciated!!!
Thank you,
Ken
--Edit.cshtml
#model Models.OrderEditPageViewModel
...
var rawViewModel = #(Html.ToJson(Model.OrderDetailClientViewModel()));
var viewModel = ko.mapping.fromJS(rawViewModel,
{
'HeaderData': {
create: function(options) {
options.data.StartDate = new Date(options.data.StartDate);
options.data.EndDate = new Date(options.data.EndDate);
var headerData = ko.mapping.fromJS(options.data);
headerData.errors = new HeaderDataError();
return headerData;
}
},
'Totals': {
create: function(options) {
var totals = ko.mapping.fromJS(options.data);
totals.errors = new TotalsError();
totals.isPromoCodeApplied = ko.observable(false);
if ($.trim(totals.PromoCode()) !== "")
totals.isPromoCodeApplied(true);
return totals;
}
}
});
...
-- This segment in Edit.cshtml shows where the OrderTotalViewModel.cshtml is called for Totals.
<tr>
<td colspan="3">
#Html.EditorFor(m => m.Totals)
</td>
</tr>
--OrderTotalViewModel.cshtml
#model Models.OrderTotalViewModel
#{ Layout = null; }
...
<td colspan="3" style="text-align:right">
<table class="PaddedTable" style="width:925px">
<tbody>
<tr>
<td style="width:125px;text-align:right">Merchandise Total:</td>
<td style="text-align:right" class="NormalTextBold">
#(Html.Knockout().SpanFor(m => m.MerchandiseTotal)
.HtmlAttributes(new { #class = "NormalTextBold" })
.Currency())
So what I have is a form created with the beginform extension like this
using (Html.BeginForm("SendEmail", "Email", FormMethod.Post, new { id = "emailForm",
onsubmit = "return Check();"})){
inside I created some Kendo Ui widget like this
<table>
<tr>
<td>#Html.LabelFor(x => x.Senders)</td>
<td>
#(Html.Kendo().DropDownList()
.Name("Sender")
.DataTextField("Text")
.DataValueField("Value")
.BindTo(Model.Senders))
</td>
</tr>
<tr>
<td>#Html.Raw(Server.HtmlDecode(#Model.RecipientTable))</td>
</tr>
<tr>
<td colspan ="2">
#(Html.Kendo().MultiSelect()
.Name("Users")
.DataTextField("Name")
.DataValueField("Id")
.Placeholder("Optional - Choose additional users to send emails to:")
.ItemTemplate("#=LastName #, #=FirstName # #=MiddleInitial #")
.TagTemplate("#=LastName #, #=FirstName # #=MiddleInitial #")
.BindTo(Model.OptionalUsers))
</td>
</tr>
in my controller Email I have this method
[HttpPost]
public bool SendEmail(EmailModel Email){ .. stuff....}
Where the EmailModel is tightly bind to the view that contains the form from above. The question and trouble I am having is that is it possible and if so how, to have the model passed to the method containing information about what the user chose? Or is it that I can not use the form's submit and will have to manually get the value and pass it as a JSON to the controller via custom function that does a ajax call?
I thought I read that you weren't using post. The only items that are returned automatically through the post are fields that have been put in a for helper. What we do is
#Html.DropDownListFor(x => x.Sender, new { #class = "ddlSender" })
then in the script we initialize the kendo part of it
$('.ddlSender').kendoDropDownList();
this way the model item is put in a for helper so it gets posted back to the controller and you get the benefits of the kendo dropdown. Hope this helps
I'm working on a Nopcommerce, and need to generate Invoice (custom made not as what they already provide, because it just doesn't solve our purpose). We need to generate Invoice
in new tab(using another cshtml file) using Controller's method also I'm passing model data on view.
<tr>
<td class="adminTitle">
#Html.NopLabelFor(model => model.ProbableDeliveryDate):
</td>
<td class="adminData">
#Html.EditorFor(model=>model.ProbableDeliveryDate)
</td>
</tr>
<tr>
<td>
#if(Model.CanGenrateInvoice)
{
<input type="submit" name="generateinvoice" value="#T("Admin.Orders.Fields.generateinvoice")" id="generateinvoice" class="adminButton" />
}
</td>
</tr>
I've to post data to get value of probableDeliveryDate to controller method and after that want to open view in new tab.
How can i do this?
If you are getting to the action from the first page via an Html.ActionLink you can do this:
Html.ActionLink("Open Invoice", "ActionName","ControllerName", new { id = Model.InvoiceID }, new { target = "_blank" });
Specifying target = "_blank" will open in the new tab
Update
Since you are posting the model to the controller (I was hoping RedirectToAction could help open a new window/tab but that doesn't look to be the case)
My spidy sense is tingling on the flow you have tho... This is just me but I would do something a little different.. such as
Post the model to the controller
Save the data that generates the invoice
Return the InvoiceID to the action
Add the InvoiceID to the model
Send the model back to the view
Inform the user that
the invoice was generated and show a link - like above - that allows the user to open the invoice OR
this provides the perfect clean solution to show model errors if there were any
Your view could have a piece of razor code that did that:
#{
if(Model.InvoiceID != null && Model.InvoiceID !=0) {
#Html.ActionLink("Open Invoice", "ActionName","ControllerName", new { id = Model.InvoiceID }, new { target = "_blank" });
}
}
I know this sound somewhat off-piste but how would you create a weakly typed view where you pass in a collection of objects and iterate in razor accordingly and display in a table?
-- Controller View --
???
-- Razor View ---
#foreach (var item in Model)
{
<tr>
<td>
#item.attr1
</td>
<td>
#item.attr2
</td>
</tr>
}
Frist you know the data send
From Controller -------> view by two way
By weak type view
and by strong type view
there is no other way of passing data from controller to view ...(remember)
what is intelliscence ----> which show the related sub property of any model
like we write Model. --------> then all property show in
droupdown list after dot(.).
A.what is weak type view
This is used without using model i.e like using ViewBag and other.
There is no intellisence for this type of view and it is complicated, and when you write
any name which not exist then it give at runtime error.
Ex.
.............Controller
ViewBag.List = List<job>;
return View();
.............Razor View
#foreach(var item in ViewBag.List)
{
// when you write no intellisence and you want to write your own correct one...
#item.
}
B. What strongly type view
this is used model to send data from controller to view an vice-versa.
Model are strongly typed to view so, it show intellicence and when you write wrong
then there only error show at compile time..
Ex.
.................Controller
List<job> jobdata =new List<job>();
return View(jobdata);
................view
//Mention here datatype that you want to strongly type using **#model**
#model List<job>
#foreach(var item in Model)
//this **Model** represent the model that passed from controller
// default you not change
{
#item. //then intellisence is come and no need write own ....
}
that is weak and strong type view ......
So now you solve any problem with this basic.....
may I hope it help u....
But it is best to use Strongly Typed view so it become easy
to use and best compare to weak...
#model dynamic
Will do what you want, I believe.
If its going to be a collection, then maybe use
#model ICollection
It's not weakly typed. It's typed to a collection of some kind.
#model IEnumerable<MyClass>
OK, late to the party I know, but what you're after is a view stating "#model dynamic" as already stated.
Working Example: (in mvc3 at time of posting)
NB In my case below, the view is actually being passed a System.Collections.IEnumerable
As it's weakly typed, you will not get intelesense for the items such as #item.Category etc..
#model dynamic
#using (Html.BeginForm())
{
<table class="tableRowHover">
<thead>
<tr>
<th>Row</th>
<th>Category</th>
<th>Description</th>
<th>Sales Price</th>
</tr>
</thead>
#{int counter = 1;}
#foreach (var item in Model)
{
<tbody>
<tr>
<td>
#Html.Raw(counter.ToString())
</td>
<td>
#item.Category
</td>
<td>
#item.Description
</td>
<td>
#item.Price
</td>
</tr>
#{ counter = counter +1;}
</tbody>
}
</table>
}
Edit: removed some css
I'm trying to list two radio buttons in each row of a table, but I haven't been able to assign unique IDs to each radio button. I'd like to assign the IDs based on the #item.myID as follows:
#foreach (var item in Model)
{
<tr>
<td>
#Html.RadioButton("Yes", #item.myID, #item.IsCool, new { id = "#item.myID", autopostback = "true" })
#Html.RadioButton("No", #item.myID, !#item.IsCool, new { id = "#item.myID", autopostback = "true" })
</td>
</tr>
}
However, the IDs keep rendering literally as "#item.myID". In other words, it's not treating the # sign as a special character. I've also tried using parenthesis, like this: "#(item.myID)".
You need to remove the quotes from around the id assignment, like so;
#foreach (var item in Model)
{
<tr>
<td>
#Html.RadioButton("Yes", item.myID, item.IsCool, new { id = item.myID, autopostback = "true" })
#Html.RadioButton("No", item.myID, !item.IsCool, new { id = item.myID, autopostback = "true" })
</td>
</tr>
}
Also, note that you don't need the additional "#" symbol when you are already in the context of another Razor code block - the Razor View Engine is pretty clever :)