MVC: Dynamically create new element from template - c#

I have an MVC app. Some elements are rather complex, so I created a template for them.
I use them calling #Html.EditorFor(). The problem is that I need a possibility to create such elements dynamically, I mean after clicking on a button 'Create New' I want to generate an empty template and let user to fill it. I can use mustache template engine or smth like this, but in this case I need to duplicate my html - in razor template and in html. I don't want to repeat myself, what is the best way to achieve this?
Another problem is that when I generate view for IEnumerable<> - razor created proper names for elements with proper indexes. In a case I want to create new element - how should I set these indexes to let binder properly work on POST? Is there is a better solution than using jQuery for this?
Thanx in advance.
UPDATE:
Here is an editor template:
#model FakeViewModel
<li>
<div>
<h3><span>#Model.Title</span><span class="icon-remove"></span><span class="icon-move"></span></h3>
<div>
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(mp => mp.Category)
<div>
<span class="font-small">Title</span>
</div>
<div>#Html.TextBoxFor(m => m.Title, new { #maxlength = FakeViewModel.MaxTitleLength, #class = "title-textbox" })</div>
<div>
#Html.TextAreaFor(m => m.Description, new { #placeholder = "short description", #data_max_length = "90" })
</div>
</div>
</div>
</li>
This is how I render it, "Special" is a type of IEnumerable<FakeViewModel>
<ul class="container" id="special">
#Html.EditorFor(m => m.Special)
</ul>
So, after rendering this I have got a layout with correct names, I mean Special[0].Id, Speacial[0].Category, etc.
Now, I want to create an empty template. For now I am using mustache template, it should have the same layout as editor template:
<script type="text/template">
<input name="Special[{{itemIndex}}].Category"
</script>
The problem is, that I use the same layout in two different places - in mustache template as well as in razor editor template.
If I need to change this layout - I need to change it in two places. I want to avoid this.
Another problem is that I need hardcode names ("Speacial" in this example) and manually put index, category and etc.

Whatever solution you use, JavaScript will be required. jQuery is just a JavaScript framework, so you could switch it out with something else, if you preferred, but you should stick with some sort or JavaScript framework, as the code for doing XMLHttpRequest cross-browser is tedious and prone to error.
As for your HTML, you can choose to either include it as a template on page load or request it fresh via an AJAX request. Personally, I'd go with a template because AJAX doesn't really buy you anything in this scenario, and it's just an extra request.
Either way, you're unfortunately not going to be able to use the Razor HtmlHelper methods, at least without mangling the HTML afterwards via JavaScript anyways; they simply won't generate the proper naming convention required for binding list objects outside of a for loop. The model-binding convention is easy to replicate manually, though, it's just:
YourCollection[N].FieldName
So if you had a list of Cars on your model and you wanted a field to edit the Color of the 3rd car, you would use:
Cars[2].Color
Any good JavaScript templating solution should be able to automatically insert the N value based on the object's position in the list of other objects.

You can use a string builder to dynamically create the html in the get method of your controller and then post that to the view. The other way is to create your own htmlhelper.

Partialview is the solution:
you can create a partial view and have it added every time you want it to be used or injected into the view. you can run a for or foreach look on that partial view and have it repeat as many times as you want.
1: create a partialview.
2: put everything you want to be rendered 2 times in the partial view.
3: replace the view code with partial view. like:
#html.Partial("~/views/partials/_samplename.cshtml")
you can repeat this partial view anywhere in the project and by changing anything in the partialview it would change everywhere.

Related

How can I get the number of existing HTML elements from code before rendering them

I'm currently trying to figure out how I can filter some HTML elements inside my code.
Basically I have some div containers which each contain some stuff I want to display.
But I only want to display a set amount (e.g. 1 or 3) of those containers which will be chosen at random.
I basically have the logic for choosing them at random already in the code.
The problem I have is that I need to somehow get the amount of containers I actually have to choose from them.
I don't want to depend on the ability of other people who work on the same code to actually update the number of containers manually when they add or delete one.
The containers are in a file looking something like this:
<div id="mainContainer">
<div id="element1">
//first container
</dev>
<div id="element2">
//second container
</dev>
<div id="element3">
//third container
</dev>
</div>
I want to get the number of those element from the code so I can filter them before the rendering of the website.
I could do the filtering by using TypeScript or JavaScript but for that I would need to load all the containers first which I want to prevent because of performance/user experience.
Is there any possibility to get like a list of all the containers or something like that?
I would really appreciate some help here :)
To the best of my knowledge you cant 'read' the DOM from within a Razor view so what you need is a different way of approaching the problem. How about something like this, which follows your current Razor layout quite closely;
#{
List<string> elementsToRender = new List<string>(5) { "element1", "element3" };
}
<div id="mainContainer">
#if (elementsToRender.Contains("element1", StringComparer.OrdinalIgnoreCase))
{
<div id="element1">
#* first container *#
</div>
}
#if (elementsToRender.Contains("element2", StringComparer.OrdinalIgnoreCase))
{
<div id="element2">
#* second container *#
</div>
}
#if (elementsToRender.Contains("element3", StringComparer.OrdinalIgnoreCase))
{
<div id="element3">
#* third container *#
</div>
}
</div>
You can still let other developers add and maintain the elements, all they need to do is make sure each one has a unique key in the elementsToRender.Contains test. For simplicity I've used the element wrapper div Id but it doesn't have to be. Then you can control which elements are shown by choosing which keys you add to elementsToRender .
You could move the population of elementsToRender into a ViewModel then populate it dynamically based on whatever logic you want.
Taking a different approach to the problem, how about putting the markup for each element into a separate partial view in a known folder. You could then scan the files in the known folder and pick which ones/how many to render into the actual page using #Html.RenderPartial.

ASP.NET MVC 5 - "Create" view that can handle any number of elements for an IEnumerable based on clicking an "Add row" button?

I am writing a recipe manager for my wife in C#/.NET MVC 5. I'm getting to the page for creating a recipe, and I'm a little stumped. A recipe consists of a Name and a list of ingredients.
When I create a view, I have my form:
#using(Html.BeginForm()){
//Form elements
#Html.DisplayNameFor(x => Model.Name)
//button for adding a new ingredient to the recipe
<input type="submit" text="Submit New Recipe!" />
}
When the button for adding an ingredient is clicked, it should render a block of html inside the form just above the button itself, that way the user can add any number of ingredients and then submit the recipe back when the form is posted back to the controller.
For this functionality, should I make the button call a controller that sends back a partial view or something? I'm not sure how to accomplish this outside of JavaScript, but I'm wanting to use a .NET MVC solution if I can.
I try to minimize my reliance on javascript whenever I can, however I agree with #br4d that knockout is your best option here. If you want to avoid it at all cost, it will be more complex, slower and not as user friendly but here is how I would do it.
Enclose the form in a div. Have a place holder div inside the form to hold your ingredients list. Make the "Add new ingredient" call into a controller that will return a partial view with the required fields. In the target attribute indicate the place holder div as the update target and append the response to the html of the place holder div by specifying InsertionMode.InsertAfter.
<div id="parentDiv">
#Html.BeginForm........
#Ajax.ActionLink("Add New Ingredient","ActionName","ControllerName",routeValues,
new AjaxOptions
{
UpdateTargetId = "ChildDiv",
InsertionMode = InsertionMode.InsertAfter
}
<div id=ChildDiv>
</div>
</div>
This code is by no means comprehensive or production ready (I prefer to have a way of handling failed ajax calls and you might want to block off interactions until the call comes back just to mention two of the enhancements). Once again KnockOut would be the preferred way to do this.

Why is razor not adding the proper bootstrap classes to form controls?

I've just downloaded VS 2013 because its new native support to the Bootstrap framework.
I create my forms, but they look different to original bootstrap ones. Problem is that it seems as razor and its "EditorFor" method, is not adding the "form-control" class needed for bootstrap.
I could add it as an html parameter, but then I would have to do that for each and every text field in my site.
How can I fix/modify Razor for it to automatically add this class for this objects?
Try using TextBoxFor.
#Html.TextBoxFor(model => model.Title, new {style="border:1px solid red;"})
This works. and if you want to add any class
#Html.TextBoxFor(model => model.Title, new {#class="fooo"})
should work.
HTML classes must always be added manually. Nothing will do this for you automatically by default.
There are a few options for you:
Put the classes in the htmlAttributes, as you mentioned.
Create razor helpers or Html helpers.
Use a library like TwitterBootstrapMVC (I think this costs?)
At the end as definitely it's not an error, it's just that .NET decided not to include this. http://www.asp.net/mvc/overview/releases/mvc51-release-notes#Bootstrap
The easiest way I foudn was using Jquery and adding the class to all the field that I need to in my case all the text-boxes en select lists.
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script>
$(".text-box").addClass("form-control");
$("select").addClass("form-control");
</script>
}

Generating views at runtime from a database table

I have a technical question as to how I can generate Multiviews (views control) using MVC framework where the views are getting generated dynamically (get details from the DB).
As per asp.net the generation of the views (control) will need to be placed in PreInit or Load events of the page. Need some technical guidance on how to go ahead.
Or is it good practice to use again the question is how to. Any other alternate solution is also welcome..
You should probably try something like this:
Generating ASP.NET MVC View Controls According to XML Configurations
The data source is XML, but that doesn't matter; you should be able to adapt the technique to a database table containing the view metadata.
The "meta-view" code for an editor form would look something like this:
#model DynamicControlsCreation.ViewModels.DefaultControlsViewModel
<p>
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Controls.Count; i++)
{
<div>
#Html.HiddenFor(x => x.Controls[i].Type)
#Html.HiddenFor(x => x.Controls[i].Name)
#Html.EditorFor(x => x.Controls[i])
</div>
}
<input type="submit" value="Submit" />
}
</p>
Note that you don't get much "shape" when you generate a view in this manner; it's mostly useful for things like configuration forms. Although you can put metadata in the database like the locations of the controls on the page, by the time you do all that, you're probably better off just making conventional views.

Change the html layout generated by EditorForModel without having 2 views

I render my ViewModel with
Create.cshtml:
#using (Html.BeginForm("Create","TestTemplate")) {
#Html.EditorForModel()
}
I do not like the output content because all the divs are one below the other. What if I want a horizontal layout where one div floats lefthandside to the other div?
How can I modify such layout changes using the EditorForModel() helper when there is no html in my Create.cshtml view ?
UPDATE:
Why do I have to create TWO views. In the one I call #Html.EditorForModel() in the other EditorTemplate-view I do all the layout stuff which I could also do within the first view via EditorFor( x => x....) ??? This seems strange to me.
You want to create an EditorTemplate. Basically create a new folder called EditorTemplates in ~/Views/Shared and then create a new view within that folder that has the name of your model, e.g. Template.cshtml or whatever.
When you call #Html.EditorForModel() it will use this view as the template.
The question is pretty old, but to my eyes there is another approach that should be mentioned. Although MVC is not MVVM try to think a little more in that pattern. Since the generated HTML is nothing more than the content of a page it will be used like a view model in the browser. It is not the job of HTML to Format your page. Try to modify the layout of your page by using CSS which will be used to render the view in the browser.
Just use a CSS selectors like the following to change the layout of your form, the input elements or just a specific input element:
form { your CSS here }
form input { your CSS here }
form input[name="NameOfInput"] { your CSS here }
In these days the power of CSS is nearly unlimited: Let your items flow left, right or alternating and so on ;-) You won't need to create two views either.

Categories

Resources