Knockout viewmodel: retrieving data from .Net Core controller - asp.net-core

How do I update the Knockout viewmodel with data from my Razor model? My Razor page is generated from a simple GET Action in a .NET Core MVC project. The simple view is:
#model Birder2.ViewModels.SalesOrderViewModel
#using Newtonsoft.Json
#{ string data = JsonConvert.SerializeObject(Model); }
<script src="~/js/salesorderviewmodel.js"></script>
<script type='text/javascript' src="~/js/knockout-3.4.2.js"></script>
<script type='text/javascript'>
var salesOrderViewModel = new SalesOrderViewModel(#Html.Raw(data));
ko.applyBindings(salesOrderViewModel);
</script>
<p data-bind="text: MessageToClient"></p>
<div>
<div>
<label>Customer Name</label>
<span data-bind="text: CustomerName"></span>
</div>
<div>
<label>PO No.</label>
<span data-bind="text: PONumber"></span>
</div>
I have the following Knockout viewmodel, which is referenced above:
SalesOrderItemViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, {}, self);
The controller is here:
public async Task<IActionResult> Details(int? id)
SalesOrderViewModel salesOrderViewModel = new SalesOrderViewModel()
{
SalesOrderId = salesOrder.SalesOrderId,
CustomerName = salesOrder.CustomerName,
PONumber = salesOrder.PONumber
};
salesOrderViewModel.MessageToClient = "I originated from the viewmodel, rather than the model.";
return View(salesOrderViewModel);
I am very new to Knockout. Unfortunately I cannot find any effective documentation specific to .NET Core. The problem seems to be updating the Knockout viewmodel. The view is rendered with empty data fields.
Can anyone help identify the issue?

Everything looks correct. The only thing you should try is to place your script with ko.applyBindings(salesOrderViewModel) below your html with databinds
Alternatively you can use window.onload:
window.onload = function() {
var salesOrderViewModel = new SalesOrderViewModel(#Html.Raw(data));
ko.applyBindings(salesOrderViewModel);
};

Related

Load partial view through controller on Search button click

I am working on an ASP.NET Core 2.1 MVC app using razor. I have searchQuery.cshtml and a (individually working perfectly) viewQuery.cshtml pages. In my searchQuery page, I let user enter queryId and on clicking "Search" button I want to run the action of ViewQuery that displays the results in viewQuery.cshtml and show the viewQuery below the search button area.
I am not good working with Ajax or so. On Search btn click, I call the viewQuery Get action thru ajax. In the button click, I pass the entered queryId of type int. But, when I load searchQuery page, it throws null exception for passing the queryId. I searched few hous, but didn't get any solution.
searchQuery.cshtml UPDATED
<div>
<div class="col-md-6">
<dl class="dl-horizontal">
<dt>
#Html.DisplayNameFor(model => model.QueryId)
</dt>
<dd>
<input asp-for="QueryId" class="form-control" />
</dd>
</dl>
</div>
<input type="submit" value="Show" />
<!-- CHANGE IN CALL -->
Search
</div>
<div class="modal fade" id="myModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
×
<h3 class="modal-title">Query Answer</h3>
</div>
<div class="modal-body" id="myModalBodyDiv">
</div>
<div class="modal-footer">
Ok
</div>
</div>
</div>
</div>
<script>
function ShowResult() {
// Retrieve queryId
var queryId = $("#QueryId").val();
// DisplayS PROPERLY
alert("Entered ID " + queryId);
// TRIED '/query/viewQuery' ALSO
$.ajax({
type: 'GET',
url: '../query/viewQuery',
data: { queryId: queryId },
success: function (response) {
alert(response); // **DISPLAYS [Object: object]**
$("#myModalBodyDiv").html(response);
$('#myModal').modal("show");
}, error: function (response) {
alert("Error: " + response);
}
});
}
</script>
My ViewQuery action in controller UPDATED
[Route("[controller]/viewQuery/{queryId:int}")]
public async Task<IActionResult> ViewQuery(int queryId)
{
// Retrieve Data from api using HttpClient
....
return PartialView("ViewQuery", qVM); // PartialView(qVM); //View(qVM);
}
Search Query Action UPDATED
[Route("searchQuery")] // customer/searchQuery
public IActionResult SearchQuery()
{
return View();
}
Can anyone please help me how do I achieve my goal. Simple - a text box were user enters queryId. A button on click, want to pass the entered queryId, call a GET action on controller and get the response. Finally show the response below the search button. I was just trying with the above modal dialog, I prefer text and not dialog.
Try & isolate the issue.
Instead of using model.QueryId in the searchQuery.cshtml, simply hardcode any reference to "modelid" - that way at least you are eliminating the possibility that Model is null on that page. Then instead of onclick="ShowResult(#Model.QueryId)"> , hard code some known id instead of #Model.QueryId. Then debug to see if your ViewQuery action method id hit. If the method is hit, then you can take it from there.
Also, I noticed that your jquery calls may need to be modified:
Instead of: $('myModalBodyDiv').html(response); it should probably be $('#myModalBodyDiv').html(response); (the "#" is missing ..) - same for $('myModal').
You can use Partial Pages(ViewQuery page) , in your searchQuery page , you could use Ajax to call server side action with parameter ID . On server side , you can query the database with ID and return PartialView with models :
[HttpPost]
public IActionResult Students (StudentFilter filters)
{
List students = Student.GetStudents(filters);
return PartialView("_Students", students);
}
Then in success callback function of Ajax , you can load the html of partial view to related area in page using jQuery :
success: function (result) {
$("#searchResultsGrid").html(result);
}
You can click here and here for code sample if using MVC template . And here is code sample if using Razor Pages .

In Asp.Net Core, how can I get the multipart/form-data from the body?

In Asp.Net Core, it appears that they have done away with the Request.Content.ReadAsMultipartAsync functionality in favor of the IFormFile.
This makes uploading where you have an actual file a LOT easier, however, I have a use case where I need to upload a file to browser memory, process it, then send it as part of the multi-form data in the body. IFormFile cannot see this as there is no actual file to read. It only works if you have a filename property on the Content-Disposition and an actual file on the client to upload.
In my Asp.Net 4 app, I could read the mutlipart data in the body whether that was sent between boundaries or as an attached file.
How do I accomplish this in .Net Core?
What I figured out is that the multipart values are passed into the HttpRequest.Form as an array of key/value pairs. The "name" value on the body's multipart determines the name of the key.
I created a helper method that grabs both files and form values.
public static List<FileModel> GetFileModelsFromRequest(HttpRequest request)
{
var fileModels = new FileModels();
foreach (var formField in request.Form)
{
// Form data
var fileModelText = formField.Value;
... process and add to the FileModel list
}
if (request.Form.Files != null && request.Form.Files.Count > 0)
{
foreach (var file in request.Form.Files)
{
using (MemoryStream ms = new MemoryStream())
{
// File data
formFile.CopyTo(ms);
}
... process and add to the FileModel list
}
}
return fileModels;
}
I have done it this way. when I had to capture image from webcam and process (show that image in browser) it in browser memory and later on post that image using a form.
public IActionResult Index()
{
var files = HttpContext.Request.Form.Files;
if (files != null)
{
foreach (var file in files)
{
var fileName = file.Name;
}
}
return View();
}
I used a JS library Webcam.js to capture image from webcam and show that image on the same page. and once a user is satisfied with the image, s/he can upload the image to the server.
<!-- Configure settings and attach camera -->
<script language="JavaScript">
Webcam.set({
width: 320,
height: 240,
image_format: 'jpeg',
jpeg_quality: 90
});
Webcam.attach('#camera');
</script>
<!-- handle snapshot and displaying it locally -->
<script language="JavaScript">
function take_snapshot() {
// take snapshot and get image data
Webcam.snap(function (data_uri) {
// display results in page
document.getElementById('imageResults').innerHTML =
'<img src="' +
data_uri +
'"/>';
Webcam.upload(data_uri,
'/Default/Index',
function (code, text) {
console.log('Photo Captured');
});
});
}
</script>
<div class="panel panel-default">
<div class="panel-heading">Camera</div>
<div class="panel-body">
<div id="camera"></div>
<!-- A button for taking snaps -->
<form>
<input type="button" class="btn btn-success" value="Take Snapshot" onClick="take_snapshot()">
</form>
<div class="panel panel-default">
<div class="panel-heading">Captured Image</div>
<div class="panel-body">
<div id="imageResults">captured image will appear here...</div>
</div>
<br />
<br />
</div>
let me know if this is what you are looking for.

MVC PartialView not rendered when passing strongly typed model

I'm calling a controller method using AJAX request.
This function used to return a partial view so I will load it in an HTML element.
the function:
public PartialViewResult LoadLockTimerEnd()
{
Session["Info"] = new Request();
RequestReply reqRep = new RequestReply("/Home/Index", "ID missing. Reseting");
return PartialView("FailurePartialView", reqRep);
}
When passing a simple string as model to this PartialView it works fine, but when passing a RequestReply object as model it is not working and the partialView is not loaded at all.
The PatialView:
#model EPS_WEB_SITE.Models.RequestReply;
#{
Layout = "~/Views/Shared/_FailureLayout.cshtml";
}
<strong>#Html.Raw(#Model.Message.ToString())</strong>
<div class="buttons-container button-container-small">
<div data-request-url="#Model.RedirectURL.ToString()">
<button type="button" id="dismiss-failure-btn" class="btn btn-danger dismiss">Dismiss</button>
</div>
</div>
The AJAX call:
$.get('/Home/LoadLockTimerEnd', function (data) {
$("#resultDiv").html(data);
});
Why does the PartialView works with string as model and not class as model?
$.ajax({
dataType: "HTML",
url: '/Home/LoadLockTimerEnd',
success: function (data) {
$("#resultDiv").html(data);
}
});
Try to call your Action using this way
OK so I found the problem:
It was a compilation error.
I needed to remove ; in the model binding in the view
#model EPS_WEB_SITE.Models.RequestReply;
I was able to find that in the network tab on Chrome browser.
Double click on the problematic request and it shown the server error.
Hope it will help someone

How to access data in partialview loaded using Ajax

I'm currently building a website where I have to update two separate targets from a single Ajax.BeginForm. I got it working by using an additional container to container the two separate targets. As in:
Original Form
#model Mod1
#using (Ajax.BeginForm("LoadData", new AjaxOptions{UpdateTargetID = "Div1"}))
{
<select id="sel1" name="sel1" onchange="$(this.form).submit">
// ...
</select>
}
#using (Ajax.BeginForm("ProcessData", new AjaxOptions{UpdateTargetID = "Div2"}))
{
<div id="Div1"></div>
// ...
<input type="submit" value="GO!" />
}
Code File
public ActionResult LoadData(int sel1)
{
// loading data from database
return PartialView(mod1);
}
Partial View
#model Mod2
<select id="sel2" name="sel2">
#foreach (var item in Model.SelectItems)
{
<option value="#item.Value">#item.Text</option>
}
</select>
#foreach (var item in Model.CheckBoxItems)
{
<label>#item.Text<input type="checkbox" id="chk1" name="chk1" value="#item.Value"></label>
}
For the processing method, I have tried:
public ProcessData(Mod1 mod1, string[] chk1, int sel2)
However I am unable to retrieve the values for either chk1 or sel2 upon form submission. examination of chk1 and sel2 in Debug mode, chk1 is null while sel2 is 0 (no such value in the select options). Can anyone please offer some insight into the reason for this and also how I can go about solving it. Thank you in advance.
If I understand you correctly you can do what you want y having two submit buttons on the same form, each calling a separate action method. That way each submit button will have access to all the fields in the form. For a detailed explanation of how you can do that see my answer here:
How to use ajax link instead of submit button for form?
Edit
In response to comment: the action method LoadData should return a partial view that contains the other two controls and have the whole begin form included in it:
#using (Ajax.BeginForm("LoadData", new AjaxOptions{
UpdateTargetID = "Div1",
InsertionMode = InsertionMode.Replace
}))
{
<select id="sel1" name="sel1" onchange="$(this.form).submit">
// ...
</select>
}
<div id="Div1">
</div>
<div id="Div2">
</div>
Move this to another partial view:
#using (Ajax.BeginForm("ProcessData", new AjaxOptions{UpdateTargetID = "Div2"}))
{
// ...
<input type="submit" value="GO!" />
}

ASP.NET MVC How to Use two Actionresults with Html.BeginForm?

I'm trying to do the same as this ASP.NET MVC Using two inputs with Html.BeginForm question describes but with enough difference that I don't really know hwo to apply it on my project:
I have a view that has 3 dropdownlists(profilelist, connected salarylist & not connected salarylist)
Looks like this:
<div class="row bgwhite">
#using (Html.BeginForm("GetConnectedSalaries", "KumaAdmin", FormMethod.Get, new { Id = "ProfileListForm" }))
{
<div class="four columns list list1">
#Html.DropDownList("Profiles", (SelectList) ViewBag.Profiles, "--Välj profilgrupp--",
new
{
//onchange = "$('#ProfileListForm')[0].submit();"
// Submits everytime a new element in the list is chosen
onchange = "document.getElementById('ProfileListForm').submit();"
})
</div>
}
#using (Html.BeginForm("Index", "KumaAdmin", FormMethod.Get, new { Id = "SalaryListForm" }))
{
<div class="four columns list list2" style="margin-top:-19px;">
#Html.DropDownList("Salaries", (SelectList) ViewBag.Salaries, "--Kopplade LöneGrupper--")
</div>
}
#using (Html.BeginForm("GetNOTConnectedSalaries", "KumaAdmin", FormMethod.Get, new { Id = "NotConSalaryListForm" }))
{
<div class="four columns list list2" style="margin-top:-19px;">
#Html.DropDownList("Salaries", (SelectList)ViewBag.NotConSalaries, "--Ej Kopplade LöneGrupper--")
<input style="float: left;" type="submit" value="Knyt" />
</div>
}
</div>
as you can see above when i change an element i the profile list i have script code that submits the form and calls the following actionresult that populates my "connected salarylist".
[HttpGet]
public ActionResult GetConnectedSalaries(int Profiles = -1)
{
Model.SalaryGroups = AdminManager.GetConnectedSalaries(Profiles);
ViewBag.Salaries = new SelectList(Model.SalaryGroups, "Id", "SalaryName", "Description");
return (Index());
}
What I wan't to do:
When I chose a element in the profilelist i would like to call 2 actionresults, the one that i have shown above AND a second one that will populare my third list that will contain "not connected salaries".
Second Actionresult:
public ActionResult GetNOTConnectedSalaries(int Profiles = -1)
{
Model.SalaryGroups = AdminManager.GetNOTConnectedSalaries(Profiles);
ViewBag.NotConSalaries = new SelectList(Model.NotConSalaryGroups, "Id", "SalaryName", "Description");
return (Index());
}
I don't want to do this with AJAX/JSON, strictly MVC.
I read the question that i linked above but did not know how to apply it to my project or if it is even possible to do the same.
If more info is needed ask and i will do my best to provide it.
Thank you!
I was so sure that the best way to do this was to have two actionresults that i was totaly blinded to the soloution that i could call both my db methods from the same actionresult and populate both of the lists.
Simple soloution:
[HttpGet]
public ActionResult GetSalaries(int Profiles = -1)
{
Model.SalaryGroups = AdminManager.GetConnectedSalaries(Profiles);
ViewBag.Salaries = new SelectList(Model.SalaryGroups, "Id", "SalaryName", "Description");
Model.NotConSalaryGroups = AdminManager.GetNOTConnectedSalaries(Profiles);
ViewBag.NotConSalaries = new SelectList(Model.NotConSalaryGroups, "Id", "SalaryName", "Description");
return (Index());
}
Sorry if I wasted your time:( but hopefully this will help others that attempt the same.
However if there is a way to do this in two actionresults then I will leave the question as open, would be interesting to see how it is done.