Comments (11)
Are you talking about A. getting the values into the form so the form displays correctly, or B. posting the values from the form to the server side ?
Code example would help me understand what you are after.
from formfactory.
I'm talking about B.
In the example I see:
@using (var form = Html.FormForAction((AccountController ctlr, AccountModel model) => ctlr.Update(model)))
{
form.AdditionalClasses = "form-horizontal";
form.Render();
}
But in .NET Core the HTML.FormForAction are commented. And this example seems that are only on the FormFactory.AspMvc.Example, in the FormFactory.AspNetCore.Example It seems that there is no View to this example.
from formfactory.
Hmm, I think FormForAction never got ported to Core, sorry.
You can create the form tag using normal html/TagHelpers, then render just the fields using FF (using FF.PropertiesFor(model).Render())
from formfactory.
That's not my problema, my problem is how (once that I make submit) I get the values in the model. It's any way to do it automatically or should I do it manually?
from formfactory.
The posted values should model bind to the model type. What does your controller code look like?
from formfactory.
Ok,
It's my fault. I was generating dynamically and compiling the classes of the forms, because I need that the final user are allowed to change the forms, and that works ok in for the render part, but it doesn´t work automatically for the View to Controller class when I don´t know the type in compile time. I tested it with a class in the project and it works well. I'm only need to figure how to make that the view can pass the data to the controller when both View and Controller knows the type at runtime.
from formfactory.
from formfactory.
@mcintyre321 - Trying to figure this out on my end as well...
In an example test controller, I fed it just the nestedFormExample2:
public class TestController : Controller
{
public ActionResult Index()
{
return View("Index", new IndexModel() { ex = new NestedFormsExample2() });
}
public class IndexModel
{
public NestedFormsExample2 ex { get; set; }
}
public ActionResult Save(NestedFormsExample2 form)
{
return Json(form);
}
}
When I submit back the output is
{"ContactMethod":{"SocialMediaType":{"FacebookName":null}}}
Even if I select "No contact method" or "Phone contact method". Unsure why this model binding is not hitting correctly. Any tips?
I tried this on both the mvc and aspnetcore examples, both before and after pullrequest 62 (nested icollections).
from formfactory.
Do you have the json that gets posted?
To get postbacks to work, it may be necessary to use polymorphic deserialization,
which requires json to be posted with type information e.g.:
{
"ContactMethod": {
"ObjectType ": "PhoneContactMethod",
"Number": "123",
"Type": {
"ObjectType ": "Landline",
"Provider": "BT"
}
}
}
to
public class TestController : Controller
{
....
public ActionResult Save(JToken form)
{
NestedFormsExample2 deserialized = form.ToObject<NestedFormsExample2>();
...
}
}
You can do this by a. using the https://github.com/manuc66/JsonSubTypes libray to mark up the base objects, so Json.Net knows what Type something should be deserialised as, and b. adding a property to add the discriminator as a hidden field in the form
[JsonConverter(typeof(JsonSubtypes), nameof(ContactMethod.ObjectType))]
[JsonSubtypes.KnownSubType(typeof(PhoneContactMethod), nameof(PhoneContactMethod))]
[JsonSubtypes.KnownSubType(typeof(NoContactMethod), nameof(NoContactMethod))]
[JsonSubtypes.KnownSubType(typeof(SocialMedia), nameof(SocialMedia))]
public abstract class ContactMethod
{
public ContactMethod(){
ObjectType = this.GetType().Name;
}
[Hidden]
public string ObjectType { get; set;}
}
BTW I haven't actually tried JsonSubTypes , but I think it should work! Let me know how you get on
from formfactory.
Hi, I work along with @reiphil
Here was my solution that I came up with last night. Beware lengthy post
1st - Created c# interface to standardize polymorphic nested forms
public interface IClassFormSelector<T>
{
T SelectedClass { get; set; }
IEnumerable<T> SelectedClass_choices();
}
which is then used like this:
public class ProjectBuilderOptions : Core.Models.IClassFormSelector<Builder>
{
public ProjectBuilderOptions()
{
SelectedClass = new SimpleBuilder() { DescriptionSimple = "Simple" };
}
public Builder SelectedClass { get; set; }
public IEnumerable<Builder> SelectedClass_choices()
{
yield return SelectedClass is SimpleBuilder ? SelectedClass.Selected() : new SimpleBuilder();
yield return SelectedClass is ConcurrentBuilder ? SelectedClass.Selected() : new ConcurrentBuilder();
yield return SelectedClass is GraphicBuilder ? SelectedClass.Selected() : new GraphicBuilder();
}
}
and then I have a projectmodel which is the root of the form:
public class ProjectModel {
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public List<Builder> Builders { get; set; } = new List<Builder>();
private bool Builders_show => false;
public ICollection<ProjectBuilderOptions> _Form_Builders { get; set; } = new List<ProjectBuilderOptions>();
public ProjectModel() {
}
}
Notice that I use _Form_Builders and this just means I want to bind the list of selectedclasses back to Builders later on.
From here the form rendering works as expected.
2nd - fixed a bug in FormFactory.js in order to set the modelname correctly on nested collection elements
I added:
var idPrefix = AssembleId($(this).closest(".ff-collection").find("> ul").children().last());
if (idPrefix != "") {
modelName = idPrefix + "." + modelName;
}
......
function AssembleId(elementRef) {
var id = "";
var parent = $(elementRef).closest(".ff-collection-new-item").find("input[type='hidden']");
if (parent.length > 0) {
var input = parent.eq(0);
id += $(input).prop("name").replace(".Index", "[" + $(input).val() + "]");
}
return id;
}
3rd updated FF.cs to include full object type info
var typeVm = new PropertyVm(typeof(string), "__type")
{
DisplayName = "",
IsHidden = true,
Value = model.GetType().FullName + ", " + model.GetType().Assembly.FullName
};
4th added some javascript to my index.cshtml to handle creating a nice json object out of html form inputs that are rendered to the screen.
To do this I utilize jquery-serialize-object.
The below script will first create a nice json format to represent our form and then second go through and recursively process the json form and handle linking _Form_Builder "SelectedClass" values with Builder array.
$("#frm").submit(function (e) {
e.preventDefault();
var frmData = $(this).serializeObject();
traverse(frmData, process);
$.ajax({
type: 'POST',
url: "/home/save",
data: JSON.stringify(frmData),
dataType: "text",
contentType: "text/plain",
processData: false
});
});
function process(key, value, parent) {
if (key.indexOf("_Form_") > -1) {
var arr = [];
for (var i in value) {
if (typeof value[i].SelectedClass !== 'undefined') {
arr.push(value[i].SelectedClass);
}
}
parent[key.replace("_Form_", "")] = arr;
delete parent[key];
}
if (value !== null && typeof (value) == "object") {
traverse(value, process);
}
console.log(parent);
console.log(key + " : " + value);
}
function traverse(o, func) {
for (var i in o) {
func.apply(this, [i, o[i], o]);
if (o[i] !== null && typeof (o[i]) == "object") {
traverse(o[i], func);
}
}
}
here is the form after serializing it
{
"__type":"Project.Web.Controllers.HomeController+ProjectModel, Project.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Name":"test",
"Description":"test 2",
"_Form_Builders":{
"Index":"fdc3dd1b-f7bb-4f7d-81e0-080cd585dc29",
"fdc3dd1b-f7bb-4f7d-81e0-080cd585dc29":{
"__type":"Project.Web.Controllers.HomeController+ProjectBuilderOptions, Project.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"d29430ad-b6cb-43e6-bdb3-adb3462cf505":"on",
"SelectedClass":{
"__type":"Project.Web.Controllers.HomeController+ConcurrentBuilder, Project.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"DescriptionConcurrent":"Concurrent!",
"_Form_Tasks":{
"Index":"dbe505eb-0560-425a-9f6d-bf5c2ca6e0e2",
"aa54face-cfb3-4801-8743-0661a1e41c68":{
"__type":"Project.Web.Controllers.HomeController+BuildActionOptions, Project.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"a04800a4-5a68-41c1-b2b0-6a9a140ca4af":"on",
"SelectedClass":{
"__type":"Project.Web.Controllers.HomeController+WinBatchAction, Project.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Description":"Windows Batch Action",
"BatchCommand":""
}
},
"dbe505eb-0560-425a-9f6d-bf5c2ca6e0e2":{
"__type":"Project.Web.Controllers.HomeController+BuildActionOptions, Project.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"a04800a4-5a68-41c1-b2b0-6a9a140ca4af":"on",
"SelectedClass":{
"__type":"Project.Web.Controllers.HomeController+GraphicsBuildAction, Project.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Name":"",
"Disabled":"false",
"Category":"",
"TestSettingsPath":"",
"_testUniqueIdentifier":"",
"TestContainers":"",
"TrxOutputFilePath":"",
"Description":"",
"ContinueOnError":"false",
"CommandText":""
}
}
}
}
}
}
}
--------------------------------
and here it is after doing the recursive processing on it (its a little easier to read now)
--------------------------------
{
"__type": "Project.Web.Controllers.HomeController+ProjectModel, Project.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Name": "test",
"Description": "test 2",
"Builders": [
{
"__type": "Project.Web.Controllers.HomeController+ConcurrentBuilder, Project.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"DescriptionConcurrent": "Concurrent!",
"Tasks": [
{
"__type": "Project.Web.Controllers.HomeController+WinBatchAction, Project.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Description": "Windows Batch Action",
"BatchCommand": ""
},
{
"__type": "Project.Web.Controllers.HomeController+GraphicsBuildAction, Project.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Name": "",
"Disabled": "false",
"Category": "",
"TestSettingsPath": "",
"_testUniqueIdentifier": "",
"TestContainers": "",
"TrxOutputFilePath": "",
"Description": "",
"ContinueOnError": "false",
"CommandText": ""
}
]
}
]
}
Finally 5th added our save method which will deserialize the json form body that was posted
[HttpPost]
public JsonResult Save() {
Stream req = Request.Body;
string body = "";
using (StreamReader reader = new StreamReader(Request.Body, System.Text.Encoding.UTF8))
{
body = reader.ReadToEnd();
}
body = body.Replace("__type","$type");
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<ProjectModel>(body, new Newtonsoft.Json.JsonSerializerSettings() {
TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Objects,
TypeNameAssemblyFormatHandling = Newtonsoft.Json.TypeNameAssemblyFormatHandling.Full
});
return Json(body);
}
So far it has no issues deserializing these polymorphic complex types.
Hopefully this can help someone else out :)
from formfactory.
Nice, glad to see you are really using FormFactory to it's full potential!
I've used form2js to serialize the forms to the ASP.NET bindable format with collection support - if you find jquery-serialize-object isn't working (e.g. for collection ordering), you could try that.
There is a security risk from using real Type names in your __type
variable - an attacker can put any Type name in there and get your system to create and fill an instance. I would recommend using the project I linked above, or using the KnownTypesBinder from https://www.newtonsoft.com/json/help/html/SerializeSerializationBinder.htm
from formfactory.
Related Issues (20)
- Installation not working with .net core 3+ HOT 1
- Sorry, an error occurred while processing your request. HOT 2
- Improvement for radio buttons
- The sample site doesn't load HOT 1
- Looking a way to extend FormFactory functionalities HOT 5
- For the dynamic form submit, how to add an additional Save button which does not do FF client side validation?
- add and remove actions don't work for the editable collection
- Validation not working on programmatically created form HOT 1
- Live Website Docs are Down HOT 4
- Asp Net Core 3.0 AggregateException The partial view 'FormFactory/Form.Property' was not found. HOT 9
- File Upload Control HOT 1
- Multiple Textbox HOT 1
- IIS Publish Error HOT 1
- Dynamic forms from Propertyvm HOT 2
- Repeatable row/table? HOT 2
- Client validations not working when using PropertyVms to create Dynamic Forms. HOT 3
- Documentation on custom templates? HOT 2
- No example provided for a server side validation HOT 1
- FormFactory Templates installs all files in root of project
- Does this work with Blazor? HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from formfactory.