I have view with tasks. Each task has an #Ajax.Action link to add this task to check list.
My view:
#foreach (var item in Model.Tasks) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.TaskText)
</td>
<td>
#Html.DisplayFor(modelItem => item.TillDate)
</td>
<td>
#Html.EnumDropDownListFor(modelItem => item.State, new { id=item.Id, #class="state"})
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id = item.Id })
#Ajax.ActionLink("Add To check list", "AddToCheckList", new { id = item.Id }, new AjaxOptions { UpdateTargetId = "CheckListPartial" });
</td>
</tr>
}
My controller action:
public PartialViewResult AddToCheckList(int id)
{
context.AddTaskToCheckList(id);
return PartialView("_LoadCheckList", context.CheckList);
}
And CheckList class:
public class CheckList
{
public string Name { get; set; }
public List<Task> Tasks { get; set; } = new List<Models.Task>();
}
Now adding works, but I have a problem: I can add one task to check list several times.
How can I validate if check list contains task and show error message?
UPD:
I've make this with my controller. Validation works, but message is not shown.
public PartialViewResult AddToCheckList(int id)
{
if(context.CheckList.Tasks.Exists(t=>t.Id==id))
ModelState.AddModelError("CheckList", "Check list already contains this task.");
if (ModelState.IsValid)
{
context.AddTaskToCheckList(id);
return PartialView("_LoadCheckList", context.CheckList);
}
return PartialView();
}
Also add this string to my View:
#Html.ValidationMessageFor(model => model.CheckList)
UPD2:
My PartialView:
#model TaskTracker.Models.CheckList
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
Текст задания
</th>
<th>
Дата выполнения
</th>
<th></th>
</tr>
#foreach (var task in Model.Tasks)
{
<tr>
<td>
#Html.DisplayFor(modelItem=>task.Value.TaskText)
</td>
<td>
#Html.DisplayFor(modelItem => task.Value.TillDate)
</td>
</tr>
}
</table>
You cannot display a ModelState error from a partial in the main view's #Html.ValidationMessageFor() - that is razor code and is parsed on the server before its sent to the view, so will display ModelState errors from the main views model only.
One option would include moving the ValidationMessageFor() to the partial view with the only drawback being the ability to position the element.
However, returning the whole table of added tasks is unnecessary when you already know all the values in the client to append the new row to the table.
Change your code to use the $.ajax methods and in the method return a JsonResult indicating success or otherwise
Html
Add To check list
Script
var url = '#Url.Action("AddToCheckList")';
var table = $('#CheckListPartial table);
$('.add').click(function() {
var id = $(this).data('id');
var cells = $(this).closest('tr').find('td');
var text = cells.eq(0).text();
var date = cells.eq(1).text();
$.post(url, { id: id }, function(result) {
if (result) {
// create and append a new row
var row = $('<tr></tr>');
row.append($(<td></td>).text(text));
row.append($(<td></td>).text(date));
table.append(row);
} else {
// display your error
}
}).fail(function (result) {
// display your error
});
})
and the controller method would be
public JsonResult AddToCheckList(int id)
{
if(context.CheckList.Tasks.Exists(t => t.Id == id))
{
return Json(null); indicate error - show a hidden element containing a message
}
context.AddTaskToCheckList(id);
return Json(true); // indicate success
}
You should also consider removing the link if successful, so the user does not accidentally click it again.
A third alternative would be to generate a checklistbox of all tasks and allow the user to select or unselect items and the post a form and save all taks inone action. Refer this answer for an example of the approach.
You can make your Tasks to be an HashSet and check in the AddTaskToCheckList method if it is already there. In that case checking is O(1)
public void AddTaskToCheckList(int id)
{
// Find task
var task = this.Tasks.Find(id);
if(!this.CheckList.Contains(task))
{
this.CheckList.Add(task);
}
else
{
// Show error message
}
}
Related
I have a list of Ajax links that are created by the code below.
It displays a name with a link "remove" next to it.
When clicked, I want the name/link removed from the list.
Problem: I can only click and update the page once.
After clicking one of the Ajax links which removes it from the list,
it won't let me click another link. I click and it doesn't do anything.
If I want to click and remove another link, I have to refresh the page.
I don't really know javascript but I'm guessing it has to do with the name or id of the element being the same.
<table cellspacing="4" cellpadding="4" border="1" width="750">
#foreach (var item2 in Model.PeopleCheckBoxes)
{
if (ctr == 3)
{
#:<tr>
}
<td><img src="icon.jpg"> </td>
<td valign="middle" align="left">
#item2.Username
#item2.Username
#Ajax.ActionLink("remove", "RemoveMemberFromEvent", "GiftList",
new { eventID = #Model.catID, peopleID = #item2.Id },
new AjaxOptions
{
UpdateTargetId = "peopleTable",
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST"
}, new { #class = "details2", title = "Remove person" })
</td>
if (ctr == 5)
{
#:</tr>
ctr = 2;
}
ctr++;
} </table>
[HttpPost]
public PartialViewResult RemoveMemberFromEvent(int eventID, int peopleID)
{
// removes item :
var aEvent = (from p in _EventMemberRepository.Table
// ............
_giftListService.DeletePersonOnGiftPage(aEvent);
// then update the database :
var members2 = from c in _custRepository.Table
var vModel = new GiftListItems();
//...........
return PartialView("_RemoveMemberFromEvent", vModel);
}
Is this a common problem?
I have table 'Artifact' with columns
ID File Type CONTENT
1 Experience Certifacte Some data in varbinary type
In UI, i have one row in html table which was one column was'drop down list' file type contains items of file types like Experience certificate, Appreciation Certificate etc., and one 'upload file' control..
dropdownlist <input type="file"/>
dropdownlist <input type="file/>
etc.,......................
.............................
And one button to add more files , which adds same above row.
Now I want to Insert multiple files content with file type.
I can Insert multiple files content in 'CONTENT' column in Artifact table like
[HttpPost]
public ActionResult FileUpload(IEnumerable<HttpPostedFileBase> file_Uploader)
{
if (file_Uploader != null && file_Uploader.ContentLength > 0)
{
foreach(var item in file_Uploader)
{
var content = new byte[item .ContentLength];
item .InputStream.Read(content, 0, file_Uploader.ContentLength);
var document= reslandEntities.ARTIFACT.Where(m => m.ID == 1).SingleOrDefault();
document.CONTENT = item.ToArray();
document.filetype= ?? // How to save file type with same file
reslandEntities.SaveChanges();
}
}
}
But the Problem is , How to save files content with drop down list file types,i mean, how to get the file types and save with same row with file content in database table?
Anybody have idea? Please help me?
Try to Pass Model to HttpPost Method Like this
[HttpPost]
public ActionResult FileUpload(IEnumerable<HttpPostedFileBase> file_Uploader, ClassName Model)
{
if (file_Uploader != null && file_Uploader.ContentLength > 0)
{
foreach(var item in file_Uploader)
{
var content = new byte[item .ContentLength];
item .InputStream.Read(content, 0, file_Uploader.ContentLength);
var document= reslandEntities.ARTIFACT.Where(m => m.ID == 1).SingleOrDefault();
document.CONTENT = item.ToArray();
document.filetype=Model.dropdownlistID;
}
reslandEntities.SaveChanges();
}
}
}
I got it.
Just Add Property in the model as
public class EmployeeModel
{
public int ArtifactId { get; set; } //For Dropdown list
public HttpPostedFileBase FileContent { get; set; } // For uploading file.
}
in View
#using (Html.BeginForm("GetFiles", "Home", FormMethod.Post, new { enctype = "multipart/form-data", #class="form" }))
{
<table>
<tr>
<td>#Html.DropDownListFor(m => m.ArtifactId, Model.getDocumenttype())</td>
<td>#Html.TextBoxFor(m => m.FileContent, new { #type = "file" })</td>
</tr>
<tr>
<td><input type="button" value="submit" id="btnSave" /></td>
</tr>
</table>
}
and for Controller Acton method
[HttpPost]
public ActionResult GetFiles(EmployeeModel model)
{
return View();
}
I have got the solution as above.
But, is it possible to send model using jquery like
var model = $('.form').serialize();
i have tried , but not getting.
I'm trying to recreate MVC Music Store but I've faced an error which I can't handle: "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection". The error is related to the following line in the ShoppingCart Index view:
#Html.ActionLink(item.Product.Title, "Details", "Store", new { id = item.ProductId }, null)
ShoppingCartController:
public class ShoppingCartController : Controller
{
OnlineStoreDbContext db = new OnlineStoreDbContext();
//
// GET: /ShoppingCart/
public ActionResult Index()
{
var cart = ShoppingCart.GetCart(this.HttpContext);
var viewModel = new ShoppingCartViewModel
{
CartItems = cart.GetCartItems(),
CartTotal = cart.GetTotal()
};
return View(viewModel);
}
//
// GET: /Store/AddToCart/5
public ActionResult AddToCart(int id)
{
// Retrieve the album from the database
var addedProduct = db.Products.Single(product => product.ProductId == id);
// Add it to the shopping cart
var cart = ShoppingCart.GetCart(this.HttpContext);
cart.AddToCart(addedProduct);
// Go back to the main store page for more shopping
return RedirectToAction("Index");
}
[HttpPost]
public ActionResult RemoveFromCart(int id)
{
// Remove the item from the cart
var cart = ShoppingCart.GetCart(this.HttpContext);
// Get the name of the album to display confirmation
string productName = db.Carts.Single(item => item.RecordId == id).Product.Title;
// Remove from cart
int itemCount = cart.RemoveFromCart(id);
// Display the confirmation message
var results = new ShoppingCartRemoveViewModel
{
Message = Server.HtmlEncode(productName) + " has been removed from your shopping cart.",
CartTotal = cart.GetTotal(),
CartCount = cart.GetCount(),
ItemCount = itemCount,
DeleteId = id
};
return Json(results);
}
//
// GET: /ShoppingCart/CartSummary
[ChildActionOnly]
public ActionResult CartSummary()
{
var cart = ShoppingCart.GetCart(this.HttpContext);
ViewData["CartCount"] = cart.GetCount();
return PartialView("CartSummary");
}
}
ShoppingCart Index View:
#model OnlineStoreMVC.UI.ViewModels.ShoppingCartViewModel
#{
ViewBag.Title = "Shopping Cart";
}
<script src="/Scripts/jquery-1.4.4.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
// Document.ready -> link up remove event handler
$(".RemoveLink").click(function () {
// Get the id from the link
var recordToDelete = $(this).attr("data-id");
if (recordToDelete != '') {
// Perform the ajax post
$.post("/ShoppingCart/RemoveFromCart", { "id": recordToDelete },
function (data) {
// Successful requests get here
// Update the page elements
if (data.ItemCount == 0) {
$('#row-' + data.DeleteId).fadeOut('slow');
} else {
$('#item-count-' + data.DeleteId).text(data.ItemCount);
}
$('#cart-total').text(data.CartTotal);
$('#update-message').text(data.Message);
$('#cart-status').text('Cart (' + data.CartCount + ')');
});
}
});
});
function handleUpdate() {
// Load and deserialize the returned JSON data
var json = context.get_data();
var data = Sys.Serialization.JavaScriptSerializer.deserialize(json);
// Update the page elements
if (data.ItemCount == 0) {
$('#row-' + data.DeleteId).fadeOut('slow');
} else {
$('#item-count-' + data.DeleteId).text(data.ItemCount);
}
$('#cart-total').text(data.CartTotal);
$('#update-message').text(data.Message);
$('#cart-status').text('Cart (' + data.CartCount + ')');
}
</script>
<h3>
<em>Review</em> your cart:
</h3>
<p class="button">
#Html.ActionLink("Checkout", "AddressAndPayment", "Checkout")
</p>
<div id="update-message">
</div>
<table>
<tr>
<th>
Movie Title
</th>
<th>
Price (each)
</th>
<th>
Quantity
</th>
<th></th>
</tr>
#foreach (var item in Model.CartItems)
{
<tr id="row-#item.RecordId">
<td>
#Html.ActionLink(item.Product.Title, "Details", "Store", new { id = item.ProductId }, null)
</td>
<td>
#item.Product.Price
</td>
<td id="item-count-#item.RecordId">
#item.Count
</td>
<td>
Remove from cart
</td>
</tr>
}
<tr>
<td>
Total
</td>
<td></td>
<td></td>
<td id="cart-total">
#Model.CartTotal
</td>
</tr>
</table>
Edit:
public class ShoppingCart
{
string ShoppingCartId { get; set; }
public const string CartSessionKey = "CartId";
public static ShoppingCart GetCart(HttpContextBase context)
{
var cart = new ShoppingCart();
cart.ShoppingCartId = cart.GetCartId(context);
return cart;
}
public static ShoppingCart GetCart(Controller controller)
{
return GetCart(controller.HttpContext);
}
}
This error means pretty much exactly what it says. Somewhere, you've disposed of your application context instance, and then tried to make another query with it. This happens frequently when you're misusing lazy loading and perhaps being a little over-eager with disposing your context (wrapping it in a using block) for example.
I'm not seeing any likely culprit in the code you posted but ShoppingCart.GetCart looks like a prime candidate for further investigation.
Basically, you want to do two things. First, you want to get a handle on what's actually being utilized by your action/views. If you're accessing related items to a cart, or something, then you should consider eager-loading those with Include. Second, you need to ensure that you context sticks around throughout the period it needs to be used. If you're lazy loading items in your view, and you intend to do that, then you'll need to make it last the life of the request: just don't use using and rely on the Dispose method of the controller for disposing of it.
That's generalized advice though. Since I believe your issue lies in this GetCart method, if that's actually the case, then you need to approach it slightly differently. First, you can ensure that GetCart returns a fully-baked result, that will not allow further querying. In cases where you're returning an enumerable, you can normally clear this up just by using .AsList(). Since this is likely just returning a single object, you would need to map it to a DTO or something: anything other than the actual entity class.
Your other option is to inject your context to the method. Normally, injecting into the class would be preferred, but it looks like you're dealing with a static class here. Basically, you want to allow your controller to create and dispose the context and then just pass it as a parameter to your GetCart method, so that it uses the context in the controller instead of creating its own. Depending on your applications needs, you might actually consider implementing some dependency injection framework to handle tasks like these.
The website I am coding is working fine except for the remove from cart option. I don't get any error in the code but when I click the link for remove from cart the code does nothing. It seems as if the page just refreshes. I am coding the system in C#.net MVC 4. Please can someone help me.
This is part of the ShoppingCartController.cs
[HttpPost]
public ActionResult RemoveFromCart(int id)
{
// Remove the item from the cart
var cart = ShoppingCart.GetCart(this.HttpContext);
// Get the name of the album to display confirmation
string albumName = storeDB.Carts
.Single(item => item.RecordId == id).Album.Title;
// Remove from cart
int itemCount = cart.RemoveFromCart(id);
// Display the confirmation message
var results = new ShoppingCartRemoveViewModel
{
Message = Server.HtmlEncode(albumName) +
" has been removed from your shopping cart.",
CartTotal = cart.GetTotal(),
CartCount = cart.GetCount(),
ItemCount = itemCount,
DeleteId = id
};
return Json(results);
}
This is part of the ShoppingCart model.
public int RemoveFromCart(int id)
{
// Get the cart
var cartItem = storeDB.Carts.Single(
cart => cart.CartId == ShoppingCartId
&& cart.RecordId == id);
int itemCount = 0;
if (cartItem != null)
{
if (cartItem.Count > 1)
{
cartItem.Count--;
itemCount = cartItem.Count;
}
else
{
storeDB.Carts.Remove(cartItem);
}
// Save changes
storeDB.SaveChanges();
}
return itemCount;
}
Lastly this is the ShoppingCart/Index view.
#foreach (var item in Model.CartItems)
{
<tr id="row-#item.RecordId">
<td>
#Html.ActionLink(item.Album.Title, "Details", "Shop", new { id = item.AlbumId }, null)
</td>
<td>
#item.Album.Price
</td>
<td id="item-count-#item.RecordId">
#item.Count
</td>
<td>
Remove from cart
</td>
</tr>
}
Unless you've got some javascript somewhere that is handling the onclick of your "Remove from cart" link or adding an href to it, your current href of "#" will 'seem to refresh the page' exactly as you've described. Consider changing that anchor tag to an ActionLink like your Details link but that points to your RemoveFromCart action method.
You need to use Html.ActionLink instead of a tag, just like you do for Shop/Details action. Example (assuming that your RemoveFromCart action is in ShopController and you want to delete an item with id = item.AlbumId):
#Html.ActionLink(
"Remove from cart",
"RemoveFromCart",
"Shop",
new { id = item.AlbumId },
new { #class = "RemoveFromCart"})
This way full page refresh will be performed and you will need to return View from your action. To make only partial refresh you will need to use Ajax.ActionLink helper.
i use mvc 4 and try to give my web application more dynamic. At the moment i try to split some of the views in partial views so the code gets better readable and i can better re-use parts of the application.
So now this leads me to a problem. I have a view similar to this one:
<h1>Manage department</h1>
<div id="EmployeesManagement">#Html.Action("OpenEmployeesManagement")</div>
<div id="DepartmentManagement">#Html.Action("OpenDepartmentManagement")</div>
<div id="DepartmentTumorModels">#Html.Action("OpenDepartmentModels")</div>
Each of those are partial views which get called from the controller like:
public PartialViewResult OpenDepartmentModels()
{
ViewBag.ChangeVisibility = -1;
HoDManagementModel hoDManagementModel = new HoDManagementModel { UserWithRoleModelList = azaraUserManagement.GetAllEmployesOfHoD(user.getUserId()), OrganisationUnits = azaraUserManagement.GetAllOrganisationUnitsFromHoD(user.getUserId()) };
List<ModelWithOrganisationUnit> Models = ModelManagement.SearchModelsOfDepartment(hoDManagementModel.OrganisationUnits);
return PartialView("DepartmentModels", Models);
}
Now to my problem. I have a partial view like this one:
#model List<Modelle.Models.BusinessTierObjects.Models.ModelWithOrganisationUnit>
<fieldset>
<legend>Manage the models of your department</legend>
<table class="tablesorter">
<thead>
<tr>
<th>ID</th>
<th>Name </th>
<th>Department </th>
<th>Visibility</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in #Model)
{
<tr>
<td>#item.ModelId</td>
<td>#Html.ActionLink((String)item.ModelName, "Details", "Details", new { id = item.ModelId }, null)</td>
<td>#item.OrganisationUnitName</td>
#if (ViewBag.ChangeVisibility == item.ModelId)
{
<td><select name="ChangeVisibility" id="ChangeVisibility">
<option value="Department" onclick="location.href='#Url.Action("ChangeVisibility", "ManageDepartment", new {tumorModelId = item.ModelId, Visibility = 0})'">Department</option>
option value="Coop" onclick="location.href='#Url.Action("ChangeVisibility", "ManageDepartment", new { ModelId = item.ModelId, Visibility = 2 })'">Coop</option>
<option value="WWW" onclick="location.href='#Url.Action("ChangeVisibility", "ManageDepartment", new { ModelId = item.ModelId, Visibility = 3 })'">WWW</option>
</select></td>
}
else{
switch ((byte)item.Visibility)
{
case 0: <td>Department</td>; break;
case 2: <td>Coop</td>; break;
case 3: <td>WWW</td>; break;
}
}
<td><button name="button" class="button" onclick="location.href='#Url.Action("RequestChangeVisibility", "ManageDepartment", new { change = #item.ModelId })'">Change Visibility</button>
</td>
</tr>}
</fieldset>
and if i click on the last button just the partial view should be reloaded. But instead the application only show me the partial view without any layout in the browser. What have i done wrong or isn´t it possible to solve my problem?
The controller action from the button is:
public PartialViewResult RequestChangeVisibility(int change)
{
ViewBag.ChangeVisibility = change;
HoDManagementModel hoDManagementModel = new HoDManagementModel { UserWithRoleModelList = azaraUserManagement.GetAllEmployesOfHoD(user.getUserId()), OrganisationUnits = azaraUserManagement.GetAllOrganisationUnitsFromHoD(user.getUserId()) };
List<ModelWithOrganisationUnit> Models = ModelManagement.SearchModelsOfDepartment(hoDManagementModel.OrganisationUnits);
return PartialView("DepartmentModels", Models);
}
The reason this happens is because you are using:
onclick="location.href='#Url.Action("RequestChangeVisibility", "ManageDepartment", new { change = #item.ModelId })'"
This will refresh the whole page to the partial view result, so you only see that partial view.
what you need to do is to do an ajax call to that controller, so you should use:
<td><button name="button" class="button" onclick="RequestChangeVisibilityAjaxCall(#item.ModelId)">Change Visibility</button>
then add the following javascript to the page:
<script>
function RequestChangeVisibilityAjaxCall(change) {
$.ajax({
url: "../ManageDepartment/RequestChangeVisibility?Change=" + change,
type: 'GET',
success: function (data) {
$('#DepartmentManagement').html(data);
}
});
}
</script>
The line:
$('#DepartmentManagement').html(data);
will use the result of the ajax call (data) to populate the div with ID DepartmentManagement - I wasnt sure where you wanted that partial view to go, so just change the ID to what ever you need it to be.
Also, the line:
url: "../ManageDepartment/RequestChangeVisibility?Change=" + change,
is the url of the controller, I think i guessed it right, but you should change this to the correct address where needed.
I hope this helps.
Martyn
[edit] a good tutorial here:
also, do some googling for "mvc jquery ajax" that should also help you understand it. Much better than I can explain it! :)