I am newbie in ASP.NET MVC 3 and Razor. I want to make multiple forms in one view. All forms (in red rectangle) will show depend on what I choose in "Jenis Registrasi" dropdown (red arrow). Sometimes few forms need different model to load.
How the good implementation for this?
Sorry for unrepresentative title and question. Thanks for guiding me. :D
I kinda threw this together, but its similar to what I do in a lot of ajax situations. I'd certainly re factor out some of the logic. Maybe add blockUi into the JavaScript.
In your client you will have something like
$('RegistrasiDropDown').change(function () {
$.get('#Url.Action("GetExtraFormFields")', { id: $('#RegistrasiDropDown').val() }, function (data) {
if (data.success == false) {
//Handle Error
});
} else {
$('#ExtraFormSection').html(data);
}
})
});
In Your controller you will have something like (in c#), but the concept will be the same in VB
public ActionResult GetExtraFormFields(string id)
{
try
{
var registrationItem= GetRegistrationItemById(id);
if (condition1 == true) //Replace your own logic here.
{
var model = new ModelType1 {
Prop1 = "foo";
}
return PartialView("_PartialView1", model)
}
else if ()//// and so on
}
catch (Exception exception)
{
return return Json(new { success = false, message = exception.msg }, JsonRequestBehavior.AllowGet);
}
}
Related
I have an Syncfusion Asp.net grid performing CRUD operations.
public ActionResult Insert(CRUDModel<Source> newItem)
{
using (var context = new ImageStormEntities())
{
context.Sources.Add(newItem.Value);
context.SaveChanges();
}
return Json(newItem.Value);
}
cshtml:
#Html.EJS().Grid("DataGrid").DataSource(ds => ds.Json(ViewBag.datasource).UpdateUrl("/Management/Update").InsertUrl("/Management/Insert").RemoveUrl("/Management/Remove").Adaptor("RemoteSaveAdaptor")).Columns(col =>
{
col.Field("id").IsPrimaryKey(true).Visible(false).Add();
col.Field("ResourceGroup").HeaderText("Source VM Resource Group").Add();
col.Field("VMName").HeaderText("Source VM Name").Add();
col.Field("imageVersion").HeaderText("Image Version").Add();
}).ActionFailure("OnActionFailure").AllowTextWrap(true).TextWrapSettings(text => { text.WrapMode(Syncfusion.EJ2.Grids.WrapMode.Header); }).AllowPaging().FilterSettings(filter => { filter.Type(Syncfusion.EJ2.Grids.FilterType.Menu); }).EditSettings(edit => { edit.AllowAdding(true).AllowEditing(true).AllowDeleting(true).ShowDeleteConfirmDialog(true).Mode(Syncfusion.EJ2.Grids.EditMode.Dialog); }).Toolbar(new List<string>
() { "Add", "Edit", "Delete", "Update", "Cancel" }).Render()
On the page, I have:
<script>
function OnActionFailure(args) {
alert(args.error.status + " : " + args.error.statusText);
}
</script>
That returns a simple toast message when values break the db constraints, but values are empty.
I want to send usefull info to user.
I can catch the error, what do I return to make this work.
Also, I would rather the use got a Messagebox to discharge rather that a toast.
Changed your controllers code this will fix your issue
To
public ActionResult Insert(CRUDModel<Source> newItem)
{
using (var context = new ImageStormEntities())
{
if(newItem.Value! = null)
{
context.Sources.Add(newItem.Value);
context.SaveChanges();
}
else
{
throw new Exception("Empty values cannot be inserted");//Add custom
exception message
}
}
return Json(newItem.Value);
}
and in your js add lime this
<script>
function OnActionFailure(args) {
var errorMessage = args[0].error.responseText.split("Exception:")[1].split('<br>')[0]; //extract the message from args
alert(errorMessage);
//you will get exact error that you have returned from serverside in errorMessage
}
</script>
<div class="form-gridcontrol">
<label>Notes</label>
#Html.CustomTextArea(m => m.Notes)
</div>
In ASP.NET MVC , I have created a custom textarea and inputing/displaying data from the database using a Model.Above is the code where you can see the Notes are getting assigned to #Html.CustomTextArea.
I have a situation where , I need to display a text "Not Applicable" if there is no value in "m.Notes"
How I should right the logic in the above code? Please guide.
There are multiple possible ways for this. One of the way is that you can populate in the controller action from where it is loaded like:
public ActionResult YourActionMethod()
{
............
............
if(String.IsNullOrEmpty(model.Notes))
model.Notes = "Not Applicable";
return View(model);
}
Another way can be to introdcue the backing field on your property and write in it's getter:
private String _notes;
public String Notes
{
get
{
return String.IsNullOrEmpty(_notes) ? "Not Applicable" : _notes;
}
set
{
_notes = value;
}
}
You can try this:
#if (Model.Notes != null)
{
#Html.CustomTextArea(m => m.Notes)
}
else
{
#Html.CustomTextArea( m => m.Notes, new { #Value = "Not Applicable"})
}
edit:this is not working with textarea
else
{
#Html.CustomTextArea(m => m.Notes, new {id="mytextarea"})
<script>
$("#mytextarea").text("Not Applicable")
</script>
}
I got a trick for you :)
I am working on my first ASP.NET MVC 3 project. For some of the fields on an edit page I want the user to be able to either choose a value from previously-entered values or enter a new one, so I'm using the jQuery autocomplete to accomplish this. That part seems to work just fine. Now, for some fields the user can choose to enter a value or not and if they do enter one, I want to validate it against some rules, so I created my own ValidationAttribute.
The validation piece will definitely check the given value against the rules and return the correct boolean value for the IsValid call. Great.
The first problem that I'm having is that if my validator's IsValid returns false, it displays the error message I've specified but if I enter something which is valid, the TextBox clears it's error background color but the error message does not clear. This happens in either FF or IE8.
The second problem is that for FireFox, my autocomplete values will display again when I edit the text in the textbox but in IE 8, once the error condition exists, my autocomplete stops working. Occasionally, if I enter a value which I know is in the autocomplete list, it will show up but it seems a bit flaky.
I may just be doing this validation thing wrong. Here's the relevant code I use. I'd be quite interested in any guidance one can give about either of these issues. The attribute is a test one but it exhibits the behavior on my page.
My ValidationAttribute:
public class MyAttribute : ValidationAttribute
{
...
public override bool IsValid(object value)
{
if (value == null)
{
return true;
}
var stringValue = Convert.ToString(value);
if (stringValue.Length == 0)
{
return true;
}
if (stringValue.Trim().Length == 0)
{
return false;
}
return true;
}
...
}
My autocomplete code:
$("#toppingid").autocomplete({
source: function (request, response) {
$.ajax({
url: '#Url.Action("AvailableToppings", "IceCream")', type: "POST", dataType: "json",
data: { query: request.term },
success: function (data) {
response($.map(data, function (item) {
return { label: item, value: item };
}))
}
})
},
minLength: 1
});
My controller action:
public JsonResult AvailableToppings()
{
// I actually retrieve this differently, but you get the idea
List<string> all = new List<string>() { "BerryCrush", "Caramel", "Fudge" };
return Json(all, JsonRequestBehavior.AllowGet);
}
My view snippet:
#Html.TextBoxFor(model => model.Topping, new { #id = "toppingid" })
#Html.ValidationMessageFor(model => model.Topping)
My viewmodel snippet:
[DisplayName("Topping:")]
[MyAttribute(ErrorMessage = "Can't be all blanks!")]
public string Topping { get; set; }
In my post action, I have logic something like this:
[HttpPost]
public ActionResult Create(IceCreamCreateEditViewModel viewModel)
{
if (ModelState.IsValid)
{
// stuff happens here which isn't germane
return RedirectToAction("Index");
}
// redisplay the view to the user
return Create();
}
I think that's all the relevant pieces of code. Thanks for any guidance you can provide.
Concerning your first question, it looks like the autocomplete plugin removes the input-validation-error class from the textbox when a selection is made. Because of this the textbox clears its background. One way to workaround this is to subscribe for the select event and reapply this class if there is an error (by checking whether the error label is displayed):
$("#toppingid").autocomplete({
source: function (request, response) {
...
},
select: function (event, ui) {
var topping = $('#toppingid');
// find the corresponding error label
var errorLabel = $('span[data-valmsg-for="' + topping.attr('name') + '"]');
if (errorLabel.is(':visible')) {
// if the error label is visible reapply the CSS class
// to the textbox
topping.addClass('input-validation-error');
}
},
minLength: 1
});
As far as your second question is concerned, unfortunately I am unable to reproduce it. The autocomplete doesn't stop working in IE8 if there is a validation error.
I’m trying to implement an order entry form using ASP.NET MVC but facing a lot of difficulties. All the samples that I found are related to viewing master detail forms, and none for adding or editing.
Suppose I have two tables: Order and OrderLines that are related to each others with one-to-many relationship. In the main view I had a “New” button when clicked it should show a new order view composed of the order fields, a grid that shows the order lines, and a “Save” button that when clicked will persist the whole order along with its lines into a database. The grid should have three buttons: “Add Line”, “Edit Line”, and “Delete Line”. When the “Add Line” is clicked a new view should be shown that allows the user to add the line to the order view grid lines –at this stage the database is not affected-. When the user clicks “Edit Line” a view will be shown that allows the user to edit the selected line and when done update the order grid lines.
The most difficult problems are:
How to pass the order and its lines collection between the order view and the order line views?
How to update the order view based on changes in the order line view?
And how to persist changes between views without the database being involved?
Is there a concrete example that shows how to implement this using MVC?
Your help and feedback is appreciated.
Pleas have a look at my blog post on creating master detail form in asp.net mvc. it also contains demo project that you can download
Unlike WebForms, ASP.NET MVC does not try to hide the stateless nature of HTTP. To work with a complex object across multiple forms you have a couple of options:
Save the object on the server with each change so that the updated object is available using only an id
Use jquery to populate the order line form and save details to the main form
I usually go with the client side option myself, with the main form having hidden fields for the data that will be edited in the subform. You may find the server side option easier though - if you really don't want to involve the database you can keep your partially updated object in the session.
Step 1: Create view model
public class OrderVM
{
public string OrderNo { get; set; }
public DateTime OrderDate { get; set; }
public string Description { get; set; }
public List<OrderDetail> OrderDetails {get;set;}
}
Step 2: Add javascript for add order lines
<script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
$(function () {
$('#orderDate').datepicker({
dateFormat : 'mm-dd-yy'
});
});
$(document).ready(function () {
var orderItems = [];
//Add button click function
$('#add').click(function () {
//Check validation of order item
var isValidItem = true;
if ($('#itemName').val().trim() == '') {
isValidItem = false;
$('#itemName').siblings('span.error').css('visibility', 'visible');
}
else {
$('#itemName').siblings('span.error').css('visibility', 'hidden');
}
if (!($('#quantity').val().trim() != '' && !isNaN($('#quantity').val().trim()))) {
isValidItem = false;
$('#quantity').siblings('span.error').css('visibility', 'visible');
}
else {
$('#quantity').siblings('span.error').css('visibility', 'hidden');
}
if (!($('#rate').val().trim() != '' && !isNaN($('#rate').val().trim()))) {
isValidItem = false;
$('#rate').siblings('span.error').css('visibility', 'visible');
}
else {
$('#rate').siblings('span.error').css('visibility', 'hidden');
}
//Add item to list if valid
if (isValidItem) {
orderItems.push({
ItemName: $('#itemName').val().trim(),
Quantity: parseInt($('#quantity').val().trim()),
Rate: parseFloat($('#rate').val().trim()),
TotalAmount: parseInt($('#quantity').val().trim()) * parseFloat($('#rate').val().trim())
});
//Clear fields
$('#itemName').val('').focus();
$('#quantity,#rate').val('');
}
//populate order items
GeneratedItemsTable();
});
//Save button click function
$('#submit').click(function () {
//validation of order
var isAllValid = true;
if (orderItems.length == 0) {
$('#orderItems').html('<span style="color:red;">Please add order items</span>');
isAllValid = false;
}
if ($('#orderNo').val().trim() == '') {
$('#orderNo').siblings('span.error').css('visibility', 'visible');
isAllValid = false;
}
else {
$('#orderNo').siblings('span.error').css('visibility', 'hidden');
}
if ($('#orderDate').val().trim() == '') {
$('#orderDate').siblings('span.error').css('visibility', 'visible');
isAllValid = false;
}
else {
$('#orderDate').siblings('span.error').css('visibility', 'hidden');
}
//Save if valid
if (isAllValid) {
var data = {
OrderNo: $('#orderNo').val().trim(),
OrderDate: $('#orderDate').val().trim(),
//Sorry forgot to add Description Field
Description : $('#description').val().trim(),
OrderDetails : orderItems
}
$(this).val('Please wait...');
$.ajax({
url: '/Home/SaveOrder',
type: "POST",
data: JSON.stringify(data),
dataType: "JSON",
contentType: "application/json",
success: function (d) {
//check is successfully save to database
if (d.status == true) {
//will send status from server side
alert('Successfully done.');
//clear form
orderItems = [];
$('#orderNo').val('');
$('#orderDate').val('');
$('#orderItems').empty();
}
else {
alert('Failed');
}
$('#submit').val('Save');
},
error: function () {
alert('Error. Please try again.');
$('#submit').val('Save');
}
});
}
});
//function for show added items in table
function GeneratedItemsTable() {
if (orderItems.length > 0) {
var $table = $('<table/>');
$table.append('<thead><tr><th>Item</th><th>Quantity</th><th>Rate</th><th>Total</th></tr></thead>');
var $tbody = $('<tbody/>');
$.each(orderItems, function (i, val) {
var $row = $('<tr/>');
$row.append($('<td/>').html(val.ItemName));
$row.append($('<td/>').html(val.Quantity));
$row.append($('<td/>').html(val.Rate));
$row.append($('<td/>').html(val.TotalAmount));
$tbody.append($row);
});
$table.append($tbody);
$('#orderItems').html($table);
}
}
});
</script>
Step 3: Create an action for save data
[HttpPost]
public JsonResult SaveOrder(OrderVM O)
{
bool status = false;
if (ModelState.IsValid)
{
using (MyDatabaseEntities dc = new MyDatabaseEntities())
{
Order order = new Order { OrderNo = O.OrderNo, OrderDate = O.OrderDate, Description = O.Description };
foreach (var i in O.OrderDetails)
{
//
// i.TotalAmount =
order.OrderDetails.Add(i);
}
dc.Orders.Add(order);
dc.SaveChanges();
status = true;
}
}
else
{
status = false;
}
return new JsonResult { Data = new { status = status} };
}
you can download source code and video tutorial
You could try Telericks free MVC grid control...
http://demos.telerik.com/aspnet-mvc/grid/hierarchyserverside
Just off the top of my head (a kind of brain dump)...
You could have a main grid part of the form. This would be full view loaded from an action (either with an order number or not depending on loading an existing one or not).
When clicking an event (new or edit) this could open a partial view in a "lightbox" style. This would then pass back a json object back to the main form.
The passed json object would then be rendered using templating to the bottom of the table (for a new one) or update an existing record. This could also be saved back to the server in the same ajax call. Or just update the client side and need the user to click a save button.
An isDirty flag will be needed so any changes set it to true and the when the browser tries to leave or close etc. then you can prompt the user to save or not.
Hope this helps.
edit
Not tried this but could be interesting with the none db aspect of your question click
Step 3: Create an action for save data.
[HttpPost]
public JsonResult SaveOrder(OrderVM O)
{
bool status = false;
if (ModelState.IsValid)
{
using (ManageMobileStoreWebContext dc = new ManageMobileStoreWebContext())
{
//Random rnd = new Random();
//OrderID = rnd.Next(),
Order order = new Order { OrderNo = O.OrderNo, OrderDate = O.OrderDate, Description = O.Description };
foreach (var i in O.OrderDetails)
{
if(order.OrderDetails == null)
{
order.OrderDetails = new List<OrderDetail>();
}
// i.TotalAmount =
order.OrderDetails.Add(i);
//dc.OrderDetails.Add(i);
}
dc.Orders.Add(order);
dc.SaveChanges();
status = true;
}
}
else
{
status = false;
}
return new JsonResult { Data = new { status = status } };
}
Doing validation in my binder, I'm wondering if there's a need to check the return value. In Option 1 below, is there ever going to be a difference in case 1 and case 2? It doesn't seem possible that TryUpdateModel would return true, but ModelState.IsValid is false.
Option 1:
if (TryUpdateModel(editItem, new string[] { "Field" }))
{
if (ModelState.IsValid)
{
} else {
// Invalid model case 1
}
} else {
// Invalid model case 2
}
Option 2:
TryUpdateModel(editItem, new string[] { "Field" }))
if (ModelState.IsValid)
{
} else {
// only one invalid model case
}
The last line of the TryUpdateModel source code is:
return ModelState.IsValid;
...which pretty much answers your question. :)