I'm working over this module (Extended Registration). This module provides a simple way of showing custom user fields in the registration.
Overrides the AccountController form user and loads the Editor Template in the Registration template
AccountController
var shape = _orchardServices.New.Register();
var user = _orchardServices.ContentManager.New("User");
if (user != null) {
shape.UserProfile= _contentManager.BuildEditor(user);
}
return new ShapeResult(this, shape);
Register.cshtml
</div>
#if (Model.OERegister != null) {
<fieldset>
#Display(Model.UserProfile)
</fieldset>
}
<div>
The shape here is the Editor Template (EditorTemplate/Parts/template)
It Works just fine, but I need to hide some fields from the registration form.
I'm kind of lost here, and I want to do it in the most Orchish way.
Thanks in advance.
As far as I understand the question here is how to generally hide some fields from a form.
Depending on how the author of the form's code designed it or whether you're the developer, there are multiple ways:
Have parts of the form in separate shapes, and return their factories in a Combined driver result from a driver. This means, your driver building part of the form by providing the shapes for a part's editor will register multiple shapes (this is similar to what e.g. BodyPartDriver.Display() does: it returns three separate shape registrations). Now these can be individually controlled by placement, so you can also hide them.
If you have no such fine control over how the form is built, you can still override the shape templates containing the fields you want to hide. Then in the override you can freely customize the look of the form. Beware that unlike the first solution, this won't automatically prevent the user from posting the hidden fields' data to the server, and thus saving it.
BTW I don't think the ContentManager.New() can ever return null. Even if the content type is not statically defined (i.e. created from a migration or through ContentDefinitionManager) it will return a content item.
Related
What I'm trying to achieve is to customise what happens during the HttpPost/OnSubmit of a form created with Umbraco Forms, whilst still maintaining the default behaviour of the form and any subsequent workflow.
In my specific scenario, I need to be able to analyse the input of the form and based on that input I will send the user to a different "thank you" page, whilst also storing some elements of the originally submitted form in TempData.
I've tried the following:
I've tried to create a DocType controller (RenderMvcController), but this only allows you
to override the HttpGet, and not Post.
I cannot use a SurfaceController as I lose the functionality of the
module, Umbraco Forms.
I've tried to use a custom workflow, but this runs asynchronous to
the user's journey and I cannot change their experience.
There isn't much useful documentation available for this at all and I'm finding this task more difficult than I expected it to be.
In order to add a custom procedure after the submission of the form, and based on this procedure change the user journey you must do the following:
Create a new controller and inherit from UmbracoFormsController and override the OnFormHandled method
public class CustomUmbracoFormsController : UmbracoFormsController
{
protected override void OnFormHandled(Form form, FormViewModel model)
{
// Find the field in the form, then search for a matching value
var field = form.AllFields.FirstOrDefault(x => x.Alias == "deliveryOptions");
var fieldValue = model.FormState[field.Id.ToString()].GetValue(0).ToString();
// Add the value to the TempData
TempData["deliveryOptions"] = fieldValue;
}
}
The above is a basic implementation that doesn't account for NULL
Update the reference to UmbracoFormsController in /Views/Partials/Forms/Form.cshtml with your new controller from above.
...
#using (Html.BeginUmbracoForm<CustomUmbracoFormsController>("HandleForm"))
...
In the above example we analyse the form data and store some information in the TempData, we can set the form to redirect to a generic thank you page in which we can analyse the values in the TempData and change the view the user sees.
Also, if you are making changes to the Form values and what these to be updated, you'll need the Record Guid, which you can retrieve from TempData["Forms_Current_Record_id"] in conjunction with a new RecordStore object.
I have a model that looks like this:
Business
- Branch
- Phone(*)
- Phone Type
- Number
- Opening hours (*)
- Days in week
- Working period (*)
- From time
- To time
- Custom field (*)
- Name
- Value
- Address
- Address line
- City
- State
- Zip
- Yada yada
I created Editor Templates for each of the class types above.
I want to have a common Business editor template with a submit form that posts the entire structure to a single action and saves it, both for an existing or new entity.
Is Editor Templates the right approach? How do I submit the form along its entire downline?
How do I make Add and Remove buttons to add/remove phone numbers within the form?
How do I order items in the collection (i.e. I want to have arrows near each phone number so the user can move it up or down in the client list, then handle the saving on server, for that I already have the solution).
Bottom line, my issue is how to get the right values posted back to the server, and how to modify the inner collections on the client. Once the proper data is on the server in this way or another I'll know how to deal with it. My problem is the client side and the correct way of data submission.
Update
I saw this answer, which basically answers the 1st part of my question, tho the latter two still remain (add-remove-order buttons - manage collections on client).
My problem is not how to add/remove/reorder rows at the client's DOM, but how to modify the client data and then receive it in the server in the action the looks like this:
[HttpPost]
public ActionResult Save(Business business)
{
/// blah blah
}
Update
Here is how I try to shove in the new data:
View:
#Ajax.ActionLink("Add", "AddCustomField", new AjaxOptions { UpdateTargetId = "customFields", InsertionMode = InsertionMode.InsertAfter })
Action:
public PartialViewResult AddOpeningTimes()
{
var ot = new OpeningTimes();
ot.WorkingPeriods.Add(new WorkingPeriod());
var e = EditorFor(ot);
//just here for debugging, the values are both empty strings
e.ViewData.TemplateInfo.HtmlFieldPrefix = ViewData.TemplateInfo.HtmlFieldPrefix;
return e;
}
//this method is on the base controller:
protected PartialViewResult EditorFor<TModel>(TModel model)
{
return PartialView("EditorTemplates/" + typeof(TModel).Name, model);
}
The thing is the name for the appropriate fields are not enumerated as needed (Branches[0].CustomField[0].Key), instead, it's just Key.
As far as i know, there is no 'simple' way to do this.
Add button - you have to wire javascript that creates a part of form (eg. phone type select and phone text box) and set its id/name. Basically you find the last item in the form, which will have name of Phone[x].PhoneType, and set the values on new part of form to appropriate values with x + 1.
An option to avoid generating the part the form by yourself is to create a hidden 'template' and copy that. Then change id and name.
Remove button - if you simply deleted items from DOM, you would create gaps in the sequence and MVC doesn't know how to deal with that. One possible approach is to mark items in the form as deleted using a hidden field, then handling that on the server.
Reordering - I would add a property called Order to whatever needs this feature, then render it as hidden and change using javascript when reordering. You also have to set it appropriately when adding an item.
Useful properties in these situations are also: IsNew, IsUpdated - along with IsDeleted allow for relatively easy processing on the server.
Of course, if you have nested collections each needing add/remove/reorder functionality it will be kind of difficult to do and debug.
UPDATE
The action rendering the partial view can't know what the html prefix should be, because it doesn't have the context (that the parent is Branch object etc.).
If you want to use AJAX, i would recommend sending the html field prefix as a parameter (public PartialViewResult AddOpeningTimes(string htmlPrefix)). htmlPrefix could be Branches[0].CustomField[last_custom_field + 1].. It's probably the cleanest way to achieve what you want, even if it's in fact not very clean.
I have a class called "PropertyFeature" which simply contains PropertyFeatureID and Description. It's a proper model created through LINQ to SQL mapped to an SQL Server database table. An example instance/row would be:
PropertyFeatureID: 2
Description: "Swimming Pool"
The number of rows (PropertyFeatures) can of course grow and shrink, and so I want to dynamically render a list of checkboxes so that the user can select any of them.
I can dynamically render the Checkboxes easily enough, with something like:
<%foreach (var Feature in (ViewData["Features"] as IEnumerable<MySolution.Models.PropertyFeature>)) { %>
<%=Html.CheckBox("Features", new { #id = Feature.PropertyFeatureID, #value = Feature.PropertyFeatureID })%><label for="Feature<%=Feature.PropertyFeatureID%>"><%=Feature.Description%></label>
<%}%>
I specify the ID for each checkbox and render the matching label so that the user can intuitively click the label and toggle the checkbox - that works great.
I set the CheckBox's "name" to "Features" so all the checkboxes render with the same name, and the MVC Model Binder piles them into a single collection called "Features" when the form is posted. This works nicely.
Once the form is submitted, I use the checked values and store them, so I need the actual integer values so I know which PropertyFeature is selected, not just a pile of Booleans and field names. So ideally, I want it as an array or a collection that's easy to work with.
I am able to retrieve the selected values from within my Controller method when the button is clicked because I have specified the parameter as int[] Features.
But the problem is that it doesn't maintain state. That is, when I click the submit button and the page reloads (with the form again displayed) I want all of the dynamic checkboxes to retain their checked status (or not). All of the other fields that I've created with Html.DropDownList and Html.TextBox all maintain their states successfully no problems at all on the same page in the same form.
I have spent hours reading all of the other threads and articles on similar issues and there is a lot of talk about using ICollection and IDictionary to bundle things up and include a Boolean value for each item so that it can maintain the checkbox state. But I don't 100% grasp how to use that in the context of my own personal example. I would like to keep the solution really simple and not have to code up pages of new classes just to maintain my checkbox state.
What is the cleanest and proper way to do this?
I got it working after much playing around with the various different approaches.
In the view:
<%string[] PostFeatures = Request.Form.GetValues("Features");%>
<% foreach (var Feature in (ViewData["AllPropertyFeatures"] as
IEnumerable<MySolution.Models.PropertyFeature>))
{ %>
<input type="checkbox" name="Features"
id="Feature<%=Feature.PropertyFeatureID.ToString()%>"
value="<%=Feature.PropertyFeatureID%>"
<%if(PostFeatures!=null)
{
if(PostFeatures.Contains(Feature.PropertyFeatureID.ToString()))
{
Response.Write("checked=\"checked\"");
}
}
%> />
<label for="Feature<%=Feature.PropertyFeatureID%>">
<%=Feature.Description%></label> <%
} %>
In the receiving controller method:
public ActionResult SearchResults(int[] Features)
This method has a number of advantages:
Allows labels to be clicked to toggle the corresponding checkboxes (usability).
Allows the Controller method to receive a super tidy array of ints, which ONLY contains the ints that have been selected - and not a whole other pile of items which were unselected or containing false/null/blank/0 etc.
Retains the checkbox's checked state when the page reloads containing the form, i.e. the user's selection is retained.
No random/stray type=hidden input fields created from the default ASP.Net MVC Html.CheckBox helper - I know it does those for a good reason, but in this instance, I don't require them as I only want to know about which IDs have been selected and for those to be in a single, tidy int[].
No masses of additional server side bloated classes, helpers and other happy mess required to achieve such a simple thing.
I would recommend this approach for anyone wanting the cleanest / bloat-free solution for a dynamic checkbox list where you need the IDs and you just want to get down to business!
The problem is that when you are rendering your list of checkboxes, you aren't setting any of them as selected. You will need to set your int[] Features in ViewData, and then in your foreach loop, check to see if the ID of that Feature is in the array in ViewData.
something like:
<%=Html.CheckBox("Features",
((int[])ViewData["SelectedFeatures"]).Contains(Feature.PropertyFeatureID),
new { #id = Feature.PropertyFeatureID, #value = Feature.PropertyFeatureID })%
although I didn't test it, so it might not be 100%.
I have two forms on one page: a results form and a search form. The search form uses a partial view because it is displayed on several different pages. I want to be able to persist the data in the search form regardles of which button on which form the user clicks. The problem is that when the user clicks on a link or button from the results form, only the form values from the results form are posted, the values from the search form are not included. How can I maintain the values in the search form even when it is not the form that is submitted? I do not want to use any type of session state to maintain the form and I dont want to write the search values in hidden fields in the results form. I just want to be able to post them with the form values of the results form so that the users search criteria can be maintained accross any page that displays the search partial view. What am I missing?
The first thought that occured to me is to remove the form wrapping the search control and just let it be rendered into the form with the results data. I worry here about naming conflicts. What happens when the search from has a control with the same name as the results form, wouldn't this cause a naming conflict? I suppose that this could just be managed manually to ensure that there are unique names whenever rendering partial views into other views, perhaps even going so far as to prefix values with the partial view name, but that reminds me of the ugliness that is INamingContainer in web forms - plus makes for cumbersome field names in your model.
Is there some sort of elegant solution that will allow a form to persist state that I am missing? Thanks!
Normally, I persist the search criteria on the server side when the search is performed. If the user changes the search criteria after performing the search, then posts the form any changes are, of course, lost but that's arguably correct behavior since the search wasn't invoked. This is true whether the search is performed from a full post or via ajax. Handling it this way keeps the actions cleaner, I think as you don't need to handle the search data in the other actions.
If you absolutely need to have the search parameters included, you could consider having the second form post via javascript, pick up the search field values dynamically and add them to the second form (as hidden fields) prior to posting the second form. You wouldn't have to maintain the values in two places in synchronization, but you would have to copy them to the second form before posting.
At the moment i got it like this:
Forms which has search box, posts query (and additional data if needed) to search controller which then renders search view. Search view is made from search box and search results partial views. During this - search box form is reconstructed by posted data.
If i need search results form to perform another search request (for example, with specified page index), it goes through ajax, which posts search box form + page index from search results form. Take a look here for ideas (update that JS method with targetId parameter for updating specified div/form and post additional data if needed here like this:
form.serialize()+"&pageIndex=5"
In short: if you need to maintain state of form + update another in one page - consider using partial updates, otherwise you will end up inventing ViewState 2.0.
One caveat with this way - it's tricky to make search box contain something what is related with search results (i.e. - total count of found items). Before i managed to handle this, our designer did that - i just need to add div with appropriate class name ("sbsubst" or something) and it looks that it's inside search box. :)
When you have few forms at your page each form sends only its own data. In WebForms you had only one form (at least server-side) and each control was included into this form. In ASP.NET MVC you can use the same scenario and I'm afraid you will have to if you want to have the described behavior. Don't forget - partial forms don't have to be real forms. Moreover, RenderPartial is mostly used for "control-like" layout creation.
As for the second part of your question I would suggest naming your text boxes in search form with some normal prefix like "search" or something like that. For instance, if you have text box "text" and "language" in the form, you will have "searchText" and "searchLanguage". These names are quite unique and you will have normal names in your parameters.
I am not suggesting you populating the hidden values in your results form on POST event since you said it's not an option for you but still it may be the only way if you want to have two forms.
I think the best approach will be storing the text from search input when it changes in the query part of your second form action url. For example (not tested):
$('input#yourSearchInput').change(function()
{
var searchText = $(this).val();
// or? var searchText = encodeURIComponent($(this).val());
var secondForm = $('form#secondFormId');
var action = secondForm.attr('action');
var queryStart = action.lastIndexOf('?search=');
if(queryStart > -1) {
action = action.substring(1, queryStart);
}
action = action + "?search=" + searchText;
secondForm.attr('action', action);
});
In Controller (or custom filter):
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var search = Request.QueryString["search"];
if(!String.IsNullOrEmpty(search)) {
ViewData["SearchFromPOST"] = search;
}
base.OnActionExecuting(filterContext);
}
In your Search Control:
<%= TextBox("yourSearchInputId", ViewData["SearchFromPOST"]) %>
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
This is a general design question: How would you implement a dynamic (runtime generated) form in ASP.NET MVC?
Here's the situation:
A site admin can define form parameters (fields, type of fields, validation) with a GUI (MVC view).
As needed, the runtime generates the form for the end user based on the admin configuration. I'm assuming that all of this logic would reside in the controller - or perhaps extension methods, action filters or something like that.
End user fills out the form, hits submit, information is captured in database.
The customization does not need to support nested controls, 3rd party controls and so forth, but I suspect a very elegant design would allow for that. Mostly, I just need the admin to be able to specify additional fields as textboxes, checkboxes, radio buttons and comboboxes. I will also need the application to allocate a space for this data to be saved in the db, but I believe I have that part figured out.
Thanks for the help.
I had the same need in a recent project. I created a class library for this. I just released a new version of the library.
Maybe it can help you:
ASP.NET MVC Dynamic Forms
You can do this very easily using my FormFactory library.
By default it reflects against a view model to produce a PropertyVm[] array, but you can also create the properties programatically, so you could load settings from a database then create PropertyVm.
This is a snippet from a Linqpad script.
```
//import-package FormFactory
//import-package FormFactory.RazorGenerator
void Main()
{
var properties = new[]{
new PropertyVm(typeof(string), "username"){
DisplayName = "Username",
NotOptional = true,
},
new PropertyVm(typeof(string), "password"){
DisplayName = "Password",
NotOptional = true,
GetCustomAttributes = () => new object[]{ new DataTypeAttribute(DataType.Password) }
}
};
var html = FormFactory.RazorEngine.PropertyRenderExtension.Render(properties, new FormFactory.RazorEngine.RazorTemplateHtmlHelper());
Util.RawHtml(html.ToEncodedString()).Dump(); //Renders html for a username and password field.
}
```
Theres a demo site with examples of the various features you can set up (e.g. nested collections, autocomplete, datepickers etc.)
Another option is to have a very loosely coupled database schema.
//this will contain all the fields and types that the admin user sets
**ApplicationFields**
FieldName
FieldType
...
//these are all the values that have some mapping to a ParentObjectID
**FormValues**
ParentObjectID
FieldName
FieldValue
When you submit your runtime generated View (from ApplicationFields) then just loop through your FormCollection and try and set it on the ParentObject you need to update.
public ActionResult MyForm(FormCollection form)
{
//this is the main object that contains all the fields
var parentObject;
foreach (string key in form)
{
parentObject.SetValue(key, form[key]);
}
...
Then your parentObject might be something like this...
public partial class ParentObject
{
IList _FormValues;
public void SetValue(string key, string value)
{
//try and find if this value already exists
FormValue v = _FormValues.SingleOrDefault(k => k.Key == key);
//if it does just set it
if (v != null)
{
v.Value = value;
return;
}
//else this might be a new form field added and therefore create a new value
v = new FormValue
{
ParentObjectID = this.ID,
Key = key,
Value = value
};
_FormValues.Add(v);
}
}
One way to do this is to create your own ModelBinder which would be at the heart of your generated forms. A modelbinder is responsible for validating the ModelState and rebuilding the typed ViewDataModel (assuming your views are typed).
The DataAnnotations model binder could be a good reference for this what this custom modelbinder allows you to do is via Attributes on your ViewDataModel describe the attribute's validation (and hint at UI rendering). However this is all defined compile time but would be a great reference to start off writing a custom modelbinder.
In your case your model binder should get the validation for a field at runtime from an xml file/string.
If you have a route like:
routes.MapRoute(null, "Forms/{formName}/", new { action = "Index", controller = "Forms", formName = ""}),
Then you could locate the correct form xml in FormsController.Index(string formName) and pass it to the view.
The FormsModel should hold all the possible methods to get data I dont see any other way around this. The Xml could map to a function name (possibly even arguments) that you can invoke using reflection on the FormsModel to fill the ViewData or typed ViewDataModel with data.
The view for Form Index could generate a form from that xml through an HtmlHelper Extension that takes an XmlDocument.
Then when you (or asp.net mvc) binds your form to your ViewData your custom model binder is invoked it can inspect the current controller values to look for the formName and look up the corresponding xml that holds all the validation rules. The ModelBinder is then responsible for filling up ModelState with any runtime defined errors.
It's a hard task but when pulled off succesfully well worth it in my view :)
Update a better alternative to model data would be a very loose database schema as David Liddle suggests. I'd still go through the trouble of saving it as xml (or someother serialized format) and using that for generating the view and to hold validation rules for a custom ModelBinder so that you have more control on layout and validation of each field.
cottsak's answer is very attractive.
There are at least two client-side XForms engines. Here's one:
https://community.emc.com/community/edn/xmltech
I can't see huge advantages of generating XForms or any other "abstraction" over the HTML comparing with straight forward generation of HTML with "Web Forms 2.0" controls list for model like List<Tuple<Meta, Value>>. Note: on server side you in any case would be obligated to parse results manually to fit it to your structrures.
Searching for "next layer abstractions" is good for rapid development, but see, "generate code" (runtime or build-time) has its own specific. Usually the generating code of "lower layer" is the better solution than generating the "higher abstract layer" code.
So just go and wirte code that generate Web 2 Controls in #Foreach loop.