How to connect ajax jQuery result to C# variable - javascript

I'm new to C#, JS, jQuery, etc.
For a schoolproject I'm building a cinema-website with ASP.NET and C#.
I have come to the point that I think I'm close to the answer, but I can't figure it out.
What is the problem?
At the Index-page of the website the customer can pick a movie to go to the next screen to buy the tickets. There he needs to pick a moment of the movie from a dropdownmenu. The dropdownmenu-code looks like this:
<div class="form-group" style="width: 30%">
#Html.LabelFor(m => Model.Showtime, new { #class = "control-label col-md-2" })
<div>
#Html.DropDownListFor(m => Model.Showtime!.StartAt, new SelectList(Model.ShowList, "Value", "Text"), "Select show", new { #class = "form-control", id = "showList" })
</div>
</div>
I've created a jQuery function that gets the picked Showtime from the dropdownmenu.
This function looks like this:
<script>
$("#showList").change(function() {
var selectedVal = $(this).val();
$.ajax({
type: 'POST',
dataType: 'JSON',
url: '/Orders/GetPickedShowtime/',
data: {showTimeId: selectedVal},
success:
function(response) {
response = JSON.stringify(response)
$('#testShowtime').text(response)
},
error:
function(response) {
console.log("ERROR: ", response)
}
});
})
</script>
In the controller I wrote a function to get the picked Showtime object from the database:
public ActionResult<Showtime> GetPickedShowtime(int showTimeId) {
var show = _context.Showtime!
.Where(s => s.Id == showTimeId);
return Json(show);
}
I used this code: $('#testShowtime').text(response) in my jQuery to test the function in my View with: <h4 id="testShowtime"></h4>. It works. It gives a Array back to the View, like:
[{"id":6987,"startAt":"2022-03-13T18:00:00Z","hallId":3,"movieId":1,"hall":null,"movie":null}]
GREAT! The function seems to work. But what I want to do now is to get the Object from this Json-string into a C# variable. If the customer selects a showtime and some tickets, a button brings them to the next page where the seatselection begins. So the variable with the picked Showtime needs to go there by clicking a button.... And now I'm stuck.
Could someone help me out of this puzzle? I'll buy you a beer :P

You can use Session variable to store your StoreTime on the server itself. AJAX is used to update a portion of the page without reloading the entire page and in your case, you are unnecessarily complicating things. You can do the following:
In your method, define a Session variable which is always updated when the user selects from the dropdown:
public ActionResult<Showtime> GetPickedShowtime(int showTimeId) {
var show = _context.Showtime!.Where(s => s.Id == showTimeId);
if(show != null)
{
Session["myCurrentShow"] = show;
}
return Json(show);
}
Now once your Session is defined, you can access it on your next ActionMethod. I am giving an example how you would use this Session variable on your last step:
public ActionResult ConfirmShowTime() {
if(Session["myCurrentShow"] != null)
{
Showtime showtime = new Showtime();
//Cast your session here to get the values
showtime = (Showtime)Session["myCurrentShow"];
}
// Your logic
return View();
}

Related

How do I populate a list field in a model from javascript?

I have a Kendo.MVC project. The view has a model with a field of type List<>. I want to populate the List from a Javascript function. I've tried several ways, but can't get it working. Can someone explain what I'm doing wrong?
So here is my model:
public class Dashboard
{
public List<Note> ListNotes { get; set; }
}
I use the ListNotes on the view like this:
foreach (Note note in Model.ListNotes)
{
#Html.Raw(note.NoteText)
}
This works if I populate Model.ListNotes in the controller when the view starts...
public ActionResult DashBoard(string xsr, string vst)
{
var notes = rep.GetNotesByCompanyID(user.ResID, 7, 7);
List<Koorsen.Models.Note> listNotes = new List<Koorsen.Models.Note>();
Dashboard employee = new Dashboard
{
ResID = intUser,
Type = intType,
FirstName = user.FirstName,
LastName = user.LastName,
ListNotes = listNotes
};
return View(employee);
}
... but I need to populate ListNotes in a Javascript after a user action.
Here is my javascript to make an ajax call to populate ListNotes:
function getReminders(e)
{
var userID = '#ViewBag.CurrUser';
$.ajax({
url: "/api/WoApi/GetReminders/" + userID,
dataType: "json",
type: "GET",
success: function (notes)
{
// Need to assign notes to Model.ListNotes here
}
});
}
Here's the method it calls with the ajax call. I've confirmed ListNotes does have the values I want; it is not empty.
public List<Koorsen.Models.Note> GetReminders(int id)
{
var notes = rep.GetNotesByCompanyID(id, 7, 7);
List<Koorsen.Models.Note> listNotes = new List<Koorsen.Models.Note>();
foreach (Koorsen.OpenAccess.Note note in notes)
{
Koorsen.Models.Note newNote = new Koorsen.Models.Note()
{
NoteID = note.NoteID,
CompanyID = note.CompanyID,
LocationID = note.LocationID,
NoteText = note.NoteText,
NoteType = note.NoteType,
InternalNote = note.InternalNote,
NoteDate = note.NoteDate,
Active = note.Active,
AddBy = note.AddBy,
AddDate = note.AddDate,
ModBy = note.ModBy,
ModDate = note.ModDate
};
listNotes.Add(newNote);
}
return listNotes;
}
If ListNotes was a string, I would have added a hidden field and populated it in Javascript. But that didn't work for ListNotes. I didn't get an error, but the text on the screen didn't change.
#Html.HiddenFor(x => x.ListNotes)
...
...
$("#ListNotes").val(notes);
I also tried
#Model.ListNotes = notes; // This threw an unterminated template literal error
document.getElementById('ListNotes').value = notes;
I've even tried refreshing the page after assigning the value:
window.location.reload();
and refreshing the panel bar the code is in
var panelBar = $("#IntroPanelBar").data("kendoPanelBar");
panelBar.reload();
Can someone explain how to get this to work?
I don't know if this will cloud the issue, but the reason I need to populate the model in javascript with an ajax call is because Model.ListNotes is being used in a Kendo Panel Bar control and I don't want Model.ListNotes to have a value until the user expands the panel bar.
Here's the code for the panel bar:
#{
#(Html.Kendo().PanelBar().Name("IntroPanelBar")
.Items(items =>
{
items
.Add()
.Text("View Important Notes and Messages")
.Expanded(false)
.Content(
#<text>
#RenderReminders()
</text>
);
}
)
.Events(e => e
.Expand("getReminders")
)
)
}
Here's the helper than renders the contents:
#helper RenderReminders()
{
if (Model.ListNotes.Count <= 0)
{
#Html.Raw("No Current Messages");
}
else
{
foreach (Note note in Model.ListNotes)
{
#Html.Raw(note.NoteText)
<br />
}
}
}
The panel bar and the helpers work fine if I populate Model.ListNotes in the controller and pass Model to the view. I just can't get it to populate in the javascript after the user expands the panel bar.
Perhaps this will do it for you. I will provide a small working example I believe you can easily extend to meet your needs. I would recommend writing the html by hand instead of using the helper methods such as #html.raw since #html.raw is just a tool to generate html in the end anyways. You can write html manually accomplish what the helper methods do anyway and I think it will be easier for you in this situation. If you write the html correctly it should bind to the model correctly (which means it won't be empty on your post request model) So if you modify that html using javascript correctly, it will bind to your model correctly as well.
Take a look at some of these examples to get a better idea of what I am talking about:
http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
So to answer your question...
You could build a hidden container to hold your list values like this (make sure this container is inside the form):
<div id="ListValues" style="display:none">
</div>
Then put the results your ajax post into a javascript variable (not shown).
Then in javascript do something like this:
$('form').off('submit'); //i do this to prevent duplicate bindings depending on how this page may be rendered futuristically as a safety precaution.
$('form').on('submit', function (e) { //on submit, modify the form data to include the information you want inside of your ListNotes
var data = getAjaxResults(); //data represents your ajax results. You can acquire and format that how you'd like I will use the following as an example format for how you could save the results as JSON data: [{NoteID ="1",CompanyID ="2"}]
let listLength = data.length;
for (let i = 0; i < listLength; i++) {
$('#ListValues').append('<input type="text" name="ListNotes['+i+'].NoteID " value="' + data.NoteID +'" />')
$('#ListValues').append('<input type="text" name="ListNotes['+i+'].CompanyID " value="' + data.CompanyID +'" />')
//for your ajax results, do this for each field on the note object
}
})
That should do it! After you submit your form, it should automatically model bind to you ListNotes! You will be able to inpsect this in your debugger on your post controller action.

Invoking a ViewComponent within another ViewComponent

I am currently coding within a ViewComponent (ViewComponent1) view. Within this View, I have listed a few items:
As you can see, the channels 11, 12, 13 and 14 are clickable. Each channel has some additional information (OBIS, avtalsid.. etc). What I´m trying to do is to invoke ViewComponent2, within ViewComponent1, and pass along some of the data, based on the clicked item.
What I tried to do is to create another View called "Test" and within that View invoke ViewComponent2 along with its parameters, like this:
<div class="row">
<div class="col-md-2 canalstyle">
<a asp-controller="Customer" asp-action="Test" asp-route-pod="#item.STATION"
asp-route-idnr="#item.IDNR" asp-route-kanal="#item.KANAL" asp-route-start="#Model.start"
asp-route-end="#Model.end"> #Html.DisplayFor(modelItem => item.KANAL)</a>
</div>
</div>
This works, but this method redirects me away from my current View (ViewComponent 1). I don't want that. I want the current view to load the additional information from ViewComponent2.
My function that runs the ajax:
function myFunction() {
var data = JSON.stringify({
'idnr': id,
'start': this.start,
'end': this.end
});
$.ajax({
url: '#Url.Action("Test2","Customer")',
type: 'GET',
data: { idnr: id, start: this.start, end: this.end },
contentType: 'application/json',
success: handleData(data)
})
};
function handleData(data) {
alert(data);
var url = $(this).attr("href");
var $target = $(this).closest("div").find(".details");
$.get(url, function (res) {
$target.html(res);
});
//do some stuff
}
And my Test2 Action:
public async Task<IActionResult> Test2(string idnr, string start, string end)
{
ServiceClient r2s = new R2S.ServiceClient();
R2S.Konstant[] kData = r2s.GetKonstantListAsync(new string[] { "IDNR" }, new string[] { idnr}).Result; // mätarnummer in... --> alla konstanter kopplade till denna.
return ViewComponent("MeterReader2", new { k = kData[0], start = start, end = end });
}
I am trying to target the same DOM.. Any ideas?
Your current code is rendering links (a tags) and normally clicking on a link will do a new GET request, which is what you are seeing , the redirect to the new action method.
If you do not want the redirect, but want to show the result of the second view component in same view, you should use ajax.
For example, If you want to show the result of second view component just below each link, you may add another html element for that. Here i am adding an empty div.
<div class="row">
<div class="col-md-2 canalstyle">
<a class="myClass" asp-controller="Customer" asp-action="DetailsVc"
asp-route-id="#item.Id" > #item.KANAL</a>
<div class="details"></div>
</div>
</div>
Here i just removed all those route params you had in your orignal question and replaced only with on param (id) . Assuming your items will have an Id property which is the unique id for the record(primary key) and using which you can get the entity (from a database or so) in your view component to get the details.
This will generate the link with css class myClass. You can see that, i used asp-action attribute value as "DetailsVc". We cannot directly use the view component name in the link tag helper as attribute value to generate the href value. So we should create a wrapper action method which returns your view component result such as below
public IActionResult DetailsVc(int id)
{
return ViewComponent("DetailsComponent", new { id =id });
}
Assuming your second view components name is DetailsComponent and it accepts an id param. Update the parameter list of this action method and view component as needed. (but i suggest passing just the unique Id value and get details in the server code again)
Now all you have to do is have some javascript code which listen to the click event on those a tags and prevent the normal behavior (redirect) and make an ajax call instead, use the ajax call result to update the details div next to the clicked link.
You can put this code in your main view (or in an external js file without the #section part)
#section Scripts
{
<script>
$(function() {
$("a.myClass").click(function(e) {
e.preventDefault();
var url = $(this).attr("href");
var $target = $(this).closest("div").find(".details");
$.get(url,function(res) {
$target.html(res);
});
});
});
</script>
}

Not able to refresh the partial view after a form submission from another partial view within it, using ajax in mvc razor

In my website I have a Facebook like chat page. I have implemented it with basic form submission method that will refresh the whole page when I post something. now I need to change it using ajax/jquery so that it should only refresh my partial views. I have written code for that and I changed my views by adding scripts.
My main Message view is like (sample):
#model myModel
<h2>
Message Board<small class="on-right"/>
</h2>
// Text box for Adding Message(post)
#Html.TextArea("Message", new { #placeholder = "Add a post", id = "Message" })
<input type="button" id="Post" value="Post"/>
// partial view that displays all the messages along with its comments
<div id="messagelist">
#{
Html.RenderPartial("_posts", Model.MessageList);
}
</div>
script for message page:
$('#Post').click(function () {
var url = "/MyController/Messages";
var Message = $("#Message").val();
$("#Message").val("");
$.post(url, { Message: Message }, function (data) {
$("#messagelist").html(data);
_post partial view:
#model IEnumerable<Model.MessageList>
//Foreach loop for displaying all the messages
#foreach (var item in Model)
{
<div >
#Html.DisplayFor(model => item.UserName)
#Html.DisplayFor(model => item.MessageText)
//Foreach loop for displaying all the comments related to each message
#foreach (var item1 in item.Comments)
{
#item1.UserName
#item1.MessageText
}
</div>
//partial view for adding comments each for messages
#Html.Partial("Comment", new ModelInstance { MessageId = item.MessageId })
}
Comment partial view (I am using ajax form submit):
#model ModelInstance
//form for submitting a message instance with parent message id for adding a comment to the parent message
#using (Ajax.BeginForm("Comment", "MyController", new AjaxOptions { UpdateTargetId = "messagelist" }))
{
#Html.AntiForgeryToken() #Html.ValidationSummary(true)
<div>
#Html.HiddenFor(modelItem => Model.MessageId)
#Html.TextBoxFor(modelItem => Model.CommentText, new { #placeholder = "leave a comment" })
<button class="btn-file" type="submit"></button>
</div>
}
Controller actions (sample):
public ActionResult Messages(string Message)
{
------------------------------
create messag object
---------------------
add to database
-------------------
fetch again for refreshing
------------------------
return PartialView("_posts", refreshed list);
}
public ActionResult Comment(StudiMessageDetails Comment)
{
------------------------------
create messag object
---------------------
add to database
-------------------
fetch again for refreshing
return PartialView("_posts", msgDetails);
}
Now the Posting message and posting comment is working perfectly. also when I post a message it only refreshes my main message view.
But when I post a comment it is giving me the refreshed partial view only. it is not getting bound to the 'div id=messagelist' and not giving me the full page. Can anybody tell where I am going wrong ? please help.
Your Ajax.BeginForm() is replacing the contents of <div id="messagelist"> with the html from return PartialView("_posts", msgDetails);. I suspect the model msgDetails contains only the details of the comment's associated message so that's all your seeing.
I suggest to rethink your design, particularly the Messages() method, which after saving the message is calling the database to get all messages and returning the complete list - you already have all the data on the page so this seems completely unnecessary and just degrades performance. You could simplify it with the following
Partial view (note the partial is for one message, not a collection)
#model Model.Message
<div class="message">
<div class=username">#Model.UserName</div>
<div class=messagetext">#Model.MessageText</div>
<div class="commentlist">
#foreach (var comment in Model.Comments)
{
<div class="comment">
<div class="username">#comment.UserName<div>
<div class="commenttext">#comment.MessageText<div>
</div>
}
</div>
<div>
#Html.TextBox("CommentText", new { placeholder = "Leave a comment", id = "" }) // remove the id attribute so its not invalid html
<button class="addcomment" data-id="#Model.MessageId">Add Comment</button>
</div>
</div>
Main View
#model myModel
...
#Html.TextArea("Message", new { placeholder = "Add a post" }) // no point adding the id - its already added for you
<input type="button" id="Post" value="Post" />
<div id="messagelist">
#foreach(var message in Model.MessageList)
{
#{ Html.RenderPartial("_posts", message); }
}
</div>
<div id="newmessage"> // style this as hidden
#{ Html.RenderPartial("_posts"); }
</div>
Scripts
// Add a model or ViewBag property for the current user name
var userName = JSON.parse('#Html.Raw(Json.Encode(ViewBag.UserName))');
$('#Post').click(function () {
var url = '#Url.Action("Message", "MyController")'; // dont hardcode!
var message = $('#Message').val();
$.post(url, { MessageText: message }, function (data) {
if(data) {
// clone the new message, update message id and add to DOM
var html = $('#newmessage').clone();
message.children('.username').text(userName);
message.children('.messagetext').text(message);
message.children('.newcomment').children('button').data('id', data);
$('#messagelist').perpend(html); // add at top of list
$('#Message').val(''); // clear message text
} else {
// something went wrong
}
});
});
$('.addcomment').click(function() {
var url = '#Url.Action("Comment", "MyController")';
var comment = $(this).prev('input');
var messageID = $(this).data('id');
var list = $(this).closest('.message').children('.commentlist');
$.post(url, { MessageID: messageID, CommentText comment.val() }, function (data) {
if (data) {
// add message
var html = $('<div><div>').addClass('comment');
html.append($('<div><div>').addClass('username').text(userName));
html.append($('<div><div>').addClass('commenttext').text(commentText));
list.append(html); // add to end of existing comments
comment.val(''); // clear existing text
} else {
// something went wrong
}
});
});
Controller
[HttpPost]
public ActionResult Message(string MessageText)
{
// create new message object and save to database
int ID = // the ID of the new message
return Json(ID); // if exception, then return null;
}
[HttpPost]
public ActionResult Comment(int MessageID, string CommentText)
{
// create new comment object and save to database
return Json(true); // if exception, then return null;
}

Having issues tying together basic javascript chat page

I have the skeleton of a chat page but am having issues tying it all together. What I'm trying to do is have messages sent to the server whenever the user clicks send, and also, for the messages shown to update every 3 seconds. Any insights, tips, or general comments would be much appreciated.
Issues right now:
When I fetch, I append the <ul class="messages"></ul> but don't want to reappend messages I've already fetched.
Make sure my chatSend is working correctly but if I run chatSend, then chatFetch, I don't retrieve the message I sent.
var input1 = document.getElementById('input1'), sendbutton = document.getElementById('sendbutton');
function IsEmpty(){
if (input1.value){
sendbutton.removeAttribute('disabled');
} else {
sendbutton.setAttribute('disabled', '');
}
}
input1.onkeyup = IsEmpty;
function chatFetch(){
$.ajax({
url: "https://api.parse.com/1/classes/chats",
dataType: "json",
method: "GET",
success: function(data){
$(".messages").clear();
for(var key in data) {
for(var i in data[key]){
console.log(data[key][i])
$(".messages").append("<li>"+data[key][i].text+"</li>");
}
}
}
})
}
function chatSend(){
$.ajax({
type: "POST",
url: "https://api.parse.com/1/classes/chats",
data: JSON.stringify({text: $('input1.draft').val()}),
success:function(message){
}
})
}
chatFetch();
$("#sendbutton").on('click',chatSend());
This seems like a pretty good project for Knockout.js, especially if you want to make sure you're not re-appending messages you've already sent. Since the library was meant in no small part for that sort of thing, I think it would make sense to leverage it to its full potential. So let's say that your API already takes care of limiting how many messages have come back, searching for the right messages, etc., and focus strictly on the UI. We can start with our Javascript view model of a chat message...
function IM(msg) {
var self = this;
self.username = ko.observable();
self.message = ko.observable();
self.timestamp = ko.observable();
}
This is taking a few liberties and assuming that you get back an IM object which has the name of the user sending the message, and the content, as well as a timestamp for the message. Probably not too far fetched to hope you have access to these data elements, right? Moving on to the large view model encapsulating your IMs...
function vm() {
var self = this;
self.messages = ko.observableArray([]);
self.message = ko.observable(new IM());
self.setup = function () {
self.chatFetch();
self.message().username([user current username] || '');
};
self.chatFetch = function () {
$.getJSON("https://api.parse.com/1/classes/chats", function(results){
for(var key in data) {
// parse your incoming data to get whatever elements you
// can matching the IM view model here then assign it as
// per these examples as closely as possible
var im = new IM();
im.username(data[key][i].username || '');
im.message(data[key][i].message || '');
im.timestamp(data[key][i].message || '');
// the ([JSON data] || '') defaults the property to an
// empty strings so it fails gracefully when no data is
// available to assign to it
self.messages.push(im);
}
});
};
}
All right, so we have out Javascript models which will update the screen via bindings (more on that in a bit) and we're getting and populating data. But how do we update and send IMs? Well, remember that self.message object? We get to use it now.
function vm() {
// ... our setup and initial get code
self.chatSend = function () {
var data = {
'user': self.message().username(),
'text': self.message().message(),
'time': new Date()
};
$.post("https://api.parse.com/1/classes/chats", data, function(result) {
// do whatever you want with the results, if anything
});
// now we update our current messages and load new ones
self.chatFetch();
};
}
All right, so how do we keep track of all of this? Through the magic of bindings. Well, it's not magic, it's pretty intense Javascript inside Knockout.js that listens for changes and the updates the elements accordingly, but you don't have to worry about that. You can just worry about your HTML which should look like this...
<div id="chat">
<ul data-bind="foreach: messages">
<li>
<span data-bind="text: username"></span> :
<span data-bind="text: message"></span> [
<span data-bind="text: timestamp"></span> ]
</li>
</ul>
</div>
<div id="chatInput">
<input data-bind="value: message" type="text" placeholder="message..." />
<button data-bind="click: $root.chatSend()">Send</button>
<div>
Now for the final step to populate your bindings and keep them updated, is to call your view model and its methods...
$(document).ready(function () {
var imVM = new vm();
// perform your initial search and setup
imVM.setup();
// apply the bindings and hook it all together
ko.applyBindings(imVM.messages, $('#chat')[0]);
ko.applyBindings(imVM.message, $('#chatInput')[0]);
// and now update the form every three seconds
setInterval(function() { imVM.chatFetch(); }, 3000);
});
So this should give you a pretty decent start on a chat system in an HTML page. I'll leave the validation, styling, and prettifying as an exercise to the programmer...

Dynamically updating html.listBox in MVC 1.0?

The client will choose an item from a dropDown list, this newly selected value will then be used to find assets linked to that selected item, these assets will then be loaded into the listBox.
This sounds simple enough, and I'm aware I could use a partial View but it seems overkill for just updating one component on a form.
Any
I've done this in MVC 1.0 myself. I used an onchange on the first drop down which called an action using the value selected. That action returned a JSON result. The jQuery script which called that action then used the JSON to fill the second drop down list.
Is that enough explanation, or would you like help writing the javascript, the action, or both?
Inside your view:
<%= this.Select("DDLName").Attr("onchange", "NameOfJavascriptFunction();") %>
<%= this.MultiSelect("ListBoxName") %>
The javascript will look like this:
function NameOfJavascriptFunction() {
var ddlValue = $("DDLName").val();
jQuery.ajax({
type: 'GET',
datatype: 'json',
url: '/Controller/Action/' + dValue,
success: updateMultiSelect
});
}
function updateMultiSelect(data, status) {
$("#ListBoxName").html("");
for(var d in data) {
$("<option value=\"" + data[d].Value + "\">" + data[d].Name + "</option>">).appendTo("#ListBoxName");
}
}
Finally, the action is something like this (put this in the controller and action from the first javascript):
public ActionResult Action(int id) //use string if the value of your ddl is not an integer
{
var data = new List<object>();
var result = new JsonResult();
this.SomeLogic.ReturnAnEnumerable(id).ToList().ForEach(foo => data.Add(new { Value = foo.ValueProperty, Name = foo.NameProperty }));
result.Data = data;
return result;
}
Feel free to ask follow up questions if you need any more explanation.

Categories