I'm attempting to use Nustache so that I can share rendering templates between back end and front end code in an ASP.Net web app. It's working fine when I just pass an object to a template, but I can't figure out how to get it to render a collection. So if I have a template like this:
{{#RenderItems}}
<th data-warning="{{WarningLevel}}" data-limit="{{LimitLevel}}">{{TotalHours}}</th>
{{/RenderItems}}
Then I want to pass in a collection of objects and get out a set of th elements.
Things I've tried:
Creating a class with the properties WarningLevel, LimitLevel and TotalHours, adding objects to a List and passing that directly: Nustache.Core.Render.FileToString(System.Web.HttpContext.Current.Server.MapPath("my.template"), ListOfObjects)
Doing the same thing, except creating an anonymous class: Nustache.Core.Render.FileToString(System.Web.HttpContext.Current.Server.MapPath("my.template"), new { RenderItems = ListOfObjects})
Instead of a list, using a Dictionary containing the same objects
Using a dictionary of dictionaries - so each item is itself a dictionary with the properties named above
The only way it renders anything at all is if I use an anonymous class, but I can't get it to populate the template with the named items whatever I do. I'm sure I'm missing something fairly obvious here, as I assume this should be straightforward, but the documentation says 'look in the code and the tests' but I can't work out which test is actually dealing with this scenario. Can someone either point me in the right direction, or provide some example code?
I was able to get this working with the following class and controller action:
public class Person {
public string Name { get; set; }
public string Email { get; set; }
}
public ActionResult Index() {
var people = new List<Person>() {
new Person { Name = "Albert Adams", Email = "albert#email.com" },
new Person { Name = "Bob Becker", Email = "bob#email.com" },
new Person { Name = "Charles Charles", Email = "charles#email.com" }
};
ViewData["People"] = people;
ViewResult viewResult = View();
viewResult.ViewEngineCollection = new ViewEngineCollection { new NustacheViewEngine() };
return viewResult;
}
And then Index.nustache:
{{#People}}
<p>{{Name}} - {{Email}}</p>
{{/People}}
Note that I pulled the latest changes from the Nustache repo this morning.
Related
I'm using Help Pages for ASP.NET Web API to create documentation for our web api. Everything is working fine using the XML documentation comments. However, for one method I can't figure out how to supply documentation for a dynamic query string.
The method uses the GetQueryNameValuePairs() of the request to select the key-value pairs of the query string to a model. For example ?1=foo&2=bar will result in a list of two objects with Id set to 1 and 2 and Value to 'foo' and 'bar', respectively.
I've tried adding the <param> tag to the XML comment, but this is ignored since the method does not contain a matching parameter.
Any help would be appreciated.
You could try extending the help page generation process. When you create your ASP.NET Web API project, the help page-related code is downloaded as source, not as a .dll, so you can extend it with any custom logic you'd like.
Here's what I would do:
Create an attribute class and decorate my special method with that (e.g. [DynamicQueryParameter("Param1",typeof(string))])
Modify the HelPageConfigurationExtensions.cs to query these attributes from the actions as well and add them manually to the UriParameters collection of the model. I would probably do this in the GenerateUriParameters() method.
[Edit] I actually had some time, so I put together the solution myself, because, you know, it's fun :)
So create an an attribute:
public class DynamicUriParameterAttribute : Attribute
{
public string Name { get; set; }
public Type Type { get; set; }
public string Description { get; set; }
}
You can decorate your action methods with this:
[DynamicUriParameter(Description = "Some description", Name ="Some name", Type =typeof(string))]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
Then I modified the HelpPageConfigurationExtensions.GenerateApiModel() like this:
private static HelpPageApiModel GenerateApiModel(ApiDescription apiDescription, HttpConfiguration config)
{
HelpPageApiModel apiModel = new HelpPageApiModel()
{
ApiDescription = apiDescription,
};
ModelDescriptionGenerator modelGenerator = config.GetModelDescriptionGenerator();
HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator();
GenerateUriParameters(apiModel, modelGenerator);
// add this part
var attrs = apiDescription.ActionDescriptor.GetCustomAttributes<DynamicUriParameterAttribute>();
foreach (var attr in attrs)
{
apiModel.UriParameters.Add(
new ParameterDescription
{
Name = attr.Name,
Documentation = attr.Description,
TypeDescription = modelGenerator.GetOrCreateModelDescription(attr.Type)
}
);
}
// until here
GenerateRequestModelDescription(apiModel, modelGenerator, sampleGenerator);
GenerateResourceDescription(apiModel, modelGenerator);
GenerateSamples(apiModel, sampleGenerator);
return apiModel;
}
I am fairly new to MVC, but have quite a bit of experience in development in general, and am having an issue with MVC request life cycle it seems.
Will try to keep this simple, even tho the project is a bit complex in some areas.
I have a view bound to a view model that has a few complex list properties. These properties are displayed via checkboxes who's IDs are not directly related to any property in the model, but instead related to the IDs of the objects in the List<>. Because of this, the checked values do not automatically get applied to the model on POST.
To get around that, I added code in the Action method in the controller that parses the proper controls (in the Request.Form collection) and assigns the checked/selected value to the proper list items in the model.
This works perfectly up to a point.
Now, I also use Fluent Validation, and the problem is when performing custom validation rules when posting a new model to the server. The Validation routine is firing BEFORE the controller's action method, and thus before my processing of the list objects.
So, my question is, is there a way I can override the initial call to the model validation so I can just call the validation manually after my processing? I know I can do that which will fix the problem without overriding the initial call, but some of the validation takes a bit of time to process since it requires linq queries to a live database, so I do not want the validation to fire 2 times - that will quite literally double the time it takes to return no matter if the model is valid or not.
EDIT: Adding a example:
namespace Models
{
[Validator(typeof(MemberValidator))]
public class ViewMember
{
public int MemberID { get; set; }
public short RegionID { get; set; }
public List<PropTypeInfo> PropTypes { get; set; }
}
}
PropTypeInfo class:
public class PropTypeInfo
{
public byte ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool Selected { get; set; }
public PropTypeInfo(byte i, string n, string d, bool sel)
{
ID = i;
Name = n;
Description = d;
Selected = sel;
}
public static List<PropTypeInfo> GetAll(bool selected = false)
{
List<PropTypeInfo> output = new List<PropTypeInfo>();
OpenAccess.Context context = new OpenAccess.Context();
var list = (from f in context.Prop_Types orderby f.PropType select f).ToList();
foreach (OpenAccess.WebrentzServerPayments.Models.Prop_Type p in list)
output.Add(new PropTypeInfo(p.PropType, p.PropName, p.DisplayText, selected));
return output;
}
}
now here is the code in the view that renders the checkboxes for each item in the list:
<div class="Column Emp-PropTypes">
#foreach (WebrentzServerPayments.Models.PropTypeInfo ptype in Model.PropTypes)
{
<div style="float:right;width:20%;font-weight:bold;">
#Html.CheckBox("ptype_" + ptype.ID, ptype.Selected, new {Value=ptype.ID}) #Html.Raw(" ") #ptype.Name
</div>
}
</div>
And here is the code I use in the Controller Action method to pull that data back in to the List:
foreach (PropTypeInfo info in member.PropTypes)
info.Selected = form[string.Format("ptype_{0}", info.ID)].Contains(info.ID.ToString());
As a little background, a "PropType" is a type of property (house, condo, apartment) - there are about 2 dozen of them, and more can be added/removed at any time. The list in the class called "PropTypes" is first populated with the Name, Description and ID from a table in the database that lists all the available proptypes for that region.
We then will mark the proptypes as "selected" if the user has chosen that particular type. Those are saved to a table called Member.PropTypes (MemberID, ProptypeID).
So, at runtime the list will contain one record for each available proptype and the selected property will be set to yes if that user has selected it. That makes it easy to render the full list in the view...
Its actually quite a bit more complex as there are almost a dozen such lists, but each works the exact same way just with different data, as well as about 200 additional properties that are easier to manage. Only these lists are causing the issue.
Any help appreciated!
Dave
I have a model "Team" with one property "TeamMembers" which is a list of the class "TeamMember". "TeamMember" itself right now only has the Id and Name Properties for testing.
I created the CRUD Views automaticall and can enter the normal properties of "Team" (Id, TeamName, Location, etc.) just fine, but of course the View doesn't know what to do with List, so (just for testing purposes) I hardcoded this into the create method:
var TeamMembers = new List<TeamMember>();
TeamMembers.Add(new TeamMembers { Name = "Bob" });
Team.TeamMembers = TeamMembers;
This seems to work. When I create the new Team, and set a breakpoint right at the end of the Create method, I can see the entry in the inspector/hover. Also a table "TeamMember" (I save the model in a local DB) is created in the DB with and entry for "bob", including a Team_Id referencing the Team I just created.
Now when I try to output the Details for a Team, TeamMembers returns "null". No error, nothing, just "null". Shouldn't it return something? anything? An Object, a Reference,...?
The goal is to create a Team and have TeamMembers contain a whole bunch of TeamMember entries that I can later iterate through and output them in a list, etc.
Can anybody tell me where my mistake lies? Is there a concept I didn't grasp, or some stupid oversight?
Thank you for your time.
You should always instantiate lists in the constructor of your class. I do not know what kind of DB or ORM you use (Entity?) but more often than not the framework will instantiate your classes with the public empty constructor and then set the properties. That could be why.
That why everytime you work on an instance of Team you are sure the list is not null.
public class Team
{
public Team()
{
TeamMembers = new List<TeamMember>();
}
// ...
public List<TeamMember> TeamMembers { get; set; }
}
Then you simply have to do this instead :
// TeamMembers is not null anymore
var team = new Team();
team.TeamMembers.Add(new TeamMember { Name = "Bob" });
You have a minor typo, you are adding TeamMembers to TeamMembers.
It should be as follows (I lowercased the TeamMembers variable too):
var teamMembers = new List<TeamMember>();
teamMembers.Add(new TeamMember { Name = "Bob" });
Team.TeamMembers = teamMembers;
You also need to make sure that you are returning your model in your action i.e
[HttpGet]
public ActionResult Index()
{
var team = new FooModel();
var teamMembers = new List<TeamMember>();
teamMembers.Add(new TeamMember { Name = "Bob" });
team.TeamMembers = teamMembers;
return View(team);
}
Rewrite of the question.
Basically I have these two classes:
class Show {
public List<Episode> Episodes{ get; set; }
}
class Episode {
public Show Show{ get; set; }
}
When I add an episode to a show, I'll have to create a new episode object. However, when I do that, I'll also have to fill in the show and it turns into a circular problem. How can I add an episode to a show where the episode also holds a reference to the show it's part of, without ending up in an eternal circle?
I think I've left out some important info:
I have a factory which creates viewmodels for me. When I want a list of shows, call the method CreateShowViewModel on each show in the list. This method is as follows:
public ShowViewModel CreateShowViewModel(Show show) {
return new ShowViewModel {
ShowID = show.ShowID,
Title = show.Title,
Episodes = CreateEpisodeViewModels(show.Episodes)
};
}
When it has to instantiate the episodes, it calls a method which performs the following function on every episode:
public EpisodeViewModel CreateEpisodeViewModel(Episode episode) {
return new EpisodeViewModel {
ShowID = episode.ShowID,
EpisodeID = episode.EpisodeID,
Title = episode.Title,
Show = CreateShowViewModel(episode.Show)
};
}
which will create the show again, etc. This poses as a circular reference, no?
I can see two apparent solutions to this problem. The first one is simply to pass along an optional ViewModel, so you break the circle in the method-calls. Something like this:
public ShowViewModel CreateShowViewModel(Show show, List<EpisodeViewModel> episodes = null /* guessing type */) {
var viewModel = new ShowViewModel {
ShowID = show.ShowID,
Title = show.Title
};
viewModel.Episodes = episodes ?? CreateEpisodeViewModels(show.Episodes, viewModel);
return viewModel;
}
public EpisodeViewModel CreateEpisodeViewModel(Episode episode, ShowViewModel show = null) {
var viewModel = new EpisodeViewModel {
ShowID = episode.ShowID,
EpisodeID = episode.EpisodeID,
Title = episode.Title
};
viewModel.Show = show ?? CreateShowViewModel(episode.Show, viewModel); // this might cause a problem due to the fact that you only have 1 episode, and not all of them.
return viewModel;
}
The other way would be to add some cashing from Show->ShowViewModel and Episode->EpisodeViewModel. I recommend using a ConditionalWeakTable for this.
Something along the lines of this:
static ConditionalWeakTable<Show, ShowViewModel> _shows = new ConditionalWeakTable<Show, ShowViewModel>();
static ShowViewModel GetForShow(Show show)
{
return _shows.GetValue(show, s => CreateShowViewModel(s));
} // do the same for episode.
I'm still not yet sure on the best way to store selectlist options for front end display or db storage.
I've been using Enums at the moment, and also using description decorators (How do you create a dropdownlist from an enum in ASP.NET MVC?)
I'm now thinking that I might as well just create a full class for this stuff, so I can store the following information properly with full control:
Item Name
Full description
int for storage in db
order
Any methods to get information in anyway from the list.
Is it right I should be thinking about implementing all this myself by hand? I want a really solid way of doing this, and an enum doesn't really feel like it's going to cut it.
Is it right I should be thinking about implementing all this myself by
hand?
Yes. Enums are often leaky and insufficient abstractions that aren't always suitable for the complex domain model you actually wish to represent.
Rather than roll your own, you may want to consider Headspring's Enumeration class (via github, nuget). We use it all the time instead of enums because it's nearly as simple and is much more flexible.
An example of a "State" enumeration and using it as a select list:
public class State : Enumeration<State>
{
public static State Alabama = new State(1, "AL", "Alabama");
public static State Alaska = new State(2, "AK", "Alaska");
// .. many more
public static State Wyoming = new State(3, "WY", "Wyoming");
public State(int value, string displayName, string description) : base(value, displayName)
{
Description = description;
}
public string Description { get; private set; }
}
public IEnumerable<SelectListItem> Creating_a_select_list(State selected)
{
return State.GetAll().Select(
x => new SelectListItem
{
Selected = x == selected,
Text = x.Description,
Value = x.Value.ToString()
});
}
I'm not trying to sell you on this particular implementation, you could certainly hand code your own (the Enumeration class is only about 100 lines of code). But I definitely think you'd benefit from moving beyond basic enums. It is the right approach given the scenario you described in your question.
The first place where such information shoiuld be is the database...or any "virtual store" such as a web service that offers an interface to you db. In fact if there are other db entiies that use these values THEY MUST be represented in the database, otherwise you will run in big troubles. In fact, suppose one of such values is a string....if you don't define a table containing all possible values+a key and simply write the string as it is in other tables...it will be impossible for you to change the format of the string since it will be "spread" all over your db...On the contrary, if you just use an external key to refer to such strings...you can easily change them since the string is stored in just ONE place in your db.
Also the enumeration solution suffers of the problem that you cannot add or deleted values...so if such operations "conceptually" might make sense you cannot use an enumeration. You can use enumeration when all options "conceptually span" all possibilities, so you are sure you will never add/delete other options, such as in the case of the enumeration (yes, no, unknown).
That said, once you have your options in the db the remainder is easy...you will have DTO entities or Business entities representing them in exactly the same way you do for all other DB entities.
For visualization purposes you may have a ViewModel version of this options that might just contain key and description, and a "Repository method" that your controllers can call to have the list of all options.
Once retrieved you controllers put them in the overall page ViewViewModel...together with all other information to be shown on the page. From the ViewModel...you can access them to put them in a dropdown.
Summing up:
1) You need a DB representation of your options
2) Then you will have DTO, business layer, and View versions of this entities...as needed, exactly as for all other DB entities.
Are you looking for a one-size-fits-all solution for all your select list options? I personally advocate choosing the option that best fits the specific issue.
In a recent project I was introduced to a hybrid of a Smart Enum. Here's an example (I apologize for typos, I'm typing this cold):
public class Priority
{
public enum Types
{
High,
Medium,
Low
}
public Types Type { get; private set; }
public string Name { get { return this.Type.ToString(); } } // ToString() with no arguments is not deprecated
public string Description { get; private set; }
public static High = new Priority{ Type = Types.High, Description = "..."};
public static Medium = new Priority{ Type = Types.Medium, Description = "..."};
public static Low = new Priority{ Type = Types.Low, Description = "..."};
public static IEnumerable<Priority> All = new[]{High, Medium, Low};
public static Priority For(Types priorityType)
{
return All.Single(x => x.Type == priorityType);
}
}
So, in implementation, you could store the Enum value, but you would reference the object itself (Priority.For(entity.priority)) for the additional metadata when rendering your views.
Is that closer to what you're looking for?
Of course, one of the gotchas is if you need to write a query against the database that relies on the metadata on the lookup, this solution is going to create a few tears along the way.
You can use "repository pattern" for data access and use viewmodels between your controllers and views. Example:
//Model
public class CustomerViewModel
{
public Customer customer { get;set; }
public IEnumerable<Village> Villages { get; set; }
}
//Controller
public ActionResult Index()
{
var customerViewModel = new CustomerViewModel
{
Customer = new Customer(),
Villages = _villageService.GetAll()
};
return View(customerViewModel);
}
//View
#model ViewModel.RegisterViewModel
#Html.DropDownListFor(q => q.Customer.VillageId, new SelectList(Model.Villages, "Id", "Title"), "Please Select")
I have written a blog post about repository pattern, you may have a look.
I store my options in the View Models themselves:
public class ViewModel {
[Required]
public int SelectListValue { get; set; }
public IDictionary<String,String> SelectListOptions {
get {
return new Dictionary<String, String>{
{ "0", Resources.Option1},
{ "1", Resources.Option2},
{ "2", Resources.Option3}
};
}
}
}
Then I can just drop the following line into my view to render the select list:
<%= Html.DropDownListFor(m => m.SelectListValue, new SelectList(this.Model.SelectListOptions, "Key", "Value", "")) %>