PartialView used by 2 views accessing ViewModel data from each - c#

I have 2 viewmodels each of them have a property called RSSFeed of type RSS. I have 2 views (one for each viewmodel) and I want them to share the same partialview which renders the data in RSSFeed.
In my main views, I have:
#{ Html.RenderPartial("Social", new ViewDataDictionary(Model)); }
In my Social partiavlview I wanted to use something like:
#ViewBag.RSSFeeds.Feed[0].Title
Running that give me 'Cannot perform runtime binding on a null reference'
Each ViewModel has:
public RSS RSSFeed = new RSS();
The supporting classes are:
public class RSSItem
{
public string Title { get; set; }
public string Link { get; set; }
public string Description { get; set; }
public DateTime PubDate { get; set; }
}
public class RSS
{
public string FeedURL { get; set; }
public List<RSSItem> Feed = new List<RSSItem>();
}
Any help is greatly appreciated. I wonder if I am actually able to do what I am looking to do or have completely done this the wrong way.

In your example above, .Feed is an empty List of RSSItems. Your code:
#ViewBag.RSSFeeds.Feed[0].Title
If attempting to get the title from the first Feed in your (empty) list which is why you are getting the null reference error.
You'd need to do a check in your partial to make sure that a Feed exists before using it.
#if (ViewBag.RSSFeeds != null && ViewBag.RSSFeeds.Feed.Count() > 0)
If you use the ViewBag, you'll need to populate that on every page load that uses it - if it's only 2 pages that might not be a problem. Maybe it would be better to use the Session to store your RSSFeeds instead?
Set
Session["RSSFeeds"] = RSSFeeds;
Get
if (Session["RSSFeeds"] != null)
return (RSSFeeds)Session["RSSFeeds"];
else
return null;

I can't comment yet, but are the inputs you are using for RenderPartial correct? on MSDN there is no method that takes a ViewDataDictionary and one other object.

Related

How can I add different controls dynamically to a web page using asp.net core?

I am new to .net core - have been using aspx web pages and .net framework 4.x for a number of years. I have a project where we want to display different controls (textbox, dropdown, checkbox) on the page based on values returned from a query. For example, user chooses "A" from a dropdown list and it shows 10 controls, if they choose object B it shows 8 controls, etc. Previously in .net framework, I would use a content placeholder with an ID and then find that ID and start adding controls (controls.Add(newControl)) in the placeholder. It doesn't seem that is an option with .net core. It seems like this would be a common need for various web applications, but I'm not finding many hits.
Another question is whether this can be done in the code behind or if it has to be done on the client-side. If one of the controls in the list is a dropdown, there will be a query that a subroutine will run to get the Key/Value pairs for the dropdown. To me this means it would be more effective on the server side.
I haven't really found any good examples when I do some searching. Can anyone point me to a good resource or provide me with a basic example - either client-side or server-side? Thanks!
There are many options, but I'll describe a simple one, using server side processing. As you explained in your comment, there will be 2 pages:
One that will display the select element that will be used to choose a set of controls.
The page that will be returned according to the previous choise, displaying the selected set of controls.
I assume that you know how to build the first page.
For the second page, you can leverage the ASP.NET Core MVC pattern to achieve the desired result.
You will need the three usual MVC elements:
An Action in a Controler.
A ViewModel for your Razor View.
A Razor View.
The Action does the following:
Receives the id of the selected set of control (via the Action's parameter).
Uses this id to retrieve the information about the corresponding set of controls from your repository.
Builds a ViewModel out of the received information.
Builds a View using the obtained ViewModel.
Return the builded View.
Here is some simplified example code:
In your controller, add the following method:
#!lang-cs
Public IActionResult GetProgramControlSet(int ProgramId)
{
// Here, use the id to get the data from your repository
// that will be used to build set of controls.
// Supposing you have defined a GetControls method,
// it could look like:
var SelectedControls = MyRepository.GetControls(ProgramId);
// If needed, you can build a ViewModel out of the received SelectedControls.
var SelectedControlsViewModel = new ControlSetViewModel(SelectedControls);
return View(SelectedControlsViewModel)
}
Of course, many things are missing here: error handling, etc...
Here is what the ViewModel could be:
#!lang-cs
public class ControlSetViewModel
{
public string Name { get; private set; }
public List<IControl> Controls { get; private set; }
public ControlSetViewModel(...)
{
// Whatever needs to be done to construct the ViewModel
}
}
public enum ControlKind
{
Button,
Select,
Textarea
//...
}
public interface IControl
{
ControlKind Kind { get; }
}
public class ControlButton : IControl
{
public ControlKind Kind => ControlKind.Button;
public string Label { get; set; }
public string Text { get; set; }
public string Color { get; set; }
// ... All other needed properties for the button
}
public class ControlTextarea : IControl
{
public ControlKind Kind => ControlKind.Textarea;
public string Label { get; set; }
public string PlaceholderText { get; set; }
public string RowCount { get; set; }
// ... All other needed properties for the textarea
}
public class ControlSelect : IControl
{
public ControlKind Kind => ControlKind.Select;
public string Label { get; set; }
public string PlaceholderText { get; set; }
public List<SelectOption> Options { get; set; }
// ... All other needed properties for the select
}
public class SelectOption
{
public string Text { get; set; }
public string Value { get; set; }
}
You could also use inheritance instead of interface for the control classes.
Now the view.
It is a Razor page containing something akin to
#model ControlSetViewModel
#*... some HTML ...*#
<div>
<h1>#Model.Name</h1>
#foreach(var control in Model.Controls)
{
<div>
switch(control.GetControlKind())
{
case ControlKind.TextArea:
var Textarea = (ControlTextarea)control;
<label>#Textarea.Label</label>
<textarea rows="#Textarea.RowCount"/>
break;
case ControlKind.Select:
var Select = (ControlSelect)control;
<label>#Select.Label</label>
<select>
#foreach(var option in Select.Options)
{
<option value="#option.Value">#option.Text</option>
}
</select>
break;
#*... etc ...*#
default:
#*... etc ...*#
}
</div>
}
</div>
#*... More HTML ...*#
Of course this is far to be finished. All the infrastructure and code that will actually react to the displayed controls is missing.
Is it a form you that will be posted?
Is it Javascript code that will react to the control manipulation?
Or another mecanism?
This questions will need to be addressed.

ASP.NET Web API model binding non-sequential list of complex objects

I am attempting to model bind a complex object with a non-sequential list using an ApiController. All of the fields except the list are set correctly, but the list contains one element (even though two list elements were posted) and the element is null. If I take the exact same code and point it to an MVC Controller using the same parameter type in my action method, everything works as expected.
Since I am using a non-sequential list, I am using the hidden ".Index" input as described by Phil Haack (http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx)
The ApiController also binds the list correctly if I remove the ".Index" input and send the list as a sequential list starting at 0. (This option work for testing, but is not a great option in production as the list items can be added and removed by the user, which is why I want to use the non-sequential list.)
I understand that Web API Controllers do parameter binding differently than MVC Controllers as discussed here, but it seems like non-sequential lists should bind correctly in Web API Controllers. Am I missing something? Why does the same code work for an MVC Controller and not a Web API Controller? How can I get non-sequential lists to bind correctly in Web API?
Here are my Post parameters:
Parameters application/x-www-form-urlencoded
BatchProductLots.Index 1
BatchProductLots.Index 2
BatchProductLots[1].BrandId 1
BatchProductLots[1].ContainerId 9
BatchProductLots[1].ContainerLot 123
BatchProductLots[1].PackageId 2
BatchProductLots[1].PlannedQuantity 0
BatchProductLots[1].ProducedQuantity 20
BatchProductLots[2].BrandId 1
BatchProductLots[2].ContainerId 9
BatchProductLots[2].ContainerLot 123
BatchProductLots[2].PackageId 1
BatchProductLots[2].PlannedQuantity 0
BatchProductLots[2].ProducedQuantity 1
BatchStatusId 1
LotNumber 070313
ProductionDate 07/03/2013
RecipeId 1
RecipeQuantity 1
SauceId 22
X-Requested-With XMLHttpRequest
Here is my Web API Controller Action:
(request.BatchProductLots list is set to one element (even though two elements were posted) and that one element is null)
public Response Create(BatchCreateRequest request)
{
Response response = new Response();
try
{
Batch batch = Mapper.Map<Batch>(request);
batchService.Save(batch);
response.Success = true;
}
catch (Exception ex)
{
response.Message = ex.Message;
response.Success = false;
}
return response;
}
Here is the complex object with the list that I am attempting to bind to:
public class BatchCreateRequest
{
public int BatchStatusId { get; set; }
public DateTime ProductionDate { get; set; }
public string LotNumber { get; set; }
public int SauceId { get; set; }
public int RecipeId { get; set; }
public int RecipeQuantity { get; set; }
public List<BatchProductLot> BatchProductLots { get; set; }
public class BatchProductLot
{
public int BrandId { get; set; }
public int ContainerId { get; set; }
public string ContainerLot { get; set; }
public int PackageId { get; set; }
public int PlannedQuantity { get; set; }
public int ProducedQuantity { get; set; }
}
}
Short answer, it's not possible using Web Api's Model Binder. MVC and Web Api use different model binders and the Web Api model binder only works on simple types.
See this answer for links that explain further as well as possible solutions.
Longer answer, create a custom implementation of System.Web.Http.ModelBinding.IModelBinder and change your Action's signature to the following
public Response Create([ModelBinder(CustomModelBinder)]BatchCreateRequest request)
Do you really need Index to be set? In that case, one possible solution could be to make Index part of the BatchProductLot class. The sequence of list won't matter then and Web Api should be able to bind it.
Another idea would be to use application/json content type and send JSON. You can use Json.Net to deserialize and model binding would work.
Read Using an alternate JSON Serializer in ASP.NET Web API and even use this Nuget Package WebApi Json.NET MediaTypeFormatter if you don't want to do the hand wiring.

using ViewBag to pass a model object

I'm trying to sort my result page (which is in another view than the filtration page). I have faced this weird issue I do not understand why keeps happening to me.
All the codes provided in very short form, please ask me if you need any other parts of my code for more information.
My Index view(where user filters results):
#model IEnumerable<Cars.Models.CarSearch>
#using (Html.BeginForm("SearchResult", "Home", FormMethod.Post,
new
{
id = "CategoryFormID",
data_modelListAction = #Url.Action("ModelList"),
data_makeListAction = #Url.Action("MakeList"),
data_editionListAction = #Url.Action("EditionList")
}))
{
<label>Make</label>
<select id="MakeID" name="carMake">
<option>All Makes</option>
</select>
}
My SearchResult view:
#model IEnumerable<Cars.Models.Car>
Make
My model:
public class Car
{
public String Make { get; set; } //is my table model
}
public class CarFilter {
public String carMake { get; set; }
}
public class CarSearch {
public CarFilter CarFilter { get; set; }
public byte PageSize { get; set; }
public short PageNumber { get; set; }
public int TotalRows { get; set; }
}
My Controller:
public ActionResult SearchResult(String sortOrder, CarFilter filters)
{
ViewBag.CurrentFilters = filters;
return View();
}
All I'm trying to do is to get carMake from Index post it to controller in CarFilter form (since in my code there are LOTS of fields in the form and I don't want to write them all down) and when user clicks on sort by Make it GET the SearchResult method and it's supposed to set filters = ViewBag.CurrentFilters which is the value user inputted from beginning.
Now the funny part is, when I replace CarFilter filters with String carMake and other places respectively. It works like a charm.
My question:
Why?
How can I do this with CarFilter filters?
UPDATE:
Problem is that filters = ViewBag.CurrentFilters in my SearchResult view does not work with the type CarFilter, because it keeps giving me NULL value when user clicked on the sort by Make.
Second UPDATE:
I tried changing filters = ViewBag.CurrentFilters with CarFilter = ViewBag.CurrentFilters. Now CarFilter filters in my SearchResult(...)method in my controller is not and null object, but ALL the values of the objects in the model class is null (which shouldn't be). I mean the filters object exists but it seems like the values of CarFilter class in my model haven't been passed by ViewBag.CurrentFilters to the view.
when you canged the name it worked because framework found property name and the bind it to what you have within action parameters doesnt work so nicely with objects. My advice is to stick with simple types
Here is similiar case:
How to send model object in Html.RenderAction (MVC3)
Its not a ViewBag problem thants how it works in general. Its the prime reason for using flatted models :/

ASP.NET MVC Forms for Model

I'm trying to learn MVC by building a full-featured website. I'm a little stuck when it comes to dealing with forms, and posting data, and models....
BTW: I'm using EF Code-First w/MS SQL CE
Here's the Models in question:
public class Assignment
{
public int AssignmentID { get; set; }
public int? CourseID { get; set; }
public string Name { get; set; }
// etc...
public virtual Course Course { get; set; }
}
public class Course
{
public int CourseID { get; set; }
// etc...
}
I'm loading a partial view that allows the user to add a new assignment
Controller:
public ActionResult Assignments()
{
var assignments = myContext.Assignments.OrderBy(x => x.DueDate);
return View(assignments);
}
[HttpPost]
public ActionResult AddAssignment(Assignment assignment)
{
myContext.Assignments.Add(assignment);
myContext.SaveChanges();
return RedirectToAction("Assignments");
}
// Returns a strongly-typed, partial view (type is Assignment)
public ActionResult AddAssignmentForm()
{
return PartialView();
}
Here's where I'm stuck: I want this form to have a drop down list for the different courses that an assignment could possibly belong to. For example, an assignment called "Chapter 3 Review, Questions 1-77" could belong to course "Pre-Algebra". However, if I use the code below, I have to explicitly declare the SelectListItems. I thought that with the given Assignment model above, I should be able to have the drop down list for Courses automatically generated using MVC awesomeness. What am I doing wrong?
AddAssignment Partial View:
#model MyModels.Assignment
#using(Html.BeginForm("AddAssignment", "Assignments"))
{
// Can't I create a drop down list without explicitly
// setting all of the SelectListItems?
#Html.DropDownListFor(x => x.Course, ....
}
Basically you are confusing/mixing your business model and your UI model.
The quick fix here is to add the data for the dropdown list to the ViewBag (a dynamic object).
Alternatively you could create a class AssignmentModel that contains the relevant Assignment properties and the List.
And No, this is not well supported in the templates.
You do realize you'll need some error handling in the Post method(s)?

How can I serialize this .NET Collection item?

I'm trying to xml serialize a POCO view data class into xml. It serializes, but incorrectly generates some xml.
eg. (current result .. not the one I'm after)
<ReviewListViewData>
<reviews>
<review>....</review>
...
</reviews>
</ReviewListViewData>
I'm trying to get (notice how I've removed the bad root node?) ...
<reviews>
<review>....</review>
...
</reviews>
Class is defined as...
public class ReviewListViewData
{
[XmlArray("reviews")]
[XmlArrayItem("review")]
public ReviewViewData[] Reviews { get; set; }
}
and here's a sample way it's called in an ASP.NET MVC ActionMethod :-
var reviewListViewData = GetReviewListViewData(...);
return XmlResult(reviewListViewData); // (XmlResult referenced from MVCContrib).
anyone have any ideas, please?
Try this:
[XmlRoot("reviews")]
public class ReviewListViewData
{
[XmlElement("review")]
public ReviewViewData[] Reviews { get; set; }
}
Did you try decorating the class ReviewListViewData with [XmlRoot("reviews")] instead of the XmlArray?

Categories

Resources