The scenario is that Im basically trying to create 4 dropdowns, that repopulate when one is changed. Aka the filters are cascading, each other.
So I decided to put it in an Ajax call. And basically, it takes the params, decides what select lists should be returned. Then replaces the old 4 dropdowns with new dropdowns. (replaces current partial with new partial)
Except, for some reason I am getting it calling the controller once.. then twice.. then 4 times.. etc. As if the old ones are not being removed/ replaced. just hidden?..
Visually, I see what Id expect. THe dropdowns change selection options.
below is the code. (p.s. sorry if some variable names are typos, they have been changed for posting on here)
Controller:
public class Filter
{
public IEnumerable<SelectListItem> List1;
public IEnumerable<SelectListItem> List2;
public IEnumerable<SelectListItem> List3;
public IEnumerable<SelectListItem> List4;
}
public ActionResult GlobalFilter(String l1, String l2, String l3, String l4)
{
Filter filter = new Filter();
filter.List1 = ...selectList
filter.List2 = ...selectList
filter.List3 = ...selectList
filter.List4 = ...selectList
return PartialView(filter);
}
view:
<div id="filterPartial">
#Html.Action("GlobalFilter", "Header")
</div>
partial view:
#model ns.Controllers.HeaderController.Filter
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
#using (Ajax.BeginForm("GlobalFilter", "Header", new AjaxOptions { UpdateTargetId = "filterPartial" }))
{
#Html.DropDownList("l1", Model.List1, new { })
#Html.DropDownList("l2", Model.List2, new { })
#Html.DropDownList("l3", Model.List3, new { })
#Html.DropDownList("l4", Model.List4, new { })
}
<script type="text/javascript">
$('#l1').change(function () {
$(this).parents('form').submit();
});
</script>
Move this outside your partial:
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
I also moved the change script outside the partial as well and changed it to handle dynamic content like so:
<script type="text/javascript">
$('#filterPartial').on('change', '#l1', function () {
$(this).closest('form').submit();
});
</script>
Related
I have a budgeting application, I have 3 models I am pulling into 1 view.
Budget - get the users budgeting information details (i.e, name of budget, date, etc.)
BillBudgetTotal - Allows the user to add a cumulative total for that budget (i.d., budgetid, total amount)
BudgetTotalBreakdown - Allows the user to break their budget down into futher details (i.e., break the total amount down by bill name (water, gas, electric, misc, etc.)
The UI will give the user the option to select a budget (via dropdown) they want to work in and then allow them to enter in their dollar amounts based on which bill they selected.
Problem: I am trying to figure out a way to allow the partial view (the area under the dropdown) to refresh based on the dropdown selection. I can't seem to get the partial to refresh with the updated model (it needs to be reset based on the value of the dropdownlist selection). I have exhausted multiple options on stack overflow.
Something like this:
Model:
public class MyBudgets : Financials
{
public Budgets Budget{ get; set; }
public BillBudgetTotal BudgetTotals { get; set; }
public BillBudgetTotalBreakdown BudgetTotalBreakdown { get; set; }
}
Html:
<div class="col-md-3"></div>
<div class="row col-md-6">
#Html.DropDownListFor(model => model.Budget.SelectedBills, Model.Budget.SelectedBills.Select(b => new SelectListItem() { Value = b.Bill_Id.ToString(), Text = b.Bill}), "Select A Bill...", new { #class = "form-control"})
</div>
<div class="col-md-3"></div>
<br /><br />
<hr />
<div id="billBudgetPartial">
#Html.Partial("Budgeting/_BillTotalAmount", Model);
</div>
Controller:
[HttpGet]
public ActionResult Budgets(int budgetId)
{
MyBudgets model = new MyBudgets
{
Budgets = _executionRepository.RetrieveBudgets(budgetId)
};
model.Budget.SelectedBills = _executionRepository.SetSelectedBudgets(budgetId);
return View(model);
}
[HttpPost]
public ActionResult Budgets()
{
return Json(new { success = "false" });
}
public ActionResult BillTotalAmount(int id)
{
var model = new MyBudgets
{
Budgets = _executionRepository.RetrieveBudgetsByBillBudget(id),
BillBudgetTotal = _executionRepository.RetrieveBillBudgetByBillId(id),
BillBudgetTotalBreakdown = _executionRepository.RetrieveBillBudgetTotalBreakdown (id)
};
return PartialView("Execution/_BillTotalAmount", model);
}
You can use ajax to do partial update to your page. when razor render your page, it will generate a SELECT element with the id "Budget_SelectedBills". So listen to the change event on this dropdown, get the selected value and send that to your server(an action method) and let it return the partial view for the markup you want below. You may use jQuery load method to update the DOM with the new markup coming from server.
#section Scripts
{
<script>
$(function(){
$("#Budget_SelectedBills").change(function(e){
var val=$(this).val();
$("#billBudgetPartial").load("/Budgeting/BillDetails/"+val);
});
});
</script>
}
Assuming you have BillDetails action method in BudgetingController which accpets the billId an return the partial view for the bottom portion of screen.
public ActionResult BillDetails(int id)
{
var model = ReplaceYourModelForBillTotalAmountViewHere();
return PartialView("Budgeting/_BillTotalAmount", model);
}
EDIT: As per the comment
How can I pass 2 parameters in this? like not just the id from the
drop but something else the list the #Model.BudgetId
If your javascript code is in the same razor view, you can simply use Model.BudgetId as the second querystring param value.
Assuming BudgetId is an int type
#secion Scripts
{
<script>
$(function(){
$("#Budget_SelectedBills").change(function(e){
var val=$(this).val();
$("#billBudgetPartial").load("/Budgeting/BillDetails/"+val
+"?budgetId="+#Model.BudgetId);
});
});
</script>
}
Now make sure that your action method has this second parameter
public ActionResult BillDetails(int id,int budgetId)
{
var model = ReplaceYourModelForBillTotalAmountViewHere();
return PartialView("Budgeting/_BillTotalAmount", model);
}
If your javascript code is in an external js file, you may keep Model.BudgetId to somewhere in the DOM and read that. Either a hidden field or keep it in html 5 data attributes of the select element.
Ok, so I have this class:
public class BackstoreInventoryUtility
{
public BackstoreInventoryInfo Item { get; set; }
public List<ItemListingUtility> ListItemUtility { get; set; }
public BackstoreInventoryUtility()
{
Item = new BackstoreInventoryInfo();
ListItemUtility = new List<ItemListingUtility>();
}
}
And here's the ListItemUtility class:
public class ItemListingUtility
{
public int Quantity { get; set; }
public string Duration { get; set; }
public List<string> AvailableDurations { get; set; }
public ItemListingUtility()
{
AvailableDurations = new List<string>();
}
}
In a view I am building, I am displaying 1 BackstoreInventoryUtility based on a BackstoreInventoryInfo item my user is currently browsing.
The ListItemUtility is a class allowing the user to proceed to certain action, like display for a set time a set quantity.
The view renders like this:
#model MyApp.Utilities.BackstoreInventoryUtility
#using (Html.BeginForm())
{
<div>
#if (Model.Item.Quantity > 0)
{
<input type="submit" value="Display"/>
}
#Html.HiddenFor(_item => _item.Item.BackstoreInventoryID)
<div class="bigFontSize bold formStyle">
<label class="center">Options will eventually be displayed here.</label>
<div>
<div class="float-left">Quantity Allocated:</div>
<div class="float-right">#Html.DisplayFor(_item => _item.Item.Quantity)
#Html.HiddenFor(_item => _item.Item.Quantity)
</div>
<div class="clear"></div>
</div>
<div class="formStyle" id="itemUtilityZone">
<label>Options</label>
#for (int i = 0; i < Model.ListItemUtility.Count; i++)
{
<div>
<div class="float-left">
Quantity To Display:
</div>
<div class="float-right">
#Html.TextBoxFor(_item => _item.ListItemUtility[i].Quantity, new { #class = "positive-integer numberTextBox" })
</div>
<div class="clear"></div>
</div>
}
</div>
#if (Model.Item.Quantity > 0)
{
<input type="submit" value="Display"/>
}
</div>
}
I'd like my user to dynamically add a new row to the view, and then when the view is submitted, all the rows would be included.
So far I am at the beginning and I am trying this:
[HttpGet]
public ActionResult AddItemUtilityRow()
{
return PartialView(new ItemListingUtility());
}
Where the partial view rendered would be identical to the div used in the table. But I am not sure how could I make this happen, should I use a jQuery call? How might I do this?
EDIT Okay, so I have tried something in jquery which VISUALLY does what I want:
<script type="text/javascript">
$(document).ready(function() {
$("#addUtility").click(function() {
$.get("#Url.Action("AddItemUtilityRow")", {
}, function(data) {
$('#itemUtilityZone').append(data);
});
});
});
</script>
So, as I said, this works but only partially because when the user submits only the default number of items in the list is submitted. How can I make it so that each time the user add a row it adds up to the model and gets later submitted?
Woah! It was more complex than I thought, but thanks to this link : http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/ I was able to make the whole thing work!
I first transfered every row created in a partial view like this:
<div class="formStyle" id="itemUtilityZone">
<label>Options</label>
#foreach (var utilityRow in Model.ListItemUtility)
{
Html.RenderPartial("ItemUtilityRow", utilityRow);
}
</div>
Which renders like this:
#using HtmlHelpers.BeginCollectionItem
#model MyApp.Utilities.ItemListingUtility
#using (Html.BeginCollectionItem("listItems"))
{
<div>
<div class="float-left">
Quantity To Display:
</div>
<div class="float-right">
#Html.TextBoxFor(_item => _item.Quantity, new { #class = "positive-integer numberTextBox" })
</div>
<div class="clear"></div>
</div>
}
Note: for the Html.BeginCollectionItem Html Helper, I had to search a bit for Steven Sanderson's Helper which he mentions in the upper link. You can find it here:
https://github.com/danludwig/BeginCollectionItem
Next, my javascript call looks like this:
$(document).ready(function() {
$("#addUtility").click(function () {
$.ajax({
url: '#Url.Action("AddItemUtilityRow")',
cache: false,
success: function(html) {
$('#ItemUtilityZone').append(html);
}
});
});
});
And the controller method that adds a new row:
[HttpGet]
public ActionResult AddEbayUtilityRow()
{
return PartialView("ItemUtilityRow", new ItemListingUtility());
}
And the rows shows just fine now. The catch is, how do I catch it back in my post method? Well, following Steve Sanderson's blog, I understood that the listItems variable was actually the name of the collection which would be sent back to the post method.
So by adding this parameter to the controller post method:
IEnumerable<EBayListingUtility> listItems
The list is indeed sent back to the post method with the count being what it is supposed to be. Hurray!
We approach this in one of two ways:
1.) Client-side approach - you can use jquery/knockout whatever to append items to your table. This is fine for simple additions, but negates the use of c# in the view.
2.) Server-side approach (and usually used) - Basically, post your viewmodel back to an action that manually adds a list item;
[HttpGet]
public ActionResult AddItemUtilityRow()
{
return PartialView(new ItemListingUtility());
}
[HttpPost]
public ActionResult AddItemUtilityRow(BackstoreInventoryUtility viewModel)
{
viewModel.ListItemUtility.Add(new ItemListingUtility());
return PartialView(viewModel);
}
We have a number of ways using jquery of 'posting' to a different action (the one that simply adds an item). I would consider using jquery's ajax call to accomplish this.
But the premise is the same:
send the data from your page to the server
manipulate the data
reuse the view you created
I my below code i am calling partial view with ajax but when i click on the link of product Name the description of that product is not retrieved through ajax and error of ajax executes. I am retrieving the details of items selected by user on the same page but it is not retrieved. Please give any suggestion where is the issue arising because i am new to MVC. thanks...
Create.cshtml
#model List<PartialView.Models.tbl_product>
<!DOCTYPE html>
<html>
<head>
<title>Create</title>
<script src="#Url.Content("~/Scripts/jquery-1.5.1.js")" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
$('.msg').click(function () {
var id = this.id;
$.ajax({
url: "/Category/Display",
data: { data: id },
success: function (mydata) {
$("#link").empty().append(mydata);
},
error: function (mydata) { alert("error"); },
type: "POST"
});
return false;
});
});
</script>
</head>
<body>
#foreach (var item in Model)
{
<a class="msg" href="#" id="#item.ProductId">#item.ProductName</a>
}
<div id="link">
</div>
</body>
</html>
ClicksUs.cshtml (PartialView)
#model List<PartialView.Models.tbl_product>
#foreach(var items in Model)
{
#items.ProductDesc
}
CategoryController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using PartialView.Models;
namespace PartialView.Controllers
{
public class CategoryController : Controller
{
dbEntities dbentity = new dbEntities();
public ActionResult Create()
{
return View(dbentity.tbl_product.ToList());
}
public ActionResult Display(int data)
{
var query = dbentity.tbl_product.First(c => c.ProductId == data);
return PartialView("ClicksUC", query);
}
}
}
Your Details controller action selects a single element here (because you are calling .First()):
public ActionResult Display(int data)
{
var query = dbentity.tbl_product.First(c => c.ProductId == data);
return PartialView("ClicksUC", query);
}
So the type of the query variable is tbl_product and not List<tbl_product>.
On the other hand your partial's model is List<PartialView.Models.tbl_product> which is obviously wrong.
Your partial's model should be a single tbl_product:
#model PartialView.Models.tbl_product
#Model.ProductDesc
Oh and what others said about the typo in your partial view name.
there are three issues in the code that you could address.
One is a typo (the partialview is called ClicksUS, NOT ClicksUC),
the other is related to the way you return the data
the third is that you use the type: "POST", you should change this to type: "GET".
try changing the code to:
public ActionResult Display(int data)
{
// using First() would have caused you an error in the view if not found
// still not perfect below, but a step closer
var query = dbentity.tbl_product.FirstOrDefault(c => c.ProductId == data);
// You had ClicksUC, rather than ClicksUS
return PartialView("ClicksUS", query);
}
I'd also strongly suggest that you create a ViewModel for your data, rather than passing the objects from the database as this will allow you to control exactly the data that should be viewed and how it should be formatted etc.
[edit]
Also, as Darin says, based on a single row being retruned, you should change your partial view model type to:
#model PartialView.Models.tbl_product
I have a classes like this:
public class member
{
public string name {get;set;}
public IList<Note> notes {get;set;}
}
public class note
{
public string text {get;set;}
public datetime created {get;set;}
}
I want to have a page which inserts the member class - which i am fine with. My question lies in how to go about adding multiple notes to the member on the same page?
What would be the best way to go about this? (maybe some ajax solution to show sub forms for the note class)
Can anyone point me in the right direction of some related examples learning material?
Thanks in advance.
I'd create an Ajax form that posts to a method called AddNote(AddNoteViewModel viewModel) on your controller. AddNoteViewModel would contain all the information you need to create a new note. The AddNote Action Method would add the new note, SaveChanges and return a list of notes for the given Member. You can use a partial view for the content that is returned from AddNote.
On the Ajax form you should set UpdateTargetId to the id of the <div> you want to update with the latest list of notes.
Another option might be to use JQuery.
Here is a good example of both: Using Ajax.BeginForm with ASP.NET MVC 3 Razor
UPDATE : I've adapted Darin Dimitrov's example (from the link) to suit your scenario. This is off the top of my head so won't be perfect but it should give you a decent starting point
Model:
public class AddNoteViewModel
{
[Required]
public int MemberId { get; set; }
[Required]
public string Text { get; set; }
}
Controller:
[HttpPost]
public ActionResult AddNote(AddNoteViewModel model)
{
var member = //Get member from db using model.MemberId
member.Notes.Add(new Note{Text = model.Text, Created = DateTime.Now});
//SaveChanges();
var notes = //Get notes for member
return View(notes);
}
View:
#model AppName.Models.AddNoteViewModel
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<div id="result"></div>
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "result" }))
{
#Html.HiddenFor(x => x.MemberId)
#Html.EditorFor(x => x.Text)
#Html.ValidationMessageFor(x => x.Text)
<input type="submit" value="OK" />
}
Using JQuery:
View:
#model AppName.Models.AddNoteViewModel
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/index.js")" type="text/javascript"></script>
<div id="result"></div>
#using (Html.BeginForm())
{
#Html.HiddenFor(x => x.MemberId)
#Html.EditorFor(x => x.Text)
#Html.ValidationMessageFor(x => x.Text)
<input type="submit" value="OK" />
}
index.js:
$(function () {
$('form').submit(function () {
if ($(this).valid()) {
$.ajax({
url: this.action,
type: this.method,
success: function (result) {
$('#result').html(result);
}
});
}
return false;
});
});
how can i render multiple different actions in one call to a speccific controller?
Html.RenderAction() / Html.Action() only handles one controller&action.
But what if i want in one call to render different views on the screen?
thanks in advance,
Sagiv
EDIT:
Hi again.
I'm not sure you understood my question.
this is the cshtml:
<div id="menu">#using (Ajax.ActionLink("click me", "SomeAction","SomeController", new AjaxOptions() { HttpMethod = "POST", OnSuccess = "showMsg", OnFailure = "showError" }))</div>
<div id="div1">bla bla content</div>
....
<div id="div2">bla bla content</div>
and this is the controller:
public class SomeController : Controller
{
public ActionResult SomeAction()
{
return View("somethingfordiv1", ModelForDiv1);
return View("somethingfordiv2", ModelForDiv2); //i want also return another view here
}
}
in this ajax call on the controller, i want to return 2 different views for 2 different divs.
thanks again :)
Here's one way you could proceed. You could aggregate the two view models into a unique view model and then have the controller action return a view containing javascript which will inject the two view results into the different divs.
As always start with the view models:
public class Model1 { }
public class Model2 { }
public class AggregatedModel
{
public Model1 Model1 { get; set; }
public Model2 Model2 { get; set; }
}
Then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult SomeAction()
{
var model = new AggregatedModel
{
Model1 = new Model1(),
Model2 = new Model2()
};
Response.ContentType = "text/javascript";
return PartialView(model);
}
}
Then the corresponding ~/Views/Home/Index.cshtml view:
<div id="menu">
#Html.ActionLink("click me", "SomeAction", "Home", new { id = "clickme" })
</div>
<div id="div1">bla bla content</div>
<div id="div2">bla bla content</div>
<script type="text/javascript">
$('#clickme').click(function () {
$.getScript(this.href);
return false;
});
</script>
Next the ~/Views/Home/SomeAction.cshtml view:
#model AggregatedModel
$('#div1').html(#Html.Raw(Json.Encode(Html.Partial("Model1", Model.Model1).ToHtmlString())));
$('#div2').html(#Html.Raw(Json.Encode(Html.Partial("Model2", Model.Model2).ToHtmlString())));
and finally the two ~/Views/Home/Model1.cshtml and ~/Views/Home/Model2.cshtml views:
#model Model1
<span>This is the contents for model1</span>
and:
#model Model2
<span>This is the contents for model2</span>
If you want to render different views on the screen return a model which represents the data for those views, then you can use RenderPartial and pass the part of the model data required to each view.
You can also use viewdata to separately have this available.
Html.RenderAction is also available but simulates another full request
For your ajax request you can return a html chunk from the rendering of a partial view and this can be determined by Request.IsAjaxRequest. Then your javascript can set the result into the document.
This is in your action
if (Request.IsAjaxRequest())
{
return View("PartialViewName", partialModel);
}
return View("NormalView", normalModel);
And the client side example (using jquery)
function hijack(form) {
$("div#SearchResults").html("");
$("div#SearchResults").addClass('loading');
$.ajax({
url: form.action,
type: form.method,
dataType: "html",
data: $(form).serialize(),
success: function(data) {
$("div#SearchResults").removeClass('loading');
$("div#SearchResults").html(data);
}
});
}