Is there a way to refresh a section of a partial view, or should I break that section out into it's own separate partial view? The problem I'm facing is that whenever I submit my form, the entire partial view updates, duplicating my form inputs, but I really only want the "noteList" to update. Here is some more details that might be helpful.
In my project, I have a page broken out into separate tabs, each of them is it's own partial view that is loaded when someone clicks on that tab. The one I'm focusing on at the moment is a notes tab.
Below is the markup and controller that catches the post.
<div id="noteList">
#Html.Grid(Model).Columns(column => {
column.For(x => x.TimeStamp);
column.For(x => x.UserName);
column.For(x => x.Note);
}).Attributes(Style => "text-aligh: center", #Class => "linkGrid")
</div>
<div id="addNoteForm">
<h2>Add New Note</h2>
#using (Ajax.BeginForm("AddNote", "Dispute", null, new AjaxOptions { UpdateTargetId = "noteList" }, new { id = "AddNote" })) {
#Html.Hidden("DisputeID", ViewData["DisputeID"])
<div class="editor-multiline-field">
#Html.Editor("Note", "editor-multiline-field")
</div>
<input type="submit" value="Add Note" />
}
</div>
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddNote(FormCollection collection) {
if (this.ModelState.IsValid) {
DisputeNote newNote = new DisputeNote(repository.Get(int.Parse(collection["DisputeID"])), User.Identity.Name, collection["Note"]);
repository.SaveForUpdate<DisputeNote>(newNote);
}
var Notes = repository.GetNotesForDispute(int.Parse(collection["DisputeID"]));
ViewData["DisputeID"] = int.Parse(collection["DisputeID"]);
return PartialView("NoteList", Notes);
}
I know breaking it into another partial view will work, but I'm curious if there is another way to do it.
You need another partial view to do it the way you have listed here - you cannot update a portion of a partial view with that same partial view itself. HOWEVER : ) there are other options though - such as just returning JSON.
You could just use a .ajax() call to post the JSON to your controller method to add the note.
See this post for the rough idea:
JSON / MVC (3P1) HttpPost - not getting it to work on my EF class
Related
I have added a button in my view. When this button is clicked partial view is added. In my form I can add as much partial view as I can. When Submitting this form data I am unable to send all the partial view data to controller.
I have made a different model having all the attributes and I have made a list of that model to my main model. Can anyone please give me some trick so that I can send all the partial view content to my controller?
In My View
<div id="CSQGroup">
</div>
<div>
<input type="button" value="Add Field" id="addField" onclick="addFieldss()" />
</div>
function addFieldss()
{
$.ajax({
url: '#Url.Content("~/AdminProduct/GetColorSizeQty")',
type: 'GET',
success:function(result) {
var newDiv = $(document.createElement("div")).attr("id", 'CSQ' + myCounter);
newDiv.html(result);
newDiv.appendTo("#CSQGroup");
myCounter++;
},
error: function(result) {
alert("Failure");
}
});
}
In My controller
public ActionResult GetColorSizeQty()
{
var data = new AdminProductDetailModel();
data.colorList = commonCore.getallTypeofList("color");
data.sizeList = commonCore.getallTypeofList("size");
return PartialView(data);
}
[HttpPost]
public ActionResult AddDetail(AdminProductDetailModel model)
{
....
}
In my Partial View
#model IKLE.Model.ProductModel.AdminProductDetailModel
<div class="editor-field">
#Html.LabelFor(model => model.fkConfigChoiceCategorySizeId)
#Html.DropDownListFor(model => model.fkConfigChoiceCategorySizeId, Model.sizeList, "--Select Size--")
#Html.ValidationMessageFor(model => model.fkConfigChoiceCategorySizeId)
</div>
<div class="editor-field">
#Html.LabelFor(model => model.fkConfigChoiceCategoryColorId)
#Html.DropDownListFor(model => model.fkConfigChoiceCategoryColorId, Model.colorList, "--Select Color--")
#Html.ValidationMessageFor(model => model.fkConfigChoiceCategoryColorId)
</div>
<div class="editor-field">
#Html.LabelFor(model => model.productTotalQuantity)
#Html.TextBoxFor(model => model.productTotalQuantity)
#Html.ValidationMessageFor(model => model.productTotalQuantity)
</div>
Your problem is that the partial renders html based on a single AdminProductDetailModel object, yet you are trying to post back a collection. When you dynamically add a new object you continue to add duplicate controls that look like <input name="productTotalQuantity" ..> (this is also creating invalid html because of the duplicate id attributes) where as they need to be <input name="[0].productTotalQuantity" ..>, <input name="[1].productTotalQuantity" ..> etc. in order to bind to a collection on post back.
The DefaultModelBinder required that the indexer for collection items start at zero and be consecutive, or that the form values include a Index=someValue where the indexer is someValue (for example <input name="[ABC].productTotalQuantity" ..><input name="Index" value="ABC">. This is explained in detail in Phil Haack's article Model Binding To A List. Using the Index approach is generally better because it also allows you to delete items from the list (otherwise it would be necessary to rename all existing controls so the indexer is consecutive).
Two possible approaches to your issue.
Option 1
Use the BeginItemCollection helper for your partial view. This helper will render a hidden input for the Index value based on a GUID. You need this in both the partial view and the loop where you render existing items. Your partial would look something like
#model IKLE.Model.ProductModel.AdminProductDetailModel
#using(Html.BeginCollectionItem())
{
<div class="editor-field">
#Html.LabelFor(model => model.fkConfigChoiceCategorySizeId)
#Html.DropDownListFor(model => model.fkConfigChoiceCategorySizeId, Model.sizeList, "--Select Size--")
#Html.ValidationMessageFor(model => model.fkConfigChoiceCategorySizeId)
</div>
....
}
Option 2
Manually create the html elements representing a new object with a 'fake' indexer, place them in a hidden container, then in the Add button event, clone the html, update the indexers and Index value and append the cloned elements to the DOM. To make sure the html is correct, create one default object in a for loop and inspect the html it generates. An example of this approach is shown in this answer
<div id="newItem" style="display:none">
<div class="editor-field">
<label for="_#__productTotalQuantity">Quantity</label>
<input type="text" id="_#__productTotalQuantity" name="[#].productTotalQuantity" value />
....
</div>
// more properties of your model
</div>
Note the use of a 'fake' indexer to prevent this one being bound on post back ('#' and '%' wont match up so they are ignored by the DefaultModelBinder)
$('#addField').click(function() {
var index = (new Date()).getTime();
var clone = $('#NewItem').clone();
// Update the indexer and Index value of the clone
clone.html($(clone).html().replace(/\[#\]/g, '[' + index + ']'));
clone.html($(clone).html().replace(/"%"/g, '"' + index + '"'));
$('#yourContainer').append(clone.html());
}
The advantage of option 1 is that you are strongly typing the view to your model, but it means making a call to the server each time you add a new item. The advantage of option 2 is that its all done client side, but if you make any changes to you model (e.g. add a validation attribute to a property) then you also need to manually update the html, making maintenance a bit harder.
Finally, if you are using client side validation (jquery-validate-unobtrusive.js), then you need re-parse the validator each time you add new elements to the DOM as explained in this answer.
$('form').data('validator', null);
$.validator.unobtrusive.parse($('form'));
And of course you need to change you POST method to accept a collection
[HttpPost]
public ActionResult AddDetail(IEnumerable<AdminProductDetailModel> model)
{
....
}
I am working on my third year project and I'm struggling with this one section so badly, I have looked around and found some answers but I really done know how to implement in my code because it always just doesn't work. So I don't know what I'm doing wrong.
What i would like is for the partial view to change when the drop down selected item has changed.
This is what was generated in the view for this section.
<div class="form-group">
#Html.LabelFor(model => model.TypeID, "TypeID", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("TypeID", String.Empty)
#Html.ValidationMessageFor(model => model.TypeID)
</div>
</div>
Most solutions I have seen use a #html.dropdownlistfor().
Any help would be appreciated even if you could just point me to the right place.
The drop down list is filled from the database relationships.
I have got this to work if i use labels in an "a" tag with an href but they are hard coded onto the page. I want to use the drop down list so that if i update the database it will have the updated list instead of me going to change it in the code, its also more user friendly in my eyes.
Thanx in advance
You could retrieve the data from the server and construct/change the DOM via JQuery, or you could use a partial view that is appropriate for each question type, attaching an event handler to the change event of the drop-down via JQuery.
One approach, loading partial views:
yourNamespace.yourScript.js file (include the file in your main view via the <script> tag with the src attribute):
(function ($) {
if (!window.yourNamespace) {
window.yourNamespace = {};
}
if (!window.yourNamespace.yourPageScript) {
window.yourNamespace.yourPageScript = function () {
var populateView = function (dropDown) {
if (dropDown && dropDown.length) {
var value = dropdown.val();
$.ajax({
method: "GET",
cache: false,
url: "some.url/PopulateType",
dataType: "HTML"
data: { selectedValue: value }
})
.done(function (response) { // jQuery 1.8 deprecates success() callback
var div = $('#partialViewDiv');
div.html('');
div.html(response);
});
}
};
return {
populateView: populateView
};
};
}
}(jQuery));
Your main view could have something like this:
<script type="text/javascript">
// put the script section somewhere appropriate in the page
$(document).ready(function () {
var dropDown = $('#TypeId'); // assuming the ID of this element is 'TypeId'
dropDown.change(function () {
yourNamespace.yourPageScript.populateView(dropDown);
});
});
</script>
<div id="partialViewDiv">
#Html.RenderPartial("path to initial partial view", model);
</div>
partial view example (adjust to be proper for any particular dropdown selection):
#model namespaceName.QuestionTypeModel
<div class="form-group>
#* put controls appropriate to the particular partial views here, such as radio buttons for the multiple choice question type, etc. *#
<div>
<div class="form-group">
#Html.LabelFor(model => model.TypeID, "TypeID", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("TypeID", Model.QuestionTypeValues)
#Html.ValidationMessageFor(model => model.TypeID)
</div>
</div>
Part of the controller:
[HttpGet]
public ActionResult Index()
{
var model = new MainViewModel();
// populate the model with data here for the initial display, including the initial drop-down values,
// presumably the same way you do now
// into model.QuestionTypeValues
return View(model); // the main view
}
[HttpGet]
public ActionResult PopulateType(int selectedValue) // could use an enum for the selectable values
{
var model = new QuestionViewModel();
string partialViewName = null;
// populate with data appropriate to the partial views
switch (selectedValue)
{
case 0:
partialViewName = "partial view name for item 0";
// populate "model" with the appropriate data
break;
case 1:
partialViewName = "partial view name for item 1";
// populate "model" with the appropriate data
break;
// and so on...
default:
throw new ArgumentException("unknown selected value", "selectedValue");
break;
}
return PartialView(partialViewName, model);
}
The approach to use jQuery to build the DOM elements instead of using partial views is left as an exercise.
I've searched and searched and for the life of me cannot figure out what I'm doing wrong. I have a Kendo UI window like so:
<a id="#(item.POD_ID)" class="k-button btn-reminder" title="Add Reminder" onclick="$('#windowR#(item.POD_ID)').data('kendoWindow').open();"><span class="icon-reminder icon-only btn-reminder"></span></a
#(Html.Kendo().Window()
.Name("windowR" + item.POD_ID)
.Title("Set Reminder")
.Content("loading...")
.LoadContentFrom("_LoadReminder", "Purchasing", new { id = item.POD_ID })
//.Iframe(true)
.Draggable()
//.Resizable()
.Modal(true)
.Visible(false)
.Width(200)
.Height(80)
.Events(events => events
.Close("onCloseReminder")
.Open("onOpenReminder")
.Deactivate("function() { this.refresh();}")
.Activate("function(){ $('#empNumBox').focus(); }")
)
)
And, if the window is an Iframe, all of this works just fine, however I cannot have it as an Iframe, because that means reloading all of the scripts and styles within it and more difficult to reference parent.
So this window, loads content from the partial view like so:
#using (Ajax.BeginForm("SetReminders", "Purchasing", new AjaxOptions { UpdateTargetId = "result" }))
{
<div id="result"></div>
<input type="number" id="empNumBox" name="empNum" style="width: 70px" class="k-textbox" required autofocus="autofocus" value="#(ViewBag.EMP_NUM)"/>
<input type="hidden" value="#ViewBag.POD_ID" name="podID" id="POD_ID_Form_Field"/>
<input type="submit" id="submitReminder_button" style="width:auto;" class="k-button submitReminder_button" value="Remind" />
}
That partial view also renders fine. Here is the problem though, when you submit the ajax form, and the kendo window is not an iframe, it will render the whole page as whatever the controller returns (and I have tried several things, you can see in the commented out code below:
[HttpPost]
public ActionResult SetReminders(int empNum, int podID)
{
//some database stuff that works fine
string response;
if (existingReminder == 0)
{
//more db stuff that also works fine
db.SaveChanges();
response = "Success!";
}
else
{
response = "Reminder exists.";
//return PartialView("_LoadReminder", new string[] { "Reminder already exists!" });
}
// $('#submitReminder_button').closest('.k-window-content').data('kendoWindow').close();
//alert('Hello world!');
//return Content("<script type='text/javascript/>function(){alert('Hello world!');}</script>");
return PartialView("_SubmitSuccess");
//return Json(response);
//return Content(response, "text/html");
}
If anyone is curious, all that the _SubmitSuccess contains is the word "Success!".
Here's an example that I found with the ajax response being put in a div, which I followed:
http://pratapreddypilaka.blogspot.com/2012/01/htmlbeginform-vs-ajaxbeginform-in-mvc3.html
It seems the real issue here is the Kendo window, but I have yet to find something on their forums about this, doesn't seem that there's a lot of examples of using a kendo window to load a partial view that submits an form via ajax and returns a different partial view to be loaded within the same window and does not use an iframe.
Any suggestions welcome, even about other parts of the code, I'm always looking to improve.
Try changing the return type of SetReminders() to PartialViewResult
I dint find answers to this and tried several ways. any help would be appreciated thanks !!
I have view which updates the page without reloading on each click using ajax scripts. Below is the code. but after entire partial views are generated, I want user to redirect complete different view on clicking a link which is not associated to controller user is in now.
my View
#model IMONLP.Models.Search
#{
ViewBag.Title = "Search";
}
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
#using (Ajax.BeginForm("Search", new AjaxOptions() { UpdateTargetId = "results"
}))
{
<br />
#Html.TextBoxFor(m => m.search_text, new { style = "width: 200px;" })
<input type="submit" value="search" />
}
#Ajax.ActionLink("Try Demo", "PLNHK", "PLNHK", new AjaxOptions { })
// this ajax link should redirect to different view,
<div id="results">
#if (!String.IsNullOrEmpty(#Model.search_text))
{
Html.RenderPartial("Searchprocess", Model);
}
</div>
My controller:
public ActionResult Search(Search s)
{
//do something
return PartialView("Searchprocess", s);
}
public ActionResult Selected(Search s)
{
//DO something
return PartialView("Selected", s);
}
The above "TryDEMO" "PLNHK" ajax action link will have to be redirected to new controller and new action and return view thats returned by that action. everytime I click on that, I found it moving to that controller and then to corresponding view but again its getting back to old page. I used #html.actionlink instead of Ajax, but now I get this error
The "RenderBody" method has not been called for layout page "~/Views/PLNHK/PLNHK.cshtml".
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
EDIT : I did create PLNHK.cshtml. while I'm trying to debug this, the control goes to PLNHK controller then to PLNHK view(PLNHK.cshtml) parses each and every step in that page, but finally I would get the output to be the older page. I was thinking may be the Ajax scripts on before page is the reason.
I am new at .NET mvc.
In a view "DisplayThings" I have something like:
#foreach (var thing in Model)
{
#Html.Partial("DisplayPartial", thing)
}
In the partial view "DisplayPartial" I have
#using (Ajax.BeginForm("Afunc", new AjaxOptions ()))
{
#Html.EditorFor(model => model.AstringThing)
#Html.EditorFor(model => model.AintThing)
<input type="submit" name="submit" value="Ajax Post" />
}
At the moment the "Afunc"-Action saves the model to the database and then redirects to a controller action to retrieve all "things" from the database and render the entire "Displaythings" View.
My question is: When i press one of the submitbuttons (There is one submitbutton for every "thing" i the list). I want only that partial view to reload/reflect on my change. I dont want to reload the entire "Displaythings" view. How do I do this? If I just return a partial view I lose everything else but that partial view.
If this is a bad approach please give me directions.
Update:
I am still doing something wrong as I get the partial view rendered in a new page. My controller :
public ActionResult Afunc(ThingModel thingmodel)
{
// do
return PartialView("DisplayPartial", thingmodel);
}
I have tried using UpdateTargetId and onsuccess both with the same result (A new page)
In the AjaxOptions that your are simply now passing as new AjaxOptions you can specify the target element using the UpdateTargetId property:
<div id="unique_thing_id">
#using (Ajax.BeginForm("Afunc", new AjaxOptions { UpdateTargetId = 'unique_thing_id' }))
{
}
</div>
Above, a container with unique id for every "thing" is represented with <div id="unique_thing_id">. This will be replaced with the repsonse of the request. Change Afunc to render only the particular "thing" partial.