Can anyone suggest a good way to develope dynamic forms with ASP.NET MVC?
I have cascading dropdowns on the page (options in the dropdown depends on the value, selected in the previous dropdown).
All the values come from the database.
How can I implement such behavior using ASP.NET MVC?
Of course I'd like to receive all the values in the controller when I submit my form.
You can use FormCollection as a parameter to receive all data that comes from the form:
[HttpPost]
public ActionResult ActionName(FormCollection collection)
{
//collection["inputName"];
}
You can do this very easily using my FormFactory library.
By default it reflects against a view model to produce a PropertyVm[] array:
```
var vm = new MyFormViewModel
{
OperatingSystem = "IOS",
OperatingSystem_choices = new[]{"IOS", "Android",};
};
Html.PropertiesFor(vm).Render(Html);
```
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.)
If you need to have some dynamic fields in your form, the best way for you would be to use some advanced javascript frameworks like Angular, Backbone, Knockout etc.
If you need something more or less simple it is enough for you to use Knockout. For more advanced scenarios I would recommend Angular, but this is my personal preference.
Here is a simple implementation of a dynamic form with Knockout:
var model = {
users: ko.observableArray(),
addUser: function() {
this.users.push({ name: ko.observable() });
}
};
ko.applyBindings(model);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="foreach: users">
<input type="text" data-bind="value: name" /><br />
</div>
<button data-bind="click: addUser">Add user</button>
<ul data-bind="foreach: users">
<li data-bind="text: name"></li>
</ul>
Now, what about ASP.NET MVC?
This is more tricky. Perhaps the best and the easiest way would be to use Ajax and post JSON to ASP.NET MVC Action. First of all, you'll need to get a JSON object from your form. With knockout it's very simple:
var json = ko.toJSON(model);
Now, when we know how to get JSON from form, next step is to send your data to an Action. jQuery is perfect for that:
$.ajax({
type: "POST",
url: "#Url.Action("AddUser")",
data: ko.toJSON(model).users, // Serialize to JSON and take users array
accept: 'application/json',
success: function (data) { alert("Done!"); } // Your success callback
});
In our case we basically send an array of strings, thus ASP.NET MVC action should look like that:
[HttpPost]
public JsonResult AddUser(List<string> users)
{
return Json(data); // return something
}
This is definitely not the only one option of how to implement dynamic forms, but I think it's pretty effective. Hope it helps.
I would create partial views for each option of the dropdown and additional fields.
Than controller, that will return such parts of html according to dropdown value:
public class FormFieldsController : Controller
{
public ActionResult Index(string dropDownOption)
{
if(dropDownOption == "Option1")
return PartialView("PartialForOption1");
etc.//
}
}
Then just call it with jquery Ajax and append your current form with result of action, when the value of dropdown changes.
$.ajax("/FormFields/Index",
{
async: false,
data: { dropDownOption: $('#dropDownId').value()},
success: function(data) {
if (callback == null) {
$("#form").append(data);
} else {
callback(data);
}
},
error: function() {
alert('Error');
},
type: 'GET',
timeout: 10000
}
);
Try this http://mvcdynamicforms.codeplex.com/releases/view/43320
it should be helpful
Related
New to MVC.
Scenario is. Using a 3rd party upload library for images. When a form is submitted, I want to make a call via ajax to submit the data and return the inserted item id. I then use that id for the 3rd party upload library to build folders where the images will be uploaded to.
I have the ajax call working and inserting the data to the database and getting the inserted id. But when the debug returns from the controller, it renders the id as a whole page.
Missing something fundamental here to MVC I think.
cshtml file:
<div class="col-md-8">
<input type="submit" value="Add Item" id="submitItem" />
<script>
$(document).ready(function () {
$("#submitItem").submit(function () {
event.preventDefault();
insertData();
});
});
function insertData()
{
var requestData = {
userID: $("#hdnUserID").val(),
title: $("#title").val(),
typeID: $("#typeID").val(),
description: $("#description").val()
};
$.ajax({
url: '<%= Url.Action("ItemUserDashBoard", "Home") %>',
type: 'post',
data: JSON.stringify(requestData),
dataType: 'json',
success: function (data) {
// your data could be a View or Json or what ever you returned in your action method
// parse your data here
alert(data);
$("#fine-uploader-gallery").fineUploader("uploadStoredFiles");
},
processData: false
});
}
</script>
</div>
HomeController.cs
[HttpPost]
public JsonResult ItemUserDashBoard(ItemAppraise.Models.iaDashBoardModel objItemUserDashBoard)
{
if(ModelState.IsValid)
{
using (dbContext)
{
ia_items iaItem = new ia_items
{
userID = objItemUserDashBoard.iaItems.userID,
typeID = objItemUserDashBoard.iaItems.typeID,
title = objItemUserDashBoard.iaItems.title,
description = objItemUserDashBoard.iaItems.description,
lastUpdate = DateTime.Now
};
dbContext.ia_items.Add(iaItem);
dbContext.SaveChanges();
//objItemUserDashBoard.iaItems.itemID = iaItem.itemID;
return Json(iaItem.itemID.ToString());
}
}
else{
return null;
}
}
Fiddler shows it as having a header of Content-Type: application/json; charset=utf-8.
But the page renders under the control url 'http://localhost:55689/Home/ItemUserDashBoard' with just the item id showing.
How do I get the data back just to use in the success part of the ajax call and not be rendered? Is this Partial Views or something similar?
Any guidance is appreciated.
In standard MVC. Any call made to a controller is handled just like a web request. So if i understand you correctly - the result of your httpPost is being rendered instead of the desired View? This is because you are returning JSON, so the controller assumes that is what you are trying to render. If you want a View to be rendered instead (and somehow use that response data) you could try setting the return type to ActionResult and returning a View("nameofview"); You can pass your response data to that view in a number of ways.
As a side note I think the problem you are facing could be better solved with Web Api instead of MVC. It works well with MVC and could be a simpler way of implementing your desired functionality. Separating your post requests and database interactions from the logic which decides which View to return.
I want to use Krajee bootstrap-fileinput (http://plugins.krajee.com/file-input) with Mvc razor, Please help me out with the process to to upload images to server and json result from actionResult.
I have just included a required js and css files in my view page and added a line
<input id="input-702" name="kartik-input-702[]" type="file" multiple="true" class="file-loading">
and a script
$("#input-702").fileinput({
uploadUrl:"#Url.Action("upload","Home")",
uploadAsync: true,
minFileCount: 1,
maxFileCount: 5,
overwriteInitial: false,
initialPreview: "",
initialPreviewConfig:"",
uploadExtraData: ""
});
This line is getting formatted and showing a drag and drop effect and select file button.The file selection and Thumbnail creation is working properly but on upload action there is the (fileinput,js) function "ajaxSubmit" which use to post the content to HomeController ActionResult "Upload".
ajaxSubmit: function (fnBefore, fnSuccess, fnComplete, fnError) {
var self = this, settings;
self.uploadExtra();
settings = $.extend({
xhr: function () {
var xhrobj = $.ajaxSettings.xhr();
return self.initXhr(xhrobj, 98);
},
url: self.uploadUrl,
type: 'POST',
dataType: 'json',
data: self.formdata,
cache: false,
processData: false,
contentType: false,
beforeSend: fnBefore,
success: fnSuccess,
complete: fnComplete,
error: fnError
}, self.ajaxSettings);
self.ajaxRequests.push($.ajax(settings));
},
Now i want to save all the files to server which are not uploaded using ActionResult and pass a value back to js. how to retrieve formdata and process.??
I was trying to find the solution to above problem and finally found the one. As I am able to save the file at server location from the controller but unfortunately I could not send the json response from controller to javascript.
Saving image using controller action :
[HttpPost]
public ActionResult upload()
{
foreach (string item in Request.Files)
{
HttpPostedFileBase file = Request.Files[item] as HttpPostedFileBase;
string fileName = file.FileName;
string UploadPath = "~/Images/";
if (file.ContentLength == 0)
continue;
if (file.ContentLength > 0)
{
string path = Path.Combine(HttpContext.Request.MapPath(UploadPath), fileName);
string extension = Path.GetExtension(file.FileName);
file.SaveAs(path);
}
}
return Json("");
}
To implement the delete button we have to Send Data from server in Asynchronous mode as a json array object eg.
initialPreview: [
'<img src='/images/desert.jpg' class='file-preview-image' alt='Desert' title='Desert'>',
],
initialPreviewConfig: [
{
caption: 'desert.jpg',
width: '120px',
url: '/localhost/avatar/delete',
key: 100,
extra: {id: 100}
}
]
Can anybody help me out to create json array object in controller and send it as a response object.???
I have a similar problem. Got it working (with the ajax calls) but always get one file in the request (whereas I need to get multiple but should be working for you!)
My code is as follows:
HTML:
<input id="input-id" type="file" data-preview-file-type="text" name="fileUpload[]" multiple class="file-loading" />
JS:
$("#input-id").fileinput(
{
uploadUrl: "/Galleries/Upload",
uploadAsync: true,
maxFileCount: 5
});
MVC controller which I would expect to be working - but i not:
[HttpPost]
public JsonResult Upload(IEnumerable<HttpPostedFileBase> fileUpload)
{
//somde logic
}
However when in side the method I will call:
Request.Files
My scenerio is that user:
clicks on 'browse'
selects picture and is able to see it in the preview
click on 'browse' once again
selects picture and the picture is appended to the current preview list
clicks upload and I get all the files in the controller
The only way I figure it, how it could be working is the ajax call switch on the plugin which allows to append the preview list. Unfortunatelly I can't submit all the pictures to the controller then.
Good day! I need to render a model's attributes to JSON so I can pass them into a template.
Model:
var UserInfo = Backbone.Model.extend({
url: appConfig.baseURL + "users/",
});
Template:
<script type="text/html" class="template" id="profile-form">
<h2 class="ui-li-heading"><%= username %></h2>
<p class="ui-li-desc"><strong><%= phone %></strong></p>
</script>
View:
var ProfilePageView = Backbone.View.extend({
events: {
'click #edit': "edit"
},
initialize: function () {
this.template = $.tpl['profile-form'];
var user = new UserInfo()
user.fetch({
data: $.param({email: localStorage.getItem('user_email')}),
type: 'POST'
});
console.log(user) //returns correct object with attrs
console.log(user.toJSON()) //returns empty object
},
render: function (eventName) {
$(this.el).html(this.template());
},
edit: function () {
window.workspace.navigate('#account/edit', { trigger: true});
}
});
When i put in console something like this, user.toJSON() returns correct data
var user = new UserInfo();
user.fetch({
data: $.param({email: localStorage.getItem('user_email')}),
type: 'POST'
});
But when i put it to my view, its returns Object {}.
Where is a mistake or tell me how can differently pass to the template data received from the server in json format? Thanks!
You appear to have two problems. fetch is asyncronous, so you need to use a callback to use the information. But first, an explanation about toJSON. .toJSON() doesn't actually return a JSON string, it returns an object that is what you want JSON to stringify. This allows you to modify the toJSON method to customize what attributes will be taken from your model or collection and added to the JSON string representation of your model. Here is a quotation from the Backbone.js docs:
toJSON collection.toJSON([options])
Return a shallow copy of the model's attributes for JSON
stringification. This can be used for persistence, serialization, or
for augmentation before being sent to the server. The name of this
method is a bit confusing, as it doesn't actually return a JSON string
— but I'm afraid that it's the way that the JavaScript API for
JSON.stringify works.
So you should replace this line in your code
console.log(user.toJSON())
with this one
console.log(JSON.stringify(user))
The object that you saw was returned by toJSON will then be turned into JSON.
Now, even after you do that, it won't work properly, because you will execute the console.log before you get the data for your model from fetch. fetch is asynchronous, so you need to call any code you want to be executed after the fetch is done in the success callback:
user.fetch({
data: $.param({email: localStorage.getItem('user_email')}),
type: 'POST',
success: function(){
console.log(user);
console.log(JSON.stringify(user));
}
});
I can successfully make a jQuery Ajax call into my C# Controller and receive back an XML string, but I need to in turn gather some Portfolio dates and package them up into a JSON object so I can send them back into another C# Controller.
If it's a C# issue, then I apologize if I'm in the wrong forum...however I'd like to pass my JSON object into the server side controller ..
Here's what I'm trying to do:
var nodeDatesJson = {"nodedates": // CREATE JSON OBJECT OF DATE STRINGS
{ "date": 01/20/2012,
"date": "01/21/2012" } };
getTradeContribs(thisPfId, nodeDatesJson.nodedates.date);
Now call the next js function:
function getTradeContribs(pfid, nodedates) {
//alert(nodedates);
$.ajax({ // GET TRADE CONTRIBS FROM SERVER !!
url: "/Portfolios/getTradeContribs?portfolioId=" + pfid + "&nodedates=" + nodedates,
type: "GET", // or "PUT"
dataType: "json",
async: true,
success: parseTradeContribs,
error: function (error) {
alert("failed in opening Trade Contribs file !!!");
}
});
}
function parseTradeContribs(data) {
alert("In parseTradeContribs..." );
$(data).find("Trade").each(function(){
$(".TradeContrib").append($(this).text());
})
}
and my C# controller is trying to read in the "nodedates" JSON object, but HOW do I read it in ?
public string getTradeContribs(string portfolioId, **string nodedates**)
{
// Build Portfolio Select request here !
RequestBuilder rzrRequest = new RequestBuilder();
// REQUEST FOR CONTRIBUTIONS !
// ... more code here..
xmlResponse.LoadXml(contribResponse);
string jsonTest = #" {""nodedates"": ""date"":""01/01/2012""}";
//return xmlResponse.OuterXml; // WORKS FINE
return "<Trade><TradeId>1234</TradeId></Trade>"; // RETURN TEST XML STR
}
thank you in advance...
Bob
The best way to receive a list of dates in a MVC action is to bind to a collection. What this means is that you should put your dates and other attributes in a form with the following naming convention:
<input type="hidden" name="dates" value="2012-1-20" />
<input type="hidden" name="dates" value="2012-1-21" />
Then you should serialize this form (look into jquery's docs for this) and post its data to your action, which will be something along the lines of:
public ActionResult getTradeContribs(string portfolioId, IList<DateTime> dates) {
// Do your work here
}
You should really take a look into MVC Model binding and collection binding as well:
Model binding to a list
Model binding objects
Also, if I may, your javascript object has two properties with the same name, which is probably not what you mean. If you want to have multiple dates stored somewhere in a object, you should use an array:
var nodeDatesJson = {"nodedates":
[ "01/20/2012", "01/21/2012" ] };
Sorry, but I didn't understand your doubt very well...but here it goes:
Maybe you should pass the json, well-formatted, as a string and use some C# parser.
This way you can get a object in server-side as same as the Json object in javascript.
=]
Hi I'm kind of new to Knockoutjs, I am in the scenario where I want to post a form where I have for example an email address, there is an requirement that the email address needs to be unique.
On the server I check if the email address is unique or not and then returns an validationjson class for example
{
isEmailUnique: false,
isPasswordStrongEnough: true;
}
How can I with knockoutjs validation show these errors in a neat way?
I would use two different server side validators for this, since they affect different observables in the view model.
Originally taken from the knockout validation readme
ko.validation.rules['isEmailUnique'] = {
validator: function(val, param){
var isValid = true;
$.ajax({
async: false,
url: '/validation/isEmailUnique',
type: 'POST',
data: { value: val, param: param },
success: function(response){
isValid = response === true;
},
error: function(){
isValid = false; //however you would like to handle this
}
});
return isValid;
},
message: 'The Email is not unique'
};
Then on the server you need to create an endpoint that accepts POST requests where you perform your lookup and then return true or false depending on the result of the query.
To use the above validator
this.email = ko.observable()
.extend({
isEmailUnique: {
message: 'Something else perhaps? It will override the message in the validator'
}
});
You can use the very same thing for the password strength validation.
Using validators like this will fire validation when the observable changes, which can be a useful way to do validation.
I'm a bit late, but for my 2 cents worth, I would take a more generic approach such as returning a standard JSON serialized AjaxResult class from your server endpoints (such as /Register) with properties such as Data (an arbitrary container used for, for example, an updated model to re-bind with the mapping plugin), and a collection of validation message strings, etc. Then you could have an HTML validation summary which is bound to an ObservableArray and push / map the messages from your Ajax result into there. This is essentially what I've been doing with Knockout and it works nicely.