When I am in the View of a specific object, I can acces all the instances by this loop in the View:
#foreach (Reservation r in Model) { }
But How to implement the same foreach cycle with Ski Class, which is not associated with this View?
Add whatever you need access to to your model. It is common to create specific ViewModel classes that are specific to one or a few views and provide access to whatever they need. In your case it would have Reservations as well as Ski data.
Example:
public class ReservationViewModel
{
public List<Reservation> Reservations { get; set; }
public List<Ski> Skis { get; set; }
}
Now in the View you can foreach over Model.Reservations, as well as over Model.Skis.
Related
I am calling several API's so I have a problem to group received data
For example:
Api 1 is returning a list of objects and each object have different buildingId.
That buildingId I need to use as an input for Api 2 which is returning list of objects (apartments) where each of the apartment have different apartmentId.
Then I have to call Api 3 and use apartmentId as an input and I will receive a list of owner/s of the apartment.
Final result should be like
BuildingA
Apartment1
ApartmentOwner1
ApartmentOwner2
Apartment2
ApartmentOwner3
Apartment3
ApartmentOwner4
ApartmentOwner5
ApartmentOwner6
BuildingB
Apartment1
ApartmentOwner1
ApartmentOwner2
Apartment2
ApartmentOwner3
ApartmentOwner4
I have created model for all 3 types of objects, this is example for Building.
public class BuildingResponse
{
public int BuildingId { get; set; }
public string City { get; set; }
public string Address { get; set; }
}
I understand that I have to use foreach over data returned by Api 1 (over buildings), but how to achieve the rest of the requirement?
Thanks
If you have control of the API, and you know you need your building objects to be fully populated, and your apartment objects to be fully populated, you could expose an endpoint that returns fully populated buildings. That is, buildings with their apartments populated and apartments with their owners populated.
If not, one approach would be to add something like a List<ApartmentResponse> to the BuildingResponse model, and something like a List<ApartmentOwnerResponse> to the ApartmentResponse model.
Then, you iterate through the BuildingResponse models you get back from your API and request the ApartmentResponse models for that building and store it in the List<ApartmentResponse> you add to the BuildingResponse model.
Then iterate through the ApartmentResponse models, request the ApartmentOwnerResponse models from the API, and add them to the List<ApartmentOwnerResponse> you add to the ApartmentResponse model.
For example...
public class BuildingResponse
{
// Other properties / methods
public List<ApartmentResponse> Apartments { get; private set; } = new List<ApartmentResponse>();
}
public class ApartmentResponse
{
// Other properties / methods
public List<ApartmentOwnerResponse> Owners { get; private set; } = new List<ApartmentOwnerResponse>();
}
public class ApartmentOwnerResponse
{
// Properties / methods
}
public List<BuildingResponse> GetBuildings()
{
List<BuildingResponse> buildings = GetBuildingsFromApi();
foreach (BuildingResponse building in buildings)
{
List<ApartmentResponse> apartments = GetBuildingApartmentsFromApi(building.BuildingId);
building.Apartments.AddRange(apartments);
foreach (ApartmentResponse apartment in apartments)
{
List<ApartmentOwnerResponse> owners = GetApartmentOwnersFromApi(apartment.ApartmentId);
apartment.Owners.AddRange(owners);
}
}
return buildings;
}
You should be aware that this is a N+1 problem, where you're executing one request against the API to get all the parents (BuildingResponse) then for each of those you're executing another request to get the children (ApartmentResponse). Then you're doing it again for ApartmentResponse and ApartmentOwnerResponse. This could be a performance problem, especially as your list of buildings and apartments grow.
Well im kinda new in Asp.net Mvc and im learning alone from scratch, i have a aplicattion that controls expends and earnings and what i am trying to do now is, basing on a list of earnings and expends give me the balance from a user, im having a lot of problems trying to control this and i dont know if i am doing it the right way
Here is my model:
public class Balance
{
public int BalanceId { get; set; }
public List<Expense> Despesas { get; set; }
public List<Earning> Rendimentos { get; set; }
public string ApplicationUserId { get; set; }
}
Soo what i did was, first trying to control when the user inserts a Earning or a row like, verifying if the User already exists on the database in the control method Create on the expenses and in the earning, if it doesnt exist he add the aplicationUserId and the expensive or the earning.
I want that the balance appears in every page, soo i added this to my Layout.cshtml
<li>#Html.Action("GetBalance", "Home")</li>
it calls the controller GetBalance:
public PartialViewResult GetBalance()
{
var userId = User.Identity.GetUserId();
var balance = db.Balance.Where(d => d.ApplicationUserId == userId);
return PartialView("_GetBalance",balance);
}
Send to the view _GetBalance the balance model:
#model <MSDiary.Models.Balance>
<p>Saldo: #GetBalance()</p>
#functions
{
HtmlString GetBalance()
{
decimal saldo = 0;
if (Model.Expense.Count != 0 || Model.Earning.Count != 0)
{
foreach (var item in Model.Despesas)
{
balance += item.EarningValue;
}
foreach (var item in Model.Rendimentos)
{
balance -= item.ExpenseValor;
}
}
return new HtmlString(balance.ToString());
}
}
What i want to know is, if there is a easyer way to do this, or what i can do to do what i want, i cant get it why my view expects something different can someone explain me what i am doing wrong?
Ps: Sorry for the long post and English, but i want to learn more :)
Firstly, the model #model <MSDiary.Models.Balance> needs to be changed to:
#model IEnumerable<MSDiary.Models.Balance>
Also, the method GetBalance should ideally be placed in a class not in GetBalance partial view. You could achieve this two ways, either through extension methods or have a Balance View Model that has the calculated balance as a property which is then passed down to your view.
As an example via an extension method:
public static class BalanceExtensions
{
public static string GetBalance(this Balance balance)
{
string displayBalance = "0:00";
// Your logic here
return displayBalance;
}
}
And then in your Partial View you can use the new HTML Helper:
#Html.GetBalance();
As an additional note I would change List to IEnumerable for expenses and earnings as it appears you are only exposing the data and not manipulating the data.
Your model would then look like:
public class Balance
{
public int BalanceId { get; set; }
public IEnumerable<Expense> Despesas { get; set; }
public IEnumerable<Earning> Rendimentos { get; set; }
public string ApplicationUserId { get; set; }
}
#Filipe Costa A few things here.
You should probably name your view the same thing as your method. The underscore preceding the name is fairly standard so I would suggest using that same name for the method. If the name of the method and view are the same you can simply pass in the model and not have to do the name + model signature of PartialView method. It's simpler.
Aside from that your code is fine but your .cshtml partial view should have this for the first line. That will accept the list you're passing.
#model IEnumerable<MSDiary.Models.Balance>
<h1>#Model.BalanceId</h1>
#*Do other stuff!*#
I want to write an HTML helper method. In my helper method I want to know what is the type of the main model (if any). To obtain that, I used htmlHelper.ViewData.ModelMetadata.ContainerType , but I can't find any way to do that when my helper is called in a template view or probably a partial view whose model is an item of a collection. In this case htmlHelper.ViewData.ModelMetadata.ContainerType returns null.
Sample Model:
public class MyItemCollection
{
public List<MyItemContainer> Collection { get; set; }
}
Sample EditorTemplate:
#model Test.MyItemContainer
#Html.MyHelper(m=>m.Item)
Sample View:
#model Test.MyItemCollection
#for(int i = 0; i < Model.Collection.Count; i++)
{
#Html.EditorFor(m=>m.Collection[i])
}
Sample Action:
public ActionResult Index()
{
var m = new MyItemCollection();
//Fetching items from Business Logic
m.Collection = FetchItems();
return View(m);
}
I think that you should create an empty constructor for MyItemCollection and then initialize the Collection property.
In the case where that element isn't being intialized by another procedure it will result in NULL which could, in turn, cause the possible Reflection-related issue you are experiencing (i.e: finding ViewData.ModelMetadata.ContainerType)
I say, initialize the property yourself. see below
public class MyItemCollection
{
// added a constructor, so we could initialize Collection.
public MyItemCollection()
{
this.Collecion = new List<MyItemContainer>();
}
public List<MyItemContainer> Collection { get; set; }
}
I want to use view model for displaying instead of domain model. I have got these view models classes:
public class ArticleDescriptionViewModel
{
public string Title { get; set; }
public DateTime DateCreated { get; set; }
}
public class HomePage
{
public List<ArticleDescriptionViewModel> Articles { get; set; }
}
In the domain model i have got:
public class ArticleDescription
{
public string Title { get; set; }
public DateTime DateCreated { get; set; }
}
And this service method:
public List<ArticleDescription> GetArticlesDescription()
{
var articleDescription= from a in _ctx.Articles
select new ArticleDescription
{ Title = a.Title, DateCreated = a.DateCreated };
return articleDescription.ToList();
}
in the controller i want to match my list inside of my view model class with the list returned by my domain model class.
public ActionResult Index()
{
HomePage HomePageInstance = new HomePage();
HomePageInstance.Articles = _repo.GetArticlesDescription();
return View(HomePageInstance);
}
I have got an error:
"Cannot implicitly convert type System.Collections.Generic.List (DBayonaCode.Domain.Services.Models.ArticleDescription)' to 'System.Collections.Generic.List(DBayonaCode.Models.ArticleDescriptionViewModel)'"
But these two classes are equivalent? I am doing something wrong. I appreciate your help?
ArticleDescription and ArticleDescriptionViewModel are two different types, so there's no implicit conversion between them. You need to map your Domain Model object to your View Model object, you can do that either manually or with a tool like AutoMapper.
You could write extension methods like these to do the mapping:
public static class Mappings
{
public static ArticleDescriptionViewModel ConvertToView(this ArticleDescription article)
{
// Mapping Code
// return new ArticleDescriptionViewModel { ... }
}
public static List<ArticleDescriptionViewModel> ConvertToViews(this List<ArticleDescription> articles)
{
List<ArticleDescriptionViewModel> articleViews = new List<ArticleDescriptionViewModel>();
foreach (ArticleDescription article in articles)
{
articleViews.Add(article.ConvertToView())
}
return articleViews;
}
}
While MVC default project template offer just one model folder, thus implicitly presenting the idea that models are one thing, in fact There are three types of data models potentially involved in an ASP.NET MVC application:
- Domain model objects will be passed from and to a middle tier services interfacing with databases.
- View Model objects are those that the Controller pass to the View.
- Input model objects are those that the default modelBinder or some custom modelBinder generates from the view, although in many cases the input models are the same view model objects.
Hope it helps.
I have a View that has the ability to both create and edit an item that I have separated into partial views:
MainEditView.cshtml
_CreateChildDialog.cshtml
_EditChildDialog.cshtml
I have separate ViewModels for both the Create and Child items:
public class CreateChildViewModel
{
public string ItemText { get; set; }
}
public class EditChildViewModel
{
public string ItemText { get; set; }
}
Since the partial views for the Edit and Create dialog boxes will both be rendered on the same page, I will have a conflict for form id's and names...since they are both called ItemText.
Is it possible to customize the binding of these elements without writing a custom model binder?
I would like to do something like:
public class EditChildViewModel
{
[BindFrom("EditItemText")]
public string ItemText { get; set; }
}
Or does it just make more sense to rename the ViewModel properties to:
public class EditChildViewModel
{
public string EditItemText { get; set; }
}
public class CreateChildViewModel
{
public string CreateItemText { get; set; }
}
EDIT
Based on converstation with Darin I want to make this a little more clear.
My Parent has an Edit action.
When you edit the Parent, you would never create a new child or edit a child when you are calling the ParentController.Edit action.
I have a separate controller for the Child object that has a Create and Edit method:
public class ChildController
{
public ActionResult Edit() {}
public ActionResult Create() {}
}
I am using jQuery calls to asynchronously post to this controller when you edit or create a child. Basically I use a jquery dialog to create/edit a child that will get saved immediately when I click Ok on the dialog. This would happen even before clicking save for the Edit action of the parent.
I would use editor templates. Normally you would pack those two view models into a main view model which will be used by the main view:
public class MyViewModel
{
public CreateChildViewModel Create { get; set; }
public EditChildViewModel Edit { get; set; }
}
and then:
#model MyViewModel
#Html.EditorFor(x => x.Create)
#Html.EditorFor(x => x.Edit)
and I would replace the two partials by their corresponding editor templates (~/Views/Shared/EditorTemplates/CreateChildViewModel.cshtml and ~/Views/Shared/EditorTemplates/EditChildViewModel.cshtml). The editor templates will take of generating proper names and ids of the corresponding input elements.
Personally I tend to prefer editor/display templates instead of partials as they handle better naming of input elements.