I try to use DevExtreme component in partial view.
But my partial view page shown when I click on the element.
And in the main page after the click I have error
Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'ApplicationEntity' with type 'System.Data.Entity.DynamicProxies.ApplicationEntity_50A6A66F1464C1DE4E8A736E85D88C5AF4F4249EAE26FB21C4F82592E001885D'. Path 'data[0].ApplicationEntity.ApplicationEntityHistories[0]'.
browser console screen
Main page Code:
<div class="row">
<div class="col-md-7">
<button id="btn">CLICK</button
</div>
<div class="col-md-5" id="divPartialViewContainer">
</div>
</div>
<script>
$(document).ready(function () {
$("#btn").on("click", function () {
var text = $(this).text().trim();
if (text.length > 0) {
console.log(text);
$.ajax({
url: '/RiskMap/RiskDetailsPartial/',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ 'param': text }),
success: function (content) {
$('#divPartialViewContainer').html(content);
},
error: function (e)
{
console.log(e);
}
});
}
});
});
</script>
Controller Code
[HttpPost]
public async Task<ActionResult> RiskDetailsPartial(string param)
{
return PartialView("_RiskDetails", new List<Risk>());
}
Partial View code:
#model IEnumerable<Core.Models.Risk>
#using Core.Models
#using Core.ComplexTypes
#{
ViewBag.Title = "Risks";
}
<h2>Risks</h2>
#(Html.DevExtreme().DataGrid<Risk>()
.DataSource(Model)
.Columns(columns =>
{
columns.AddFor(m => m.Id);
columns.AddFor(m => m.Impact);
columns.AddFor(m => m.Probability);
})
)
The message is clear you just need to read it more thoroughly.
Newtonsoft.Json.JsonSerializationException: Self referencing loop
detected for property 'ApplicationEntity' with type
'System.Data.Entity.DynamicProxies.ApplicationEntity_50A6A66F1464C1DE4E8A736E85D88C5AF4F4249EAE26FB21C4F82592E001885D'.
Path 'data[0].ApplicationEntity.ApplicationEntityHistories[0]'.
Json serializer is attempting to serialize some kind of entity(lets call it EntityA) you passed to it, but the problem is that this entity contains another entity(lets call it EntityB) that contains the first entity(EntityA). This is going in circles!
This has also happened to me with my own ORM and I found out the problem is lazy loading. I solved it by adding an interface to each of my entities:
interface IJSonify
{
object Json();
}
Here I simply return an anonymous object. Entity that implements this interface decides how it will represent itself as JSON object.
I had the same problem and I solved it by declaring JsonConvert defaultSettings on webApplication init.
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Formatting = Newtonsoft.Json.Formatting.Indented,
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
};
Apparently DevExtreme don't use standard ASP.NET MVC json serializer inside DataSourceLoader.Load(..) method, so if you set ASP.NET MVC json serializer ReferenceLoopHandling setting it is not enough.
Another solution is use [JsonIgnore] dataAnnotation above the property that generate the loop reference
Related
Here are some details about our development environment:
-DevExpress 20.1.7 ( we are using DevExtreme )
-Microsoft Visual Studio Enterprise 2019 (Version 16.4.6)
-ASP.NET Core 3.1.0
I canNot do the following with JavaScript because the document javascript object is undefined inside the following razor code.
#await Html.PartialAsync("UpldPopupContentTmpltPartial", new
ViewDataDictionary(ViewData) { { "BookId",
document.getElementById("HiddenSelectedUploadDataType").Value } });
Could someone please give me a detailed explanation with sample code that will show me how I can pass the document.getElementById("HiddenSelectedUploadDataType").Value to the aforementioned partial view?
#yiyi-you Thank you for your detailed explanation within your answer.
However, I used the following line of code with #Html.Raw , but even though I'm practical if deadline dates for projects are really close, and I have to get stuff done, I still prefer the proper code implementation practices especially when it makes it easier for code reuse in the future and/or security and/or clarity:
#await Html.PartialAsync("UpldPopupContentTmpltPartial", new
ViewDataDictionary(ViewData) { { "BookId",
#Html.Raw("document.getElementById('HiddenSelectedUploadDataType').Value")}
});
Is the aforementioned code adhere to proper implementation practices? If yes or no then please do explain. Appreciate feedback. Thanks.
There are two solutions:
1.pass model data to Partial,here is a demo:
TestPartial.cshtml:
#model Model1
<input asp-for="HiddenSelectedUploadDataType" />
#await Html.PartialAsync("UpldPopupContentTmpltPartial", new ViewDataDictionary(ViewData) { { "BookId",Model.HiddenSelectedUploadDataType } })
Model1:
public class Model1
{
public string HiddenSelectedUploadDataType { get; set; }
}
UpldPopupContentTmpltPartial.cshtml:
<div>#ViewData["BookId"]</div>
result:
2.You can use ajax to pass document.getElementById("HiddenSelectedUploadDataType").Value to action,and action return partial view to view,here is a demo:
TestPartial.cshtml:
#model Model1
<input asp-for="HiddenSelectedUploadDataType" />
<div id="partial"></div>
#section scripts{
<script>
$(function () {
$.ajax({
url: '/TestSlick/GetPartial',
type: 'POST',
data: {
BookId: $("#HiddenSelectedUploadDataType").val()
},
success: function (data) {
$('#partial').html(data);
}
});
})
</script>
}
TestSlickController:
public IActionResult TestPartial() {
Model1 m = new Model1 { HiddenSelectedUploadDataType="sdsss"};
return View(m);
}
public IActionResult GetPartial(string BookId) {
ViewData["BookId"] = BookId;
return PartialView("UpldPopupContentTmpltPartial");
}
result:
I had a view that contained the following script:
$(document).ready(function () {
var messages = #Html.Raw(Json.Encode(Model.Messages)); // Accessing the Model here.
$.each(messages, function (index, item) {
addMessageRow(item, false);
});
$("#btnAddMessage").on("click", function () {
addMessageRow();
});
// More code
});
.. which worked just fine. Then, I converted it to a partial view which I display in a bootstrap modal. As far as I know, the script section doesn't work in the modal/partial view (and it didn't). So, I moved the javascript code to the parent view to be executed right after calling the .modal method:
$("a[data-modal]").on("click", function () {
$("#childModalContent").load(this.href, function () {
$("#childModal").modal({ keyboard: true }, "show");
var messages = /*GetMessagesAsJson();*/ // How to get the Messages here?
$.each(messages, function (index, item) {
addMessageRow(item, false);
});
$("#btnAddMessage").on("click", function () { // This works just fine.
addMessageRow();
});
});
return false;
});
..which worked, except that I don't know how to get the Messages collection now because it belongs to the Model of the partial view. This is how the partial view gets its model:
public ActionResult Edit(int? parentId, int? childId)
{
if (parentId == null || childId == null) { /*BadRequest*/ }
child child = GetChild(parentId, childId);
if (child == null) { /*NotFound*/ }
return PartialView("_Edit", child);
}
And here's how the action is called:
#Html.ActionLink("Edit", "Edit", "ControllerName",
new {parentId = Model.ParentId, childId = item.Id },
new { #class = "btn btn-default", data_modal = "" })
As the .on("click") function above shows, clicking this link loads the partial view as a model. For this to work, I have the following placeholder in the parent view:
<div id="childModal" class="modal fade in">
<div class="modal-dialog">
<div class="modal-content">
<div id="childModalContent"></div>
</div>
</div>
</div>
I checked this similar question. The answer there suggests passing the relevant data between the views but I'm not sure how to do that considering that the partial view is returned by the controller as shown above.
How can I execute javascript code on the partial (modal) view accessing its Model?
Scenario 1 - Data inside the partial view is static i.e. Data model of partial view doesn't change after parent view has been loaded
From the action method, return view not partial view
Create another partial view which accepts the same model as the partial view. Put all JS code here
Now we have two partial views. One with HTML and another with JS code. Call both of them from the parent view. HTML partial view will go inside the HTML and JS code partial view will go in the Scripts section.
The partial view with JS code is same as any other partial view but only has JS code. See example below
Here is an example in .net core:
Action method
public async Task<IActionResult> Search(SearchModel model)
{
var vm = _someService.GetData(model);
return View(vm);
}
Parent View
#model SomeViewModelOfYourChoice
<section class="filter-section">
//Render HTML partial view
#{ await Html.RenderPartialAsync("_SearchResultsFiltersPartial", Model); }
</section>
#section scripts{
//Render JS code partial view
#{ await Html.RenderPartialAsync("_SearchResultsScriptsPartial", Model); }
}
Partial view with JS only code
#model PartialViewModelOfYourChoice
<script>
$(document).ready(function () {
// Model is now available here e.g. #Model
//All your JS code here
});
</script>
Scenario 2 - Data inside the partial view is dynamic i.e data changes every time the partial view is loaded
Return JSON from your action method, not partial view(see example below)
Don't pass any model inside the partial view
On bootstrap modal show event fetch the data from the action and set the result(JSON) in a variable
Run the JS code with the result(JSON)
Here is an example
Return the JSON from the action method
public ActionResult Edit(int? parentId, int? childId)
{
if (parentId == null || childId == null) { /*BadRequest*/ }
child child = GetChild(parentId, childId);
if (child == null) { /*NotFound*/ }
return Json(child,JsonRequestBehavior.AllowGet);
}
Parent View
#model SomeViewModelOfYourChoice
<section class="filter-section">
//Render HTML partial view. Note that model is not passed here
#{ await Html.RenderPartialAsync("_SearchResultsFiltersPartial"); }
</section>
}
On anchor click call the action method.
//Generate the URL
// I believe that Model.ParentId will remain the same but you need to dynamically pass the item.id
var url = '#Html.Raw(Url.Action("Edit", "ControllerName", new {parentId = Model.ParentId, childId = item.Id }))';
// Call action method
$.ajax({url: url,type: 'GET',contentType: 'application/json; charset=utf-8',
success: function (result) {
messages = result;
//Run your rest of the JS code here
}
});
Well, I came up with a hacky workaround. It does the job but it's inefficient because now I have to make an AJAX request to get the child object each time the link is clicked although I already have the same object as the Model of the partial view (just don't know how to access it!)
So, here's what I did. I first added an action to return the child object:
public ActionResult GetChild(int id)
{
var child = _context.ChildEntityDbSet.Find(id);
if (child == null) { return HttpNotFound(); }
return this.JsonEx(child, JsonRequestBehavior.AllowGet);
}
..then a call is made to that action to retrieve that object each time the "Edit" link is clicked to show the modal view:
var currentChild;
$("a[data-modal]").on("click", function () {
currentChild = null;
var childId = $(this).attr("data-child-id");
var modalUrl = this.href;
$.ajax({
url: "/ControllerName/GetChild/" + childId
})
.done(function (data) {
currentChild = data;
if (currentChild != null) { showModal(modalUrl); }
})
.fail(function () {
// Handle
});
return false;
});
And then I access the messages via the currentChild object instead of the Model:
$("#childModal").on("show.bs.modal", function () {
// Add existing messages as rows.
if (currentChild != null) {
var messages = currentChild.messages;
$.each(messages, function (index, item) {
addMessageRow(item, false);
});
}
$("#btnAddMessage").on("click", function () {
addMessageRow();
});
// More code
});
I still hope that someone could come up with a better solution.
I have a partial view on a View of MVC so after Submit the form that is submitting within jquery that you can see below in the code. I have to refresh the Partial view to show some changes that made in partial view after clicking on save button. What should I do in the section of script on click of save?
#using(Html.BeginForm(FormMethod.Post, new{id="form"}))
{
<div>
#Html.Partial("_VehicleCard", Model)
</div>
<div>
<div id="submitBtn" class="row>
#(Model.VehicleCards.Count>0?"":"hidden")">
<div>
<button type="button" id="btnSubmit">Save</button>
</div>
</div>
</div>
}
#section scripts{
<script>
$('#btnSubmit').click(function (event) {
event.preventDefault();
event.stopImmediatePropagation();
$('#form').submit();
//here i wants to refresh Patrial View.
});
</script>
}
Here is my Controller code:
public PartialViewResult GetVehicleForEndMileage(string date, int? Id)
{
try
{
var model = new VehicleEndMilageVM();
DateTime selectedDate;
DateTime.TryParseExact(date, "dd/MM/yyyy", null,
DateTimeStyles.None, out selectedDate);
model.SelectedDate = selectedDate.ToString("dd/MM/yyyy");
model.LocationId = Id ?? 0;
model.VehicleCards =
vehicleDailyInspectionBLL.GetDailyInspectionDetail(selectedDate, Id).Select(x => new VehicleCard
{
VehicleNumber = x.VehicleNumber,
StartMilage = x.StartMilage,
Driver = x.Driver,
EndMilage = x.EndMilage,
VehicleId = x.VehicleId,
VehicleDailyInspectionId = x.VehicleDailyInspectionId,
IsEndMilageAdded = (x.EndMilage !=null && x.EndMilage > 0) ? true : false
}).ToList();
return PartialView("_VehicleCard", model);
}
catch (Exception ex)
{
throw;
}
}
You can simply do it via an ajax call.
First, you have to set an id for <div> tag
<div id="htmlContainer">
#Html.Partial("_VehicleCard", Model)
</div>
Then
$('#btnSubmit').click(function (event) {
event.preventDefault();
event.stopImmediatePropagation();
$('#form').submit();
$.ajax({
url: 'url',
dataType: 'html',
success: function(data) {
$('#htmlContainer').html(data);
}
});
});
You controller seems to be like this :
public PartialViewResult GetVehicleCard(...)
{
return PartialView("_VehicleCard", your view model);
}
HttpPost methods are for SENDING data to the server. You do not need to send your data to the server, rather, you need to GET data from the server with specified criteria and then display it. With that being send, you do not need your HTML.BeginForm() method. Moreover, you do not need to declare a PartialViewResult return type, an ActionResult will suffice. Additionally, you don't need to return the the name of the partial view and the associated model. Simply give the partial view the model results like so:
return PartialView(model)
Next, create an AJAX link on the page you will be clicking your button on like so:
#Ajax.ActionLink("GetVehicleForEndMileage", "Vehicles", new AjaxOptions()
{
HttpMethod = "GET",
InsertionMode = InsertionMode.InsertAfter,
UpdateTargetId = "Results"
})
<div id="Results"></div>
You can wrap this link in a button tag to work with your current set-up.
Now just define your Partial View in a separate .cshtml file.
#model ModelName
<div>
// Model attributes to be displayed here.
</div>
Now, embed that partial view within the view you wish to have the callback displayed.
Having said all of that, your javascript/jQuery can be removed.
I have a working solution, but I don't know how to reload the page after a certain ID is selected from the drop down list. My list is being populated from the DB. When I select it, I can see the ID and the corresponding data for it. However, there is no change on the screen.
Model class:
public List<Hello> getID()
{
var que = (from rel in db.Table1
select new Hello
{
ID = rel.R_ID
}).ToList();
return que;
}
public List<Hello> getStuff()
{
var que = (from wre in db.View
select new Hello
{
ID = wre.R_ID,
Summary = wre.Summary,
Description = wre.Description
}
}
getHello() is the same exact method as the getStuff(), just accepts a string ID parameter.
Controller class:
public ActionResult Index()
{
var model = test.getStuff();
ViewBag.IDs = new SelectList(test.getID(), "", "ID");
return View(model);
}
[HttpPost]
public JsonResult getDataBySelectedID(string selectedId)
{
var que = test.getHello(selectedId);
return Json(que, JsonRequestBehavior.AllowGet);
}
Partial_View Class:
<div class="container">
<table id="myTable" align="left">
<tr>
<th>#Html.DisplayNameFor(model => model.R_ID)</th>
<th>#Html.DisplayNameFor(model => model.Summary)</th>
<th>#Html.DisplayNameFor(model => model.Description)</th>
</tr>
#foreach (var item in Model)
{
<tr id="Home">
<td>#Html.DisplayFor(x => item.R_ID)</td>
<td>#Html.DisplayFor(x => item.Summary)</td>
<td>#Html.DisplayFor(x => item.Description)</td>
</tr>
}
</table>
</div>
View Class:
#Html.DropDownList("ID", ViewBag.IDs as SelectList)
<script>
$(document).ready(function () {
$("#ID").on("change", function () {
var selectedId = this.value;
var url = "/Sample/getDataBySelectedID";
$.ajax({
method: "POST",
dataType: "json",
url: url,
data: {
selectedId: selectedId
}
});
});
});
</script>
#foreach (var item in Model)
{
<tr>
<td>
#{Html.RenderPartial("Partial_Index", item);}
</td>
</tr>
}
How would I be able to reload the page with the selected value and its corresponding data?
Any help would be appreciated!
Thank you.
As described in the comments, you'll have to load your data into your page somehow. I recommend you do this through partial views.
1) Create a reusable Partial View
First, create a partial view that references your model. To do this, create a view as you normally would for your controller, and tick the "Partial View" option. Make sure to select your model in the model dropdown.
Then, move your .cshtml that references your model from your current view to your partial view. For example, if you have a table that lists out the fields of your model, you would cut the entire table into your partial view. You want to include the minimal amount of code needed in the partial view (aka, don't copy your entire view into it).
2) Reference the Partial View in your Current View
Now that you have your partial view set up, you should use it in your existing view to load the table. You should make sure this works first before continuing. You can use the #Html.RenderPartial(string ViewName, object Model) helper method to render it. Read more. So, you might place this line where your now-cut-code was: #RenderPartial("MyPartialView", model), where "MyPartialView" is the name of your partial view, and model is the model object that you want to pass into the partial view.
3) Add Methods to Render Partial View on Controller
At this point, you just need to be able to update your partial view after using AJAX. First, you need to add the ability to render the Partial View as a string so that you can easily inject it into your view. I recommend you do this by implementing a controller interface and letting your controller inherit the needed methods from that. In my programs, I have the following controller interface that my controllers inherit from:
public class IBaseController : Controller
{
internal string PartialViewToString(string partialViewName, object model = null)
{
ControllerContext controllerContext = new ControllerContext(Request.RequestContext, this);
return ViewToString(
controllerContext,
ViewEngines.Engines.FindPartialView(controllerContext, partialViewName) ?? throw new FileNotFoundException("Partial view cannot be found."),
model
);
}
protected string ViewToString(string viewName, object model = null)
{
ControllerContext controllerContext = new ControllerContext(Request.RequestContext, this);
return ViewToString(
controllerContext,
ViewEngines.Engines.FindView(controllerContext, viewName, null) ?? throw new FileNotFoundException("View cannot be found."),
model
);
}
protected string ViewToString(string viewName, string controllerName, string areaName, object model = null)
{
RouteData routeData = new RouteData();
routeData.Values.Add("controller", controllerName);
if (areaName != null)
{
routeData.Values.Add("Area", areaName);
routeData.DataTokens["area"] = areaName;
}
ControllerContext controllerContext = new ControllerContext(HttpContext, routeData, this);
return ViewToString(
controllerContext,
ViewEngines.Engines.FindView(controllerContext, viewName, null) ?? throw new FileNotFoundException("View cannot be found."),
model
);
}
private string ViewToString(ControllerContext controllerContext, ViewEngineResult viewEngineResult, object model)
{
using (StringWriter writer = new StringWriter())
{
ViewContext viewContext = new ViewContext(
ControllerContext,
viewEngineResult.View,
new ViewDataDictionary(model),
new TempDataDictionary(),
writer
);
viewEngineResult.View.Render(viewContext, writer);
return writer.ToString();
}
}
}
Then, on your controller you can inherit from this interface like so:
public class ExampleController : IBaseController
{
}
Now, you can use the new methods to easily render your partial view to a string.
In your getDataBySelectedID action, this is what you'll want to do.
[HttpPost]
public JsonResult getDataBySelectedID(string selectedId)
{
var que = test.getHello(selectedId);
string partialViewString = PartialViewToString("MyPartialView", que);
return Json(partialViewString, JsonRequestBehavior.AllowGet);
}
You may need to modify the above statement to fit your uses, but it should get you close.
4) Inject Partial View into Page on AJAX Success
Now, we've setup a Partial View to handle the model data that we want to update. We've updated our view to load from the Partial View by default. We've implemented a controller interface that will let use render that Partial View to a string so that we can inject it into our page. Now, we just need to do that injection.
First, wrap the previous setup #Html.RenderPartial() statement in a div. Let's give it the ID partialViewDiv. This will let us easily target it with jQuery.
$(document).ready(function () {
$("#ID").on("change", function () {
var selectedId = this.value;
var url = "/Sample/getDataBySelectedID";
var $partialViewDiv = $('#partialViewDiv');
$.ajax({
method: "POST",
dataType: "json",
url: url,
data: {
selectedId: selectedId
}
})
.done(function (response, status, jqxhr) {
$partialViewDiv.html(response);
// Do any other updates here.
})
.fail(function (reponse, status, error) {
$partialViewDiv.html('');
// Handle your error here.
});
});
});
Again, these is mostly pseudo-code so you may have to make some modifications. But, at this point, you should be roughly where you need to be. The AJAX call should update your view by reloading the partial view with your new model data.
Tips
Loading a partial view like this may break some jQuery event handlers, depending on your application. If that happens, take a look at this.
You can return any string from the Controller with your AJAX call. You could, if needed, return different partial views than what you originally loaded. Just use the methods from the interface and you can render whatever you need.
The above code is only some general guidelines. Without knowing your full implementation, I can't provide 100% working, bug free code. But, post here if you have any issues and I'll try to help.
A solution i see is with php
location.reload();
That is how you reload but if you want to reload with data you could use something like
window.location.replace("PathToThePage.php?YourDataName=YourData");
I have a view with multiple sections. i would like to update sections individually using partial views and ajax.
I have this so far:
Controller:
[HttpPost]
public PartialViewResult userdetailssettings(UserDetails model)
{ .... }
View Html:
<div id="userDetailsPartial">
#Html.Partial("_user_details", Model.userdetails)
</div>
Partial Html:
<div class="form-group">
<div class="col-md-12">
#Html.TextBoxFor(x => x.Forename, new { #class = "form-control", placeholder = "Enter your forename" })
#Html.ValidationMessageFor(x => x.Forename)
</div>
</div>
<div class="form-group">
<div class="col-md-12">
#Html.TextBoxFor(x => x.Surname, new { #class = "form-control", placeholder = "Enter your surname" })
#Html.ValidationMessageFor(x => x.Surname)
</div>
</div>
Javascript on View:
var detailsUrl = "#Url.Action("userdetailssettings", "UserLogin")";
var detailsmodel = JSON.stringify(#Html.Raw(Json.Encode(#Model.userdetails)));
$(document).on('click touchstart', '#saveDetails', function () {
$.ajax({
type: "POST",
dataType: 'json',
data: detailsmodel,
url: detailsUrl,
contentType: "application/json"
}).done(function (res) {
$("#userDetailsPartial").html(res);
addresssearch();
});
});
The model is being passed to the controller by the ajax, however the values are not that of the inputs. They are the original values passed from the controller to open the view.
I have tried wrapping the partial in tags and also tried adding form tags inside the partial.
I have also tried putting this code:
var detailsUrl = "#Url.Action("userdetailssettings", "UserLogin")";
var detailsmodel = JSON.stringify(#Html.Raw(Json.Encode(#Model.userdetails)));
Inside the click function.
Nothing I do passes the updated values from the inputs.
I have thought of creating a new instance of the model from the inputs in the javascript i.e.
var detailsmodel = [ { Forename: $('#Forename').val(), Surname: $('#Surname').val() } ];
But if I am just creating json why can I not just convert the bound model to json.
why can I not just convert the bound model to json
This is because you are using MVC, not MVVM.
The "bound model" is one way from the controller to the view via the model; it's possible you're mixing the term "bound model" with "model" and "binding".
If you POST the form, you'll get the model in the Action (based on parameters of course), but if you pass via ajax, you'll need to get the current values from the form (as in your comment 'creating a new instance of the model from the inputs').
You can generate data to pass via AJAX in various ways, such as:
var data = $("form").serialize();
rather than adding every input manually.
var detailsmodel = JSON.stringify... is set when the view is generated and will not change automatically using MVC.
That's because the data you're passing is statically set at page load, based on #Html.Raw(Json.Encode(#Model.userdetails)).
You would need to use something like $form.serialize(), or otherwise create the post body from the actual fields on the page.