Submitting Ajax form but nothing sent to controller? - javascript

I am using the following viewModel on my view:
public class CourseViewModel
{
public IEnumerable<Course_page> Course_page { get; set; }
public IEnumerable<course_section> Course_section { get; set; }
public IEnumerable<course_subsection> Course_subsection { get; set; }
public IEnumerable<Enrollment> Enrollments { get; set; }
}
In my view I have the following ajax form:
#using (Ajax.BeginForm("updateprogress", "User", new { EnrollmentID = Model.Enrollments.Single().EnrollmentID }, new AjaxOptions
{
HttpMethod = "POST",
}, new { id = "ajaxForm" }))
{
#Html.HiddenFor(Model => Model.Enrollments.Single().progress1, new { id = "scenecheck" })
}
I have an animation generated by a program called Hype, within the animation are scenes and the current scene number is called using a javascript function with the value populating the form above in the 'progress1' field:
function updateprogress(hypeDocument, element, event) {
var scenenumber = hypeDocument.currentSceneName();
hypeDocument.getElementById('scenecheck').value = scenenumber;
$('form#ajaxForm').submit();
}
I have some other javascript which listens for the scene change and calls the function above whenever it does change.
When I change HiddenFor to TexboxFor on the page I can see that everything is working correctly within the view, the scene number is present in the textbox.
My controller action when this form submits is as follows:
[Authorize]
[HttpPost]
public ActionResult updateprogress(int EnrollmentID, CourseViewModel model)
{
Enrollment enrollment = db.enrollment.Find(EnrollmentID);
Mapper.Map(model, enrollment);
db.SaveChanges();
return null;
}
I'm using Automapper to map the change to the viewmodel back to the actual model, only a single field is to change in the viewmodel. This might not be the ideal way to do it, I'm still trying to work out how I should approach this.
My two issues are:
1 - The form does not appear to pass anything back to the controller when it submits. So the 'model' object is empty, null. This leads me to believe that what I thought I understood about what I was doing was wrong. Why is the updated field not being passed back to the controller?
2 - Is this the best method for updating the value of a single field in the viewmodel?
Thanks

You have a very complex model, the internal model binder is not going to know what to do with it because you have an IEnumerable of an object and you're editing(or hiding in this case) progress1 of the first Enrollment. You'll have to create a custom model binder.
What's happening on the view is it's storing this input tag of progress1 but when the controller gets it, it's not going to have any idea which Enrollment object it belongs to.
Try just passing in the first Enrollment to your view for editing.

Related

How to reset a form after submission using Controller As syntax

After adding an object via a form, I would like to reset the form so the user can add another. I've tried $setPristine() but can't figure out the syntax. As of now, I have just set the location path back to the same page, but that of course doesn't reset anything. Also wondering if $setPristine is the correct way. Here is my save function.
export class AddCarController {
public newCar;
public save() {
this.carService.save(this.newCar).then(() => { this.$location.path('/addCarPage') });
}
constructor(
private carService: MyApp.Services.CarService,
private $location: angular.ILocationService
) { }
}
In angular Your can create model in just in html and also in controller or on both places
If you have created Model in html
$scope.newCar = {} //this will empty whole ui model object
If you have created Model In Javascript Controller
$scope.newCar = new carModel() //this will create new car object with default values , if you have model in controller
Navigate Back And Forth - Not Recommended

Create List<T> from View and POST with form to MVC controller [duplicate]

This question already has an answer here:
Submit same Partial View called multiple times data to controller?
(1 answer)
Closed 6 years ago.
Background
I'm working on client's website project and I've used MVC to create the site. I've got EF working with a SQLDB to store data.
My WebApp is fitted with a back-office area for managing/adding/updating Items etc.
A particular page in question allows the user to add a new Item to the database by entering data and clicking 'Save' on the form.
The Item has some properties which all get added the DB nicely in a new row.
Item also contains a List<Appointment>s declared against it (I will serialize these and save them in the appropriate column).
The objects look like this (I have removed some properties for brevity):
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public List<Appointment> Appointments { get; set; }
// Plus some other properties
}
public class Appointment
{
public string Day { get; set; }
public string From { get; set; }
public string To { get; set; }
}
I've set out a ViewModel to use and I'm practising all that usual good-practice stuff. It looks like this:
public class AddItemViewModel
{
[Required(ErrorMessage = "Please enter an item name")]
[Display(Name="Item Name")]
public string Name { get; set; }
[Display(Name="Appointments")]
public List<Appointment> Appointments { get; set; }
...
// Other properties in here
...
}
I am using the Razor view engine.
I have everything working fine using the #Html.EditorFor(x => x.Property) helper; for all (but one - tell you in a moment) elements on the root object's (Item's) properties.
I can post the ViewModel to the controller and save a new Item, and all that jazz - so all is good there.
Problem
I have a List<Appointment> declared in my ViewModel.
How can I allow the user to add new Appointments to this List<Appointment> from within the page?
I want to assign these to the Item object to be created when the ViewModel is posted - I'll do this in my controller/services etc.
How can I achieve this?
Desired view
I need the user to be able to add Appointments from within the page - up to however many they like, really.
I would like there to be a list of the "created" Appointments in a list (ul or anything similar etc.) in an area on the page.
Underneath that list, I would like some textboxes that are relevant to the Day, From and To properties for the Appointment - with an 'Add' button, which appends it to the list (and clears the textboxes, ideally).
As a bonus, I would also like some sort of "×"-esque "delete" button next to each item, to remove it from the List<Appointment> and the view.
Here is a sample (drawn in MSPaint - old-school tools ftw!) of what I imagine the view to look like:
Things I have tried
I have been digging far and wide, but nothing has come to my rescue, as yet.
I have looked at many solutions to do with adding the objects in
JavaScript to the page - which does work. Only problem is I can't
pick them up in the object that gets posted to the controller.
I have also been pointed in the direct of using Ajax to create an Appointment object and insert it into the page, but this didn't seem to get picked up, either.
Your expertise, knowledge and wisdom is much appreciated.
To be able to add new Appointments input controls on the UI dynamically, on click of Add button, you can
[code below is from the top of my head, and more like pseudo-code]
a) use jQuery/js to clone a hidden div that you have pre-rendered on the view which contains all the relevant appointment controls.
e.g. this would be a pre-rendered div which will serve as a template
<div style="display:none;" id="appointment-template">
<input type=text>
</div>
And then the jQuery clone function on click of Add would be something like
var addNewAppointmentRowFromTemplate = function(){
$("#appointments-list-container").append($("#appointment-template").clone());
}
OR
b) Get the partial view from the server and append it to the form (again using jQuery $.get / js xhr)
$.get( "controller/templatDivActionMethodUrl", function( data ) {
$("#appointments-list-container").append( data );
});
To be able to send the model with List in Item that is accepted in ActionMethod's Item parameter, you can
a) Dynamically create a json object using jquery/javascript that has the same structure as the server side model (maintainability will be an issue when model is changed).
var appointmentsJson = [];
//foreach loop is a pseudo code, can be replaced with $.each()
foreach (var appointment in $("#appointments-list-container").children()){
appointmentsJson.push(new {'day' : $(appointment).children("#day").val(),'from' : $(appointment).children("#from").val(),'to' : $(appointment).children("#to").val()}) ;}
//and then post this json to actionMethod
$.post("serverController/Actionmethod",{appointments: appointmentsJson});
OR
b) Use the ModelBinder feature that comes with ASP.Net MVC. There's good amount of documentation/tutorials on this our there, a starting point:
https://docs.asp.net/en/latest/mvc/models/model-binding.html

How can I handle a dynamic viewModel in my controller?

In my controller, my create method takes a viewModel that inherits from DynamicObject. The reason is that I need static properties on the model, but I may also add things to it via JavaScript before I pass it to the controller.
So, I have:
public class MyViewModel: DynamicObject {
public string id {get;set;}
}
[HttpPost]
public IHttpActionResult Create(MyViewModel viewModel) {
// save to Db
}
From the JavaScript (using Angular), I've passed in this viewmodel:
var vm = { id: 1, name: "Jim"};
However, this does not work and is throwing me this error:
[{"PropertyName":null,"ErrorMessage":null,"ErrorCode":null,"AttemptedValue":null,"CustomState":null}]
I haven't found an example of what I'm trying to do, but I would think making an object dynamic would allow for this. What am I missing here?

Display a popup message or a view, depending on model validation

Imagine a webshop with several steps (views) during checkout. When the customer presses the 'next' button, the corresponding 'Action' method is called. In that method some validation is done, checking if the customer is free to proceed, or if there's something wrong with the order. Depending of the validation result, I'd like one of two things to happen:
A) The next view is generated and displayed.
B) The customer stays in the current page, but a modal dialog pops up, containing an error message.
I've tried several approaches found around the web, but nothing has worked so far. The last thing I tried was using AJAX, but I've actually got no clue if this is the right approach.
public ActionResult SomeAction()
{
if (ModelState.IsValid)
{
return View();
}
// If not valid
return errorMessage
}
$(document).ready(function () {
$("#btn").click(function () {
$.ajax({
url: "/Home/SomeAction",
async: true,
success: function (result) {
if (!result.success) {
alert(result.error);
}
// Else display view
}
});
Do you want to display a partial view in a modal pop up? If so, you could use the JQuery AJAX method as you've already done but combine it with a JQuery pop up/lightbox plugin to display the resulting HTML.
So, in your code where "/Home/SomeAction" is the PartialView for your pop up return a "PartialView" from your Controller and when you receive the HTML back in the JQuery "success:" event, maybe use (for example) "Jquery Colorbox" to display it (see here: http://www.jacklmoore.com/colorbox/)
It's hard to say how do you want to achieve such workflow but one thing that comes to my mind is returning values with ie. error in TempData store and doing proper redirection to next step or if validation fails to the same step.
For simple model like this:
public class Cart
{
[Required]
public string Item { get; set; }
[Required]
public string Quantity { get; set; }
}
Some code example for controller could look like this:
public class CartController : Controller
{
//
// GET: /Cart/
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(Cart model)
{
if (ModelState.IsValid)
return RedirectToAction("Step1");
TempData["Error"] = "Validation failed";
return View();
}
public ActionResult Step1()
{
// add more logic, steps etc
return View();
}
}
On view side:
#using (Html.BeginForm())
{
<fieldset>
<p>
<label for="Id">Item:</label>
#Html.TextBox("Item")
#Html.ValidationMessage("Item", "*")
#Html.Editor("Quantity")
</p>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
#if (TempData["Error"] != null)
{
<p>Please fix all errors!</p>
}
I would probably embeed this into view master page ie. only for those steps.
Edit: Changed some code added more real-world example :-)
Btw. keep in mind using such mechanism you'll get validation from MVC framework for free so you'll get your fields highlighted if needed. You can as well pass this model later on to other steps if needed. Overall here's nice starting guide for validation: http://www.asp.net/mvc/overview/older-versions/getting-started-with-aspnet-mvc4/adding-validation-to-the-model
If you would like to go AJAX way then you could use something like this:
$.post("#Url.Action("Index")", values, function(data) {
// add logic for loading next page or error
});
where values object you'll have to construct yourself with proper structure.
Good luck!
No need for ajax to check a form, why not just a few lines of javascript? Like "On click, if the 'e-mail' input field is empty, throw an alert" etc. There are many form validation plug-ins out there, like jQuery inline form validation, just google it.
See : http://www.w3schools.com/js/js_validation.asp

MVC Razor get values from controller in a view for javascript

I have to get values from a controller method in a view to write into some javascript. In web pages it is as simple as <%=functionName()%>. Is there a way to do this in MVC. I cannot use the model because the javascript has to be available on page load. Any insights appreciated.
Is there a way to do this in MVC
Yes, of course.
I cannot use the model because the javascript has to be available on page load.
Of course that you can use a view model, there's nothing that would prevent you from doing so. So you start by defining it:
public class MyViewModel
{
public string Foo { get; set; }
}
then have a controller action that will populate this view model and pass it the view:
public ActionResult Index()
{
var model = new MyViewModel();
model.Foo = "Hello World";
return View(model);
}
and finally have a strongly typed view to this view model in which:
#model MyViewModel
<script type="text/javascript">
$(function() {
var foo = #Html.Raw(Json.Encode(Model.Foo));
alert(foo);
});
</script>
But now let's suppose that you don't want to pollute your view with javascript but have it in a separate js file instead (which of course is the correct way).
You could embed the value somewhere in your DOM, for example using HTML5 data-* attributes:
<div id="foo" data-model="#Html.Raw(Html.AttributeEncode(Json.Encode(Model)))">
Click me to surprise you
</div>
and then in a separate javascript subscribe to the click event of this element and read the data-* attribute in which we have JSON serialized the entire view model:
$(function() {
$('#foo').click(function() {
var model = $(this).data('model');
alert(model.Foo);
});
});

Categories