I want to do a foreach using two viewbag thus in the view, but I get an error in the second foreach.
#foreach (var item in ViewBag.stages)
{
<div id="style101">
<h2><span> <strong>#item.NameStage</strong></span></h2>
</div>
foreach (var item2 in (ViewBag.actions.IdStage == item.IdStage ))
How I can do this?
Try to avoid using ViewBag / ViewData to pass data from your action methods to view. You must have realized now that this made your view a bit dirty. Use strongly typed view models.
Assume your view is to create something. So create a view model for that. Assuming you have multiple Actions in each stage.
public class CreateSomeThingVM
{
public string Title { set;get;}
public List<Stage> Stages { set;get;}
public CreateSomeThingVM()
{
Stages=new List<Stage>();
}
}
public class Stage
{
public int ID { set;get;}
public string StageName { set;get;}
public List<Action> Actions { set;get;}
public Stage()
{
Actions =new List<Action>();
}
}
public class Action
{
public int ID { set;get;}
public string ActionName { set;get;}
}
And in your Action method, get the data and set the properties.
public ActionResult Create()
{
var vm=new CreateSomeThingVM();
vm.Stages=GetListOfStagesFromSomeWhereWithItsActions();
return View(vm);
}
Assumuing GetListOfStagesFromSomeWhereWithItsActions method returns a list of Stage object with its proper Actions.(You should do your filtering here as needed to get the relevant actions for each stage).
Now in your View which is strongly typed to our CreateSomeThingVM view model, write some clean code
#model CreateSomeThingVM
#foreach(var stage in Model.Stages)
{
<h2>#stage.StageName</h2>
foreach(var action in Model.Actions)
{
<p>#action.ActionName</p>
}
}
It looks like the expression in your second loop after the in keyword evaluates to a boolean rather than an IEnumerable.
Did you mean to use .where() to filter an enumerable instead?
Perhaps:
Viewbag.actions.where( a => a.IdStage == item.IdStage)
Related
I have a view model that is used to display a form on one view, and then is also used to represent the POST data to an action. The action then displays another view model that contains much of the same data from the first view model. However, the first view model has several "display only" properties that are also required on the second view model (for display only on the second view also).
I am wondering what the best way to pass this "display only" data to the second view would be. Currently, the best solution I have come up with is to have a bunch of hidden form fields that contain the display only property values, and then the model gets auto-populated for the action that handles the form POST. However, using hidden form fields seems very "hackish", and there seems like there should be a better solution to passing this data to another view The action doesn't need the display only information, it is only accessing it to populate the properties of the second view model that is passed to the second view.
Let me just explain my question with code, as what I am after is probably better understood through code than words.
Models:
public class SearchFilters
{
// ...
}
public class SearchResult
{
public int Id { get; set; }
public bool Selected { get; set; }
public string SomeDisplayValue1 { get; set; }
public string SomeDisplayValue2 { get; set; }
// ...
}
public class ResultsViewModel
{
public IList<SearchResult> Results { get; set; }
// ...
}
public class DoSomethingWithSelectedResultsViewModel
{
public IList<SearchResult> SelectedResults { get; set; }
public string SomeOtherProperty { get; set; }
// ...
}
Controller:
[HttpPost]
public ActionResult Results(SearchFilters filters)
{
ResultsViewModel results = new ResultsViewModel();
// ...
return new View(results);
}
[HttpPost]
public ActionResult DoSomethingWithSelectedResults(ResultsViewModel model)
{
// ...
return View(new DoSomethingWithSelectedResultsViewModel
{
SelectedResults = model.Results.Where(r => r.Selected).ToList(),
SomeOtherProperty = "...",
// ...
});
}
View: Results.cshtml
#model ResultsViewModel
#using (Html.BeginForm("DoSomethingWithSelectedResults", "Search"))
{
<table>
for (int i = 0; i < Model.Results.Count; i++)
{
<tr>
<td>
#Html.CheckBoxFor(m => Model.Results[i].Selected)
#* I would like to eliminate these hidden inputs *#
#Html.HiddenFor(m => Model.Results[i].Id)
#Html.HiddenFor(m => Model.Results[i].SomeDisplayValue1)
#Html.HiddenFor(m => Model.Results[i].SomeDisplayValue2)
</td>
<td>#Html.DisplayFor(m => Model.Results[i].SomeDisplayValue1)</td>
<td>#Html.DisplayFor(m => Model.Results[i].SomeDisplayValue2)</td>
<tr>
}
</table>
<button type="submit">Do Something With Selected Results</button>
}
As far as I know, one of the best way to pass data from View to another View through a Controller is to use ViewBag, ViewData or TempData. As an example, you can pass the data retrieved from View I as shown below:
TempData[DataToBePassed] = model.CustomData;
And then retrieve this data in View II similar to that:
#if(TempData[DataToBePassed] != null)
{
var dataFromFirstView = TempData[DataToBePassed];
}
For more information take a look at When to use ViewBag, ViewData, or TempData in ASP.NET MVC 3 applications.
You could put the model in the TempData property of the controller, that way it's automatically available in the next request.
More here
Found what I was looking for, I just hadn't worked with MVC enough yet to know about it. The Controller.UpdateModel method does exactly what I was looking for.
Example (using the code from the question):
[HttpPost]
public ActionResult DoSomethingWithSelectedResults()
{
// Load initial model data here, in this case I had simply cached the results in
// temp data in the previous action as suggested by Emeka Awagu.
ResultsViewModel model = (ResultsViewModel)TempData["results"];
// Call UpdateModel and let it do it's magic.
UpdateModel(model);
// ...
return View(new DoSomethingWithSelectedResultsViewModel
{
SelectedResults = model.Results.Where(r => r.Selected).ToList(),
SomeOtherProperty = "...",
// ...
});
}
Using this method I was able to eliminate all the hidden form fields and did not have to write any custom copy logic, since UpdateModel deals with it automatically.
Note: I did have to implement some custom model binders to get things to work correctly with dictionaries and collections (see here, here, and here).
My Problem is like this ,Im trying to get a model object from a view after seinding it with a form,the model Looks like this:
public class PackageModel
{
public PackageDTO Package { get; set; }
public IEnumerable<SelectListItem> Allcategories { get; set; }
}
while PackageDTO is just an DTO object conatining many attributes.
Now the view for this model,ist just showing the attributes and this model will be sent within a httppost request to the index page as normal(there it will be processed ans saved ),
the index method in the Controller Looks like this:
[HttpPost]
public ActionResult Index(PackagemODEL packageModel, FormCollection form)
{
}
Now i dont know what im doing wrong,but the Object packageModel is not totally null,just the list Allcategories and another string Attribute in the PackageDTO object,the rest seems to be working.
The view contains this code
<fieldset>
<legend>#Resources.AppvManagementService_EditPackage_Title</legend>
#using (Html.BeginForm("Index","WantedController",FormMethod.Post,new {enctype="multipart/form-data"}))
{
#Html.ValidationSummary()
<labelName </label>#Html.TextBoxFor(model=>model.Package.Name) <br/>
<label>Sid </label>#Html.TextBoxFor(model=>model.Package.Sid,new {#disabled="disabled"}) <br/>
<label>Category </label>#Html.DropDownList("CategoryName",Model.Allcategories,Model.Package.Category)<br/>
<label>Description: </label>#Html.TextBoxFor(model=>model.Package.Description) <br/>
<label>Type: </label>#Html.TextBoxFor(model=>model.Package.Type) <br/>
<button type="submit">submit</button>
}
Doest anyone have any idea why ist like this?? am i doing something wrong(im sure i am :))
thx for every one
How do you expect Allcategories to be populated? Your view contains a field, which posts a value under the name "CategoryName" - there's nothing in your view that populates a list of categories. More importantly; to you really need it to be populated? It seems to me that Allcategories is only really needed for populating the dropdown in the view. On the post, you shouldn't need it. If you DO still need it, you're going to have to either:
Repopulate it in the controller on the HttpPost method:
[HttpPost]
public ActionResult Index(PackagemODEL packageModel, FormCollection form)
{
packageModel.Allcategories = new IEnumerable<SelectListItem>();
}
Clutter up your view with pointless hidden fields to pass the values back in (I wouldn't recommend this for a list of items unless you really need to):
#for (int i = 0; i < Model.Allcategories.Count; i++)
{
#Html.HiddenFor( m => m.Allcategories[i])
}
Populate it in the model constructor:
public class PackageModel
{
public IEnumerable<SelectListItem> Allcategories { get; set; }
public PackageModel()
{
Allcategories = new IEnumerable<SelectListItem>();
/* Add values to Allcategories here */
}
}
If the values of Allcategories doesn't change, you could also consider making it a static readonly property of your model and hardcoding the values (or pulling them from a config file or similar).
As for getting back the selected CategoryName, you need a field in your model in which to store it, otherwise the only way to access it at the moment is via Request.Form:
public class PackageModel
{
public IEnumerable<SelectListItem> Allcategories { get; set; }
public string CategoryName { get; set; }
}
#Html.DropDownListFor(m => m.CategoryName, Model.Allcategories, Model.Package.Category)
An aside: Please, please, please take your DTO out of your model and set appropriate properties in your model itself. Your DTO does not belong in your view model, which is a model for your view and nothing more.
I have a controller like this:
public ActionResult Index()
{
ViewBag.Title = "Index";
ViewBag.LoggedIn = TheUser.CheckStatus();
return View();
}
Thing is, I have to set LoggedIn to the output of my other function TheUser.CheckStatus() so that I can reference it with razor... Is there a way in Razor to access a function straight off? for example...
#TheUser.CheckStatus
instead of
#ViewBag.LoggedIn
The recommended way in MVC for passing information to a view is to create a model specific to that view (aka view model) e.g.
public class IndexViewModel
{
public string Title { get; set; }
public bool IsAuthenticated { get; set; }
}
....
public ActionResult Index()
{
return View(new IndexViewModel()
{
Title = "Index",
IsAuthenticated = UserIsLoggedIn()
});
}
However, to answer your question:
Is there a way in Razor to access a function straight off?
If you are using ASP.NET Membership you can use the IsAuthenticated property on the request e.g.
#Request.IsAuthenticated
Otherwise, you do need to pass this information to the view (whether that be via ViewBag/view model etc.)
Alternatively, you could write your own extension method for Request which would allow you to access it directly in the view:
#Request.UserLoggedIn()
Or even as a HtmlHelper e.g.
public static class HtmlHelperExtensions
{
public static bool UserIsLoggedIn(this HtmlHelper helper)
{
return /* authentication code here */
}
}
Then in your views you can use #Html.UserIsLoggedIn() which I think is what you are after.
use a ViewModel class (your view will then be strongly typed, and you'll be able to use "classic" helpers).
//viewModel class
public class UserStatusViewModel {
public string Title {get;set;}
public bool IsLogged {get;set;
}
//action
public ActionResult Index() {
var model = new UserStatusViewModel{ Title = "Index", IsLogged = TheUser.CheckStatus()};
return View(model);
}
//view
#model UserStatusViewModel
#Html.DisplayFor(m => m.Title)
#Html.DisplayFor(m => m.IsLoggedIn)
I am using the following code for a master view model that contains two lists of data,
namespace trsDatabase.Models
{
public class masterViewModel
{
public IEnumerable <Customer> Customers { get; set; }
public IEnumerable <CustomerSite> CustomerSites { get; set; }
}
}
I am using the following code to pass the veiwmodel to the view,
public ViewResult Index()
{
masterViewModel sitesModel = new masterViewModel();
return View(sitesModel);
}
Then in my view I have the following,
#model IEnumerable<trsDatabase.Models.masterViewModel>
foreach (var site in customer.CustomerSites)
{
foreach (var cust in customer.Customers)
{
<tr>
<td>
#cust.CustomerName
</td>
<td>
#site.UnitNo
</td>
using the above code I am able to access all properties from the two lists in the viewmodel, however when I navigate to the view I get an error as the view is expecting an IEnumerable. If I change the declaration to just pass the viewmodel
#model trsDatabase.Models.masterViewModel
the foreach statement won't work, it gives this error
The model item passed into the dictionary is of type 'trsDatabase.Models.masterViewModel', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[trsDatabase.Models.masterViewModel]'.
Can anyone offer any advice or point me in the right direction for resolving this, is it possible to make my viewmodel IEnumerable?
Change this
#model IEnumerable<trsDatabase.Models.masterViewModel>
to this
#model trsDatabase.Models.masterViewModel
You are passing in a single instance of masterViewModel, so your view should expect a single instance, which is exactly what the error is telling you if not in a cryptic way.
Yes You can...
in you Model (masterViewModel) make the Customers and CustomerSites List like this:
namespace trsDatabase.Models
{
public class masterViewModel
{
public List<Customer> Customers { get; set; }
public List<CustomerSite> CustomerSites { get; set; }
}
}
in the same Model, define a method that would return IEnumerable like this:
public IEnumerable<Customer> Getall()
{
List<Customer> lcustomer= new List<Customer>();
//Get Customer data from Database or wherever
lcustomer.Add(new Customer{ firstname= "Quentin ", lastname= "tarantino" });
return lcustomer;
}
In your controller, instantiate your Model. then call Getall() method which would return IEnumerable basically a list of customers, and pass it to your View
var rep = new masterViewModel();
var model = rep.Getall();
return View(model);
In ASP.net MVC:
How should/Can I pass Form data (From the View) to the Controller?
This is the way I am heading :
The Controller Index function is passing a ViewModel object to the View.
The ViewModel object contains a paginated list as well as some SelectLists.
_ The ViewModel object also contains a custom class I named theFilter. This class' purpose is to hold the Filter information Posted from the View via a Form.
I want the Index [AcceptVerbs(HttpVerbs.Post)] function to receive theFilter object populated with the form data, as well as the page number (as is it right now)
Here are snippets of my code:
The Controller/Index postback function:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(int? page, CaseFilter caseFilter)
{
const int pageSize = 10;
var cases = caseRepo.FindAllCases();
var paginatedCases = new PaginatedList<Case>(cases, page ?? 0, pageSize);
return View(new CaseIndexViewModel(paginatedCases, caseFilter));
}
The Filter Class:
public class CaseFilter
{
int iVolume_id = 0,
iSubject_id = 0;
public CaseFilter() {
}
public int Volume_id { get { return iVolume_id; } set { iVolume_id = value; } }
public int Subject_id { get { return iSubject_id; } set { iSubject_id = value; } }
}
And the ViewModel class:
public class CaseIndexViewModel
{
public PaginatedList<Case> PaginatedCases { get; private set; }
public CaseFilter CaseFilter { get; private set; }
public CaseIndexViewModel(PaginatedList<Case> paginatedCases, CaseFilter caseFilter)
{
PaginatedCases = paginatedCases;
CaseFilter = caseFilter;
}
}
Basically I am trying to avoid using Request.Form to populate the Filter class, at least not to use it within the Controller.
Any help, suggestions or disses are welcome!
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(FormCollection collection)
{
string valueFromNameTextBox = collection["name"];
}
You can index into this collection with the names of all the inputs on the form.
Rather than complicate my method signatures, I've taken to using the ValueProvider property and Try/UpdateModel in the Controller to retrieve form/route values unless the values are simple properties. On the other hand, I would probably also not make the filter part of the model for the View -- I tend to have a narrower conception of the model for the page, wanting it rather to be the business model rather that a model of all the data on the page -- and would simply pass the filter values via ViewData.
To expand BFree's answer, you can go through all the elements in the form by doing something like this:
foreach (string key in collection.keys) {
if (key.contains("blah"))
text1 = collection[key];
}
If it has too many elements for the key.contains if, it can get a bit ugly though, so beware ;).
Finally, I do not need to even use the Request Collection. The CaseFilter object is filled automatically as I set it as a parameter in
public ActionResult Index(int? page, CaseFilter caseFilter)
The code above works as it is.