I want to be able to click on a blog link and open the blog showing the page of that specific blog. My route config accepts optional id parameters. I am doing this with AJAX as well so hoping to click on a blog link and return to BlogController. I have searched around but cannot find something that helps me..
Here is my code
CSHTML
#foreach (var blodID in blogCont)
{
<a href="??" id="blogHREF" blog-id="#blodID.blogID">
Text
</a>
}
AJAX
$(document).on("click", "#blogHREF", function () {
var retBlogID = $(this).attr("blog-id");
var blogData = {
blogID: retBlogID
}
$.ajax({
type: "GET",
url: "Blog/getBlog",
data: blogData
});
});
C#
public class BlogController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult getBlog(blogTable blogged)
{
return View(blogged.blogAuthor);
}
}
If you are simply navigating to a new page (such as your question suggests) I would simply use a html helper.
#Html.Action("getBlog", "Blog", new {blogged = blodID })
So this:
<a href="??" id="blogHREF" blog-id="#blodID.blogID">
Text
</a>
would be this:
<a href="#Html.Action("getBlog", "Blog", new {blogged = blodID })" id="// THIS HAS TO BE UNIQUE">
Text
</a>
This is assuming that blodID is a blogTable.
Remove the [HttpGet], we don't need it.
Also worth noting that the id on your <a> tag doesn't look to me to be a unique value. These have to be unique. No matter where you use Id's in HTML.
Related
So I have a list, where useer can add any number of items. In other words, I have a <ul> and variable length of <li> elements.
Variable length <li> elements means, user adds <li> elements in runtime, if she/he is done, she/he submits the form. So I don't know how many <li> elements are there.
Something like this :
<form asp-controller="MyController" asp-action="AddList" method="post">
<ul id="myUL">
<li>item1</li>
<li>item2</li>
/*...*/
<li>itemN</li>
</ul>
<button type="submit" class="btn btn-default">New List</button>
</form>
I add <li> elements dynamically with JavaScript if user clicks a button, like this:
function newElement() {
var li = document.createElement("li");
var inputValue = document.getElementById("myInput").value;
var t = document.createTextNode(inputValue);
li.appendChild(t);
document.getElementById("myUL").appendChild(li);
}
My controller's method (AddList) looks like this:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> NewShoppingList(object list)
{
// I don't know how to accept that list
}
But this way my parameter list is null. How can I get that <ul> with some <li> elements?
ul and li are not submittable form elements.You can try the following.
I'm not sure what your view model looks like, but it seems like a list of string values? So then it will look like this in your view model:
public class YourViewModel
{
public List<string> Items { get; set; }
}
In your view try the following:
<ul id="myUL">
<li>item1</li>
<li>item2</li>
/*...*/
<li>itemN</li>
</ul>
function addHidden(theLi, key, value) {
// Create a hidden input element, and append it to the li:
var input = document.createElement('input');
input.type = 'hidden';
input.name = key;'name-as-seen-at-the-server';
input.value = value;
theLi.appendChild(input);
}
function newElement() {
var li = document.createElement("li");
var inputValue = document.getElementById("myInput").value;
var t = document.createTextNode(inputValue);
li.appendChild(t);
addHidden(li, 'your-model-propertyName' + li-Count+1, inputValue );
document.getElementById("myUL").appendChild(li);
}
When you post then these items should still be in the list.
[HttpPost]
[AllowAnonymous]
//[ValidateAntiForgeryToken]
public async Task<IActionResult> NewShoppingList(List<string> list)
{
// I don't know how to accept that list
}
If you dont like add hidden field for each li , you can get all li items and send thems with Ajax.
like this:
function sendData() {
var items = [];
$("#myUL li").map(function () {
items.push(this.innerText);
});
$.ajax({
type: "POST",
data: {
list: items
},
url: "/Home/NewShoppingList",
success: function (res) {
}
}
I hope this helps.
You should avoid posting the html, after all you'll probably need to parse it, save to db etc. As Jeremy Thomson suggested use some client side library - jQuery, Angular or what ever you want and do the posts of items using Ajax. You can post them on each user input, or keep them in mvc model, javascript object etc. and post them at once.
example just to get the idea:
MVC - Binding dynamically added controls to a List<T> in a Model
I have been at this for an hour now and I haven't got a clue what I am doing wrong.
I got the following ActionLink:
#Html.ActionLink("Remove", "RemoveActivity", "Dashboard", new { id = a.Id },htmlAttributes: null)
This targets the following Method in my DashboardController:
[HttpPost]
public ActionResult RemoveActivity(int id)
{
activityRepo.Delete(activityRepo.GetById(id));
return RedirectToAction("ActivityDetails");
}
For some reason this error gets returned:
The resource cannot be found. Description: HTTP 404. The resource you
are looking for (or one of its dependencies) could have been removed,
had its name changed, or is temporarily unavailable. Please review
the following URL and make sure that it is spelled correctly.
Requested URL: /Dashboard/RemoveActivity/564
A table row with the Id of 564 does exist in the database. It worked a few hours ago.
Any help is appreciated. I am clueless!
EDIT:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace HaarlemFestival_Web
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
}
#Html.ActionLink() generates a <a> tag which makes a GET, not a POST. You need to include a <form> element and submit the value to your POST method
#using (Html.BeginForm("RemoveActivity", "Dashboard", new { id = a.Id }))
{
<input type="submit value="Remove" />
}
and you can style your submit button to look like a link if that is what you want visually
I also suggest you add the #Html.AntiForgeryToken() method in the <form> and add the [ValidateAntiForgeryToken] attribute to your method to prevent CSRF attacks.
You should also consider validating that the current user does have the permission to delete that record.
Note that since you method is changing data, it should be a POST, so do not be tempted to just remove the [HttpPost] attribte from your method so that the link works.
Because #Html.ActionLink will render an anchor tag, Clicking on which is always "GET" request. So if you want an HTTP-Post method you need to override its behavior using javascript like this:
#Html.ActionLink("Remove", "RemoveActivity", "Dashboard", new { id = a.Id ,#class="post_link"},htmlAttributes: null);
[HttpPost]
public String RemoveActivity(int id)
{
activityRepo.Delete(activityRepo.GetById(id));
return "Remove/ActivityDetails";
}
<script type="text/javascript">
$(function(){
$("a.post_link").click(function(e){
e.preventDefault();
$.post($(this).attr("href"),function(data){
//got your redirection link and do a redirection request at here
window.location = data;
});
});
});
</script>
You can try this , very simple way
In you view just place anchor tag: // make sure you have Id coming here
Remove
Jquery block:
<script type="text/javascript">
function RemoveActivity(index) {
var urlBase = '/Dashboard/RemoveActivity/';
$.ajax({
url: urlBase,
data: { id: index },
success: function (result) {
},
error: function (ex) {
}
});
}
</script>
keep your controller action as post method only. I hope this helps.
So I'm trying to create a comments section for a blog. I'm having trouble identifying the opened blog posts id in my jquery.
I'm getting these errors from the chrome console
GET http://localhost:46223/api/posts//comments
the postid should be inbetween the double slash but its not. When I manually enter the postID inside the ajax call it works perfectly.
An Api Controller is exposing the comments from the database, relevant code below.
[Route("api/posts/{postId:long}/comments")]
public class CommentsController : Controller
{
readonly BlogDataContext _dbContext;
public CommentsController(BlogDataContext db)
{
_dbContext = db;
}
// GET: api/values
[HttpGet]
public IQueryable<Comment> Get(long postId)
{
return _dbContext.Comments.Where(x => x.PostId == postId);
}
When I press the "Show Comments" link it chrome console gives me the error I was talking about earlier. Relevant code from my partial view below. The most important line from below is only the first one.
Show Comments
<div class="comments-container hide">
<h3>Comments</h3>
<div class="comments">
</div>
<hr />
<div>
Add a comment
<div class="new-comment hide">
<form role="form">
<div class="form-group">
<textarea name="Body" class="new-comment form-control" placeholder="Enter comment here..."></textarea>
<button type="submit" class="btn btn-default">Create Comment</button>
</div>
</form>
</div>
</div>
</div>
Relevant code snippets from my .js
$(document).on('click', '.show-comments', function (evt) {
evt.stopPropagation();
new Post(this).showComments();
return false;
});
function Post(el) {
var $el = $(el),
postEl = $el.hasClass('blog-post') ? $el : $el.parents('.blog-post'),
postId = postEl.data('post-id'),
addCommentEl = postEl.find('.add-comment'),
newCommentEl = postEl.find('.new-comment'),
commentEl = newCommentEl.find('[name=Body]'),
commentsContainer = postEl.find('.comments-container'),
commentsEl = postEl.find('.comments'),
showCommentsButton = postEl.find('.show-comments'),
noCommentsEl = postEl.find('.no-comments');
return {
addComment: addComment,
renderComment: renderComments,
showAddComment: showAddComment,
showComments: showComments,
};
function showComments() {
PostCommentService.getComments(postId).then(renderComments);
}
var PostCommentService = (
function PostCommentService() {
function call(postId, method, data) {
return $.ajax({
// RESTful Web API URL: /api/posts/{postId}/comments
url: ['/api/posts', postId, 'comments'].join('/'), // If I Change the 'postId' here to an integer of an existing postId, it works perfectly.
type: method,
data: JSON.stringify(data),
contentType: 'application/json'
});
}
return {
// Add comment by calling URL with POST method and passing data
addComment: function (comment) {
return call(comment.PostId, 'POST', comment);
},
// Get comments by calling URL with GET method
getComments: function (postId) {
return call(postId, 'GET');
}
};
})();
Full .js file
I'm sorry if I missed to include something, but I have a lot of code. If you need to know anything else let me know.
I'd also be grateful just for some suggestions where my error might be.
Your code is getting the post id from the data attribute post-id of the postEl. postEl could be the same anchor tag which was clicked or it's parent with blog-post css class.
var $el = $(el),
postEl = $el.hasClass('blog-post') ? $el : $el.parents('.blog-post'),
postId = postEl.data('post-id'),
But in your HTML markup, there is no such data attribute for the anchor tag. So if you add that, your code will be able to get the post id and use that to build the url
Show Comments
I hard coded 250 as the value for the data-post-id attribute. You may replace it with a value coming from your model.
Show Comments
Is there any way to get the name of View that called method in controller and save it for example in some custom variable inside that controller's method?
For example:
I have one View that uses Ajax to get to InfinateScroll method in controller:
<div class="container-post">
<div id="postListDiv">
#{Html.RenderAction("PostList", "Posts", new { Model = Model });}
</div>
<div id="loadingDiv" style="text-align: center; display: none; margin-bottom: 20px;">
<img alt="Loading" src="#Url.Content("~/images/ajax-loader.gif")" />
</div>
</div>
<script src="#Url.Content("~/Scripts/jquery-1.10.2.min.js")"></script>
<script type="text/javascript">
var BlockNumber = 2;
var NoMoreData = false;
var inProgress = false;
$(window).scroll(function () {
if ($(window).scrollTop() == $(document).height() - $(window).height() && !NoMoreData && !inProgress) {
inProgress = true;
$("#loadingDiv").show();
$.post("#Url.Action("InfinateScroll", "Posts")", { "BlockNumber": BlockNumber },
function (data) {
BlockNumber = BlockNumber + 1;
NoMoreData = data.NoMoreData;
$("#postListDiv").append(data.HTMLString);
$("#loadingDiv").hide();
inProgress = false;
});
}
});
</script>
I use this View on two pages. In one case I'm using it to show only posts from specific user (user who is logged in), and on the other view I'm showing posts from all users in database(similar to Facebook wall where you can see only your post, and NewsFeed where you can not only your's but also posts from your frineds).
For some reason I would like to know which page was active when call for InfinateScroll method was made.
This is the method where I would like to make some differences between those two pages so I can do some check out's later.
[HttpPost]
public ActionResult InfinateScroll(int BlockNumber)
{
int BlockSize = 5;
var posts = PostManager.GetPosts(BlockNumber, BlockSize);
JsonModel jsonModel = new JsonModel();
jsonModel.NoMoreData = posts.Count < BlockSize;
jsonModel.HTMLString = RenderPartialViewToString("PostList", posts);
return Json(jsonModel);
}
This method gets posts using helper method GetPosts and it's used for showing more posts on scroll.
You can get the name of the current View from inside the view using the following:
#Path.GetFileNameWithoutExtension(Server.MapPath(VirtualPath))
Source: How to get the current view name in asp.net MVC 3?
so you could add this as a routevalue into your #Url.Action like so:
#Url.Action(
"InfinateScroll",
"Posts",
new{callingView=Path.GetFileNameWithoutExtension(Server.MapPath(VirtualPath))})
Then you could add a parameter to your controller method
public ActionResult InfinateScroll(int BlockNumber, string callingView)
You can create a hidden variable in the html like this -
<input type="hidden" id="pageName" value="myPage1" />
Add an extra parameter to your Action -
public ActionResult InfiniteScroll(int BlockNumber, int pageName)
And then, in your jquery code, when you post, send in pageName as well.
$.post("#Url.Action("InfinateScroll", "Posts")", { "BlockNumber": BlockNumber, "pageName": $('#pageName').val() },
Hope this helps.
In one case I'm using it to show only posts from specific user... and
on the other view I'm showing posts from all users in database...
Putting your desired logic on the view is unsafe, especially if showing data is user-based or user-specific. However, if you insists on having the logic on the view then you should pass along another variable to the controller like so:
$.post("#Url.Action("InfinateScroll", "Posts")",
{ "BlockNumber": BlockNumber, "UserId": userId },
// rest of your code goes here...
});
You then should have another parameter in your controller:
[HttpPost]
public ActionResult InfinateScroll(int BlockNumber, int userId)
{
//filter your data based on the "userId" parameter
}
But like I mentioned this is unsafe because someone can easily pass in a valid "userId" and get to the data when you don't want them to. So the safest (or safer) way is to have the "filtering logic" in your controller like so:
[HttpPost]
public ActionResult InfinateScroll(int BlockNumber)
{
// a context based logic
var userId = GetLoggedInUserId();
// that method could return null or zero
// and depending on how you approach it
//filter your data based on the "userId"
}
I have a page with a video at the top and a list of videos you can choose from. Currently, clicking a link in the video list will reload the entire page. I need it to only refresh the partial view I have containing the video at the top of the page.
I saw several posts here on SO showing how to reload partial views with JQuery, but couldn't get it to work correctly in my situation. I'm unsure how to pass the correct id of the video along.
Controller:
public ActionResult Videos(int topVideo = 0)
{
VideosModel model = new VideosModel();
model.Videos = StatsVideoService.GetEntityList(new Lookup(TableStatsVideo.IsDeleted, false)).OrderByDescending(x => x.DateCreated).ToList();
if (topVideo == 0)
model.TopVideo = model.Videos.First();
else
{
model.TopVideo = model.Videos.Where(x => x.StatsVideoId == topVideo).FirstOrDefault();
if (model.TopVideo == null)
model.TopVideo = model.Videos.First();
}
return View(model);
}
View:
#model Project.Models.VideosModel
<section class="videos">
<div id="top_video">
#{Html.RenderPartial("StatsVideo", Model.TopVideo);}
</div>
<ul>
#foreach (var item in Model.Videos)
{
<li>
<div class="videoList">
<a href ="#Url.Action("Videos", "Home", new { topVideo = item.StatsVideoId })">
<img src="#Url.Content("~/Content/img/video-ph.png")" />
</a>
<p class="videoTitle">#item.Title</p>
</div>
</li>
}
</ul>
</section>
If there's any more information needed, please let me know.
After several hours of bashing my head against the wall, I got it to work! Just as a reference to anyone else in the future who's viewing this article, here's how I got it to work:
I set the onclick of the link to point to a javascript method, passing in the id of the video as a parameter:
#foreach (var item in Model.Videos)
{
<li>
<div class="videoList">
<a href ="#" onclick="updateTopVideo(#item.StatsVideoId)">
<img src="#Url.Content("~/Content/img/video-ph.png")" />
</a>
<p class="videoTitle">#item.Title</p>
</div>
</li>
}
And then I included this script in the view at the bottom:
<script>
var updateTopVideo = function (itemId) {
var url = '#Url.Content("~/Home/StatsVideo/")';
url = url + itemId;
$.get(url, "", callBack, "html");
};
var callBack = function (response) {
$('#top_video').html(response);
};
</script>
Finally, I added a method to my controller that would return the partial view needed for the video at the top of the screen:
public ActionResult StatsVideo(int Id)
{
IStatsVideo vid = StatsVideoService.GetEntity(new Lookup(TableStatsVideo.StatsVideoId, Id));
if (vid == null)
vid = StatsVideoService.GetEntityList(new Lookup(TableStatsVideo.IsDeleted, false)).OrderByDescending(x => x.DateCreated).FirstOrDefault();
return PartialView(vid);
}
This code should be fairly easy to understand. Basically, the onclick calls the first javascript method, which then calls the controller. The controller builds the partial view and returns it. The first javascript method passes it to the second javascript method which sets the html of the div "top_video" to be the returned partial view.
If anything doesn't make sense, or anyone's having trouble with this in the future, let me know and I'll do my best to offer some help.
I think there may be several confusing and inconsistent elements here.
First, you are returning a full view instead of a partial view. This reloads all containing elements, not just the part that is relevant to your partial view.
Second, you are using Url.Action, which only generates the url. I would recommend using Ajax.ActionLink, which allows you to do fully ajax calls, refreshing the content of your partial div and updating a target div element.
instead of:
<div class="videoList">
<a href ="#Url.Action("Videos", "Home", new { topVideo = item.StatsVideoId })">
<img src="#Url.Content("~/Content/img/video-ph.png")" />
</a>
<p class="videoTitle">#item.Title</p>
</div>
try the more modern solution
<div class="videoList">
#Ajax.ActionLink(
"Videos",
"Home",
"new { topVideo = item.StatsVideoId },
new AjaxOptions {
HttpMethod = "GET",
OnSuccess = "handleSuccess"
}
)
</div>
This way you can be very specific on what you want each link to do, and you can pass along multiple parameters as well as define a callback function. You can also use "UpdateTargetId" in your ajax options to load your newly refreshed partial view into a DOM element.
You can remove the around the image and just store the url generated by the Url.Action in a data-href attribute.
Then you can use the jquery load method to load the data:
$(".videolist>img").click(function () {
$("#content").load($(this).data("href"));
});
I created a fiddle that loads content dynamically here, so you can play with it if you want: http://jsfiddle.net/bTsLV/1/