I have one text field where user can write short message (like status on g+ or fb) bellow that field I have a list where that messages need to be displayed. When user submit that message it is stored in database, after that I refresh whole View. This is how I display that list:
#foreach (var m in #Model.Messages){
<div>
<p>#m.Author</p>
<p>#m.Text</p>
</div>
}
Now I wan't to make better user experience. I wan't to add that message without refreshing while View. I know that I have to use JQuery, Ajax etc. but I have searched on the google and can't find any good tutorial or example for ASP MVC / Razor and this like feature. Can somebody give me some direction?
Typing asp.net mvc ajax jquery in google usually yields sufficiently enough results. But anyway here's what you could do. Assuming you have a text field where the users will type their messages:
#using (Html.BeginForm("AddMessage", "Messages", FormMethod.Post, new { id = "addMessageForm" }))
{
#Html.TextBoxFor(x => x.Author)
#Html.TextAreaFor(x => x.Text)
<button type="submit">Add message</button>
}
you could AJAXify this form:
$(function() {
$('addMessageForm').submit(function() {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function(result) {
$('#messages').append(result);
}
});
return false;
});
});
and finally you would have a controller action which will perform the actual adding of the message to the database:
[HttpPost]
public ActionResult AddMessage(MessageViewModel message)
{
// TODO: add the message to the database
return PartialView("~/Views/Messages/DisplayTemplates/MessageViewModel.cshtml", model);
}
and a corresponding display template (~/Views/Messages/DisplayTemplates/MessageViewModel.cshtml):
#model MessageViewModel
<div>
#Html.DisplayFor(x => x.Author)
#Html.DisplayFor(x => x.Text)
</div>
and the list of messages will be shown using display templates and not using loops:
<div id="messages">
#Html.DisplayFor(x => x.Messages)
</div>
Related
I am making a "write anything here" webpage where users can write anything in a textbox then post it and it is visible to everyone. It worked fine till I found out that when any user writes and submits, all the others have to refresh the page so as to get the new data from database. So a solution to this was to call ajax continuously in some intervals. This would check if there are new entries in the table. If yes, then it would render it to the html without refreshing the whole page. Now I am pure ajax noob and after hours of research I am unable to find out how to do it.
Here is the html code
<div id="textArea">
<form action="http://127.0.0.1:3000" method="POST">
<br>
<textarea minlength="3" name="comment" placeholder="Enter Text Here.." required></textarea>
<input id="postButton" type="submit" name="" value="POST">
</form>
</div>
</div>
<div class="show">
{% for item in data %}
<div id="auto" class="disPost">{{item[0]}}</div>
{% endfor %}
</div>
Here the textarea is in a form and it submits the text to database via flask server.
Also, all the comments that users wrote are shown in "div.show"
Now the flask code is like this
#app.route('/', methods = ['POST', 'GET'])
def home():
if request.method == 'POST':
post = request.form["comment"]
myquery = "select p_id from posts order by p_id desc limit 1"
mycursor.execute(myquery)
new_p_id = mycursor.fetchone()[0] + 1
myquery = "select exists(select * from posts where p_des=%s)"
rec_tup = (post,)
mycursor.execute(myquery, rec_tup)
if mycursor.fetchone()[0]==0:
myquery = "insert into posts values(%s, %s)"
rec_tup = (new_p_id, post)
mycursor.execute(myquery, rec_tup)
mydb.commit()
mycursor.execute("select distinct p_des from posts order by p_id desc")
data = mycursor.fetchall()
return render_template("homepage.html", data=data)
"mydb" is the connector & "mycursor" is the connector's cursor
Now I am stuck somewhere in how to call AJAX function. I am not able to write beyond this ..
$(document).ready(function() {
setInterval(function() {
$.ajax({
url: '',
type: 'GET',
data: //something must be here,
success: function(data) {
//here "div" must be added to the "show" class - that is comment of other users
}
})
}, 3000);
});
I know that I have to do something like this but am literally not able to solve it.
I know this is not good question and I must look at tutorials first. But believe me I had it all. I am not able to solve this problem at all.
Thank you for seeing this :)
I did this on my latest project, you can try it too. But make sure to refresh only div element you want the data show, not all the page.
$(document).ready(function() {
function getData(){
$.ajax({
url: '',
type: 'GET',
data: //something must be here,
success: function(data) {
//here "div" must be added to the "show" class - that is comment of other users
}
});
}
getData();
setInterval(function() {getData()},2000);
});
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.
In an MVC View, is there an efficient way to store client-side values for use on subsequent page visits?
Typical scenario
An Index page has a table that's getting a bit long so I add a filter (I know paging is another option) and use an input control with some JavaScript to limit the table rows without having to perform another "Get" from the server.
This works fine but, if I navigate off (say) into an edit page then return back to the Index page, the filter is clearly no longer there.
After a bit of searching I never found anything simple so I post my meagre answer below.
The View contains a form at the top of the page into which a user can type filter text (on form "Get", text is set from a session value):-
<form id="frmEdit">
#Html.AntiForgeryToken()
<div class="form-group row">
<div class="col-sm-6">
#Html.ActionLink("Create New", "Create", null, new { #class = "nav-item nav-link" })
</div>
<label for="search" class="col-sm-2 col-form-label text-right">Filter</label>
<div class="col-sm-4">
<input type="text" placeholder="Filter" class="form-control" id="search" value=#Session["SparesSectionFilter"]>
</div>
</div>
</form>
A script section contains the filtering JavaScript but also a postback to the controller
#section Scripts{
<script type="text/javascript">
// on load
PerformFilter();
// hook up events
$(function () {
$("input#search").on("keydown keyup", function () {
PerformFilter();
// post back to session for reuse
$.post('SparesSections/Session_Add', { __RequestVerificationToken: $('[name=__RequestVerificationToken]').val(), itemName: 'SparesSectionFilter', itemValue: $("#search").val() });
});
})
</script>
}
I have a custom base-class for my controller into which I've added the following actions. These are usable from any controller using this class. The Razor view loads the session value but I've included a "Get" in the controller for client-side options.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Session_Add(string itemName, string itemValue)
{
Session.Add(itemName, itemValue);
return Json(new { itemName = itemName, itemValue = itemValue }, JsonRequestBehavior.AllowGet);
}
[HttpGet]
public ActionResult Session_Get(string itemName)
{
return Json(new { itemName = itemName, itemValue = Session[itemName] ?? string.Empty }, JsonRequestBehavior.AllowGet);
}
I'm very new to MVC and Javascript so please be patient with me, I'm working on small application and I came to part when I need to select something from dropdown list and based on that selection I need to redirect user to another View, I also need to determine somehow where I should redirect user, so that is reason why I tried to pass parameter also ( database ID to my post method) but unfortunatelly this is not working, in section below I will post my code:
Method which is sending data to my DropDownList :
public ActionResult ShowArticleGroup()
{
List<ArticleGroup> articlesGroups = GroupsController.GetAllGroups();
ViewBag.articlesGroups = articlesGroups;
return View(articlesGroups);
}
[HttpPost]
public ActionResult ShowArticleGroup(string id)
{
//Here I wanted to take ID of selected Group and because there will be allways 3 Groups I can do if else and Redirect by ID
if(id =="00000000-0000-0000-0000-000000000002")
{
return RedirectToAction("Create","Article");
}
return RedirectToAction("Create", "Article");
}
And my VIEW - there is only one control on the view : just one dropdown, and based on selection I should be redirected to another view, and I wanted here to take ID of selected group and by that I wanted to redirect user to appropiate view:
#model IEnumerable<Model.ArticleGroup>
#{
ViewBag.Title = "Add new article";
}
<h3 style="text-align:center">Choose article group</h3>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true)
<div class="form-group" style="text-align:center">
#Html.DropDownList("Group", new SelectList(ViewBag.articlesGroups, "GroupID", "GroupTitle.Name"), null, new { onchange = "document.location.href = '/Articles/ShowArticleGroup/' + this.options[this.selectedIndex].value;" })
</div>
</div>
}
First of all, usage of location.href on DropDownList seems wrong here:
#Html.DropDownList("Group", new SelectList(ViewBag.articlesGroups, "GroupID", "GroupTitle.Name"), null,
new { onchange = "document.location.href = '/Articles/ShowArticleGroup/' + this.options[this.selectedIndex].value;" })
AFAIK, location.href used for redirect to another page using HTTP GET, hence it will try to call first ShowArticleGroup action method without parameter, and the URL parameter simply ignored since given URL parameter only exist in POST.
To submit the form with DropDownList, you need to handle change event triggering POST into controller action method:
jQuery
<script type="text/javascript">
$(document).ready(function() {
$("#Group").change(function() {
var groupId = $("#Group").val();
$.post('#Url.Action("ShowArticleGroup", "ControllerName")', { id: groupId }, function (response, status) {
// response handling (optional)
});
});
});
</script>
DropDownList
#Html.DropDownList("Group", new SelectList(ViewBag.articlesGroups, "GroupID", "GroupTitle.Name"), null)
I recommend you using strongly-typed DropDownListFor with binding to a viewmodel approach if you want to pass viewmodel contents during form submit.
NB: $.post is shorthand version of $.ajax which uses POST submit method as default.
Related issues:
Autopost back in mvc drop down list
MVC 4 postback on Dropdownlist change
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;
}