MVC DropDownListFor how to add RouteLink - javascript

Hi sorry for asking such an easy question but I'm scratching my head all day today and cannot figure this out. I found lots of similar questions but non of them resolve my problem.
I had a page with list of products and few buttons to filter products by category. Because number of products has increased I decided to change them to drop down box.
So I have drop down box which populates categories:
#Html.DropDownListFor(m => m.SelectedCategoryId, Model.CategoryItems, new { id = "changeCategory" })
and javascript which fires on change event:
<script type="text/javascript">
$(document).ready(function () {
$("#changeCategory").change(function () {
var selectedCategory = $(this).text();
$.ajax({
url: '#Url.Action("List", "Deal")',
type: 'GET',
data: { category: selectedCategory },
cache: false,
});
});
});
</script>
This doesn't work. My previous routing works with the code below:
#foreach (var link in Model) {
#Html.RouteLink(link, new {
controller = "Deal",
action = "List",
category = link,
page = 1
}, new {
#class = "btn btn-block btn-default btn-lg"
})
}
UPDATE:
I have changed the jQuery code to:
<script type="text/javascript">
$(document).ready(function () {
$("#changeCategory").change(function () {
var selectedCategory = $("#changeCategory option:selected").text();
$.ajax({
url: selectedCategory,
type: 'POST',
cache: true,
});
});
});
</script>
and the link looks correct now but the website doesn't reload. When I watch this in the Chrome Developer Tool in Network section the link appear there and when I click it it does open correct page.
Why it doesn't do that on website?
UPDATE 2
My Controller
public ViewResult List(string category, int page = 1)
{
DealsListViewModel model = new DealsListViewModel
{
Deals = repository.Deals
.Where(p => category == null || p.Category == category)
.OrderBy(p => p.DealID)
.Skip((page - 1) * PageSize)
.Take(PageSize),
PagingInfo = new PagingInfo
{
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = repository.Deals.Count()
},
CurrentCategory = category
};
return View(model);
}
Any help is appriciated

It appears you want to redirect to the List method of DealController and pass the text of the selected option. If so then
$("#changeCategory").change(function () {
window.location.href = '#Url.Action("List", "Deal")' + '/' + $(this).find('option:selected').text();
});
assuming your action method is something like
public ActionResult(string someValue)
AJAX calls stay on the same page and do not redirect to another page.
And out of curiosity, why override the default id (and not just use $("#SelectedCategoryId").change(...)?
Edit
If you want to return some html to include on the page, return a partial view and update the page html in the AJAX success function
Controller
public PartialViewResult List(string category, int page = 1)
{
DealsListViewModel model = new DealsListViewModel ....
....
return PartialView(model)
}
Script (assumes you have an element with `id="results" where you want to render the returned html)
$("#changeCategory").change(function () {
var url = '#Url.Action("List", "Deal")';
var category = $(this).find('option:selected').text();
var page = ? // if you want to pass this as well
$.get(url, { category: category, page: page }, function(data) {
$('#results').html(data);
});
});

Try the following in your ajax call:
type: 'POST'

Related

How can I pass an element as a parameter

I have the following code:
#Html.PagedListPager(Model, page => Url.Action("Index", new {from = ? , to = ? , page}))
And this is my Action method:
public ActionResult Index(string from, string to, int? page)
{
}
I want to specify the from and to parameters that comes from a picker which are defined as below:
#Html.EditorFor(c => c.LastOrDefault().Date, "MyPickerTemplate")
#Html.EditorFor(c => c.FirstOrDefault().Date, "MyPickerTemplate")
How can I send the value of the EditorFor's as a parameter to Url.Action? I can find them with javaScript like below:
var from = document.GetElementByClassName("date")[0].value;
var to = document.GetElementByClassName("date")[1].value;
But I don't know how should I send them as parameter to Url.Action.
var from = document.GetElementByClassName("date")[0].value;
var to = document.GetElementByClassName("date")[1].value;
Now you have two parameters. You can call ajax function as below.
$.ajax({
url: "/Controller/TheAction",
type: "get",
data: { fromParam: from, toParam: to }
});
Here is the controller,
public ActionResult TheAction(DateTime fromParam, DateTime toParam)
{
// your code.
}
EDIT:
You must call ajax from pager button click. For that, do following.
<div id="myPager">
#Html.PagedListPager(Model, Page => "")
</div>
<script type="text/javascript">
$(function () {
$('#myPager').on('click', 'a', function () {
var currentPage = $(this);
// ajax call
});
});
</script>

Reload div fires constantly without event MVC

I am working on a project in Visual studio using MVC. I am trying to reload a div after a btn is clicked and data has been posted to the controller dynamically. I use an Ajax post and return a JSONresult. The code for posting looks like this:
<div id = "DelUser">
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "Dform" }))
{
<label> Select User(s) to Delete: </label>
#Html.ListBox("Users", ViewBag.Users as MultiSelectList,
new { #class = "chzn-select", #style = "width:250px; height:350 px" })
}
<button class="btn btn-primary dropdown-toggle" id="Button1" type="button" onclick="DeleteUsers()"> Delete Selected </button>
</div>
<script>
function DeleteUsers() {
var myList = []
$("#Users > option:selected").each(function () {
myList.push($(this).val());
});
jQuery.ajax({
type: 'post',
dataType: 'json',
contentType: "application/json; charset=utf-8",
url: 'DeleteU',
data: JSON.stringify(myList),
success: function (data) {
$('#msgbx2').html(data.msg);
//here I am trying to refresh the div after the post but it fires every second
$('#DelUser').html('/Home/DeleteUser');
},
failure: function (errMsg) {
$('#msgbx2').html(data.msg);
}
});
return false;
}
The name of the controller is DeleteUser and the JSONresult controller is DeleteU.
I am trying to do this so that the dropdownlist updates after I delete the selected users. If there is another way to dynamically update this div, any information on that would be good to know too. Thank you in advance.
Here are the Controllers as well:
[HttpGet]
public ActionResult DeleteUser()
{
List<string> u = new List<string>();
object[] users = data.getDataFrmDB("Select username From `users`;");
if (users != null)
{
foreach (object[] user in users)
{
u.Add((string)user[0]);
}
}
ViewBag.Users = new MultiSelectList(u, "Username");
return View();
}
[HttpPost]
public JsonResult DeleteU(List<string> Users)
{
bool good = false;
if (Users != null)
{
foreach (string user in Users)
{
string ins = "DELETE FROM `xcal-server`.`users` WHERE username='"+user+"';";
good = data.insert_update_delete_DB(ins);
List<string> u = new List<string>();
object[] users = data.getDataFrmDB("Select username From `users`;");
if (users != null)
{
foreach (object[] usera in users)
{
u.Add((string)usera[0]);
}
}
ViewBag.Users = new MultiSelectList(u, "Username");
if (good == true)
{
ViewBag.error = "You have successfully deleted user";
}
else
{
ViewBag.error = "There was an issue removing user";
}
}
return Json(new { msg = "You have Successfully deleted Users " });
}
return Json(new { msg = "the passwords entered do not match" });
}
This is just a section of my main page that has other action functions/controllers in it which is why i did not originally post the controllers
You can simply update the existing listbox by removing the currently selected items. Start by removing onclick="DeleteUsers() from the button and use Unobtrusive JavaScript. Since you do not appear to have a POST method for DeleteUser() or a submit button, you may as well also replace #using (Html.BeginForm(...)) with just <form> elements. The script will then be
$('#Button1').click(function () {
var selected = $('#Users option:selected'); // store the selected users
if (selected.length == 0) {
return; // no point making a post
}
var users = [];
$.each(selected, function (index, item) {
users.push($(this).val());
})
$.ajax({
type: 'post',
dataType: 'json',
url: '#Url.Action("DeleteU", "yourControllerName")', // always use Url.Action to generate your url's
data: { users: users },
traditional: true,
success: function () {
// remove the currently selected options
selected.remove();
}
});
})
However a lot of the code in your controller does not make sense. Your adding values to ViewBag and even creating a SelectList but your not returning a view (your returning json), so all that is lost. And even if you were returning a view, your loop keeps overwriting the value of ViewBag.error so that only the last value would be set. Ideally, you should be calling a service to delete the users by passing the user name (or a collection of user names so that they are all deleted in a transaction). But based on you current code, you controller method can be
[HttpPost]
public JsonResult DeleteU(List<string> Users)
{
if (Users == null)
{
// throw an error that can be caught in the ajax error handler
}
List<string> deletedUsers = new List<string>();
foreach (string user in Users)
{
string ins = "DELETE FROM `xcal-server`.`users` WHERE username='"+user+"';";
if (data.insert_update_delete_DB(ins))
{
deletedUsers.Add(user);
}
}
return Json(deletedUsers);
}
and then the ajax success callback
success: function (data) {
$.each(data, function(index, item) {
// remove each item that was successfully deleted in the controller
$('#Users option:contains("' + item + '")').remove();
}
}
i think you need to add a $(document).ready(function(){}); around your script

Ctrl+Click on link that renders partial view

I got a link that renders partial view using AJAX.
Here is my link code:
<a href="#" onclick="LoadChildCategories(#i.CategoryId,
#i.IsTrading.ToString().ToLower())">#i.Name</a>
And here is LoadChildCategories function code:
function LoadChildCategories(id, isTrading) {
var link;
if (isTrading === false) {
link = '#Html.Raw(#Url.Action("NonTradingCategories", "Home",
new {categoryId = -1}))';
} else {
link = '#Html.Raw(#Url.Action("ModelList", "Home", new {categoryId = -1}))';
}
link = link.replace("-1", id);
$.ajax({
url: link,
method: 'GET',
success: function(data) {
$("#viewPartial").html(data);
}
});
}
When I click it without CTRL it's ok, partial view renders into my div. But when I click it with CTRL partial view renders into current tab and then another tab opens at Index page.
And when I rightclick on link and select to open it in another tab then nothing happens at current tab and new tab opens at Index page.
So, is there any ways to handle that?
I found pretty nice solution, so I modified project according to this solution: Make an MVC Application into a SPA with AJAX and History.js
1) Make controller methods return View, not PartialView and add one line of code than will check is it an AJAX request:
public ViewResult Category(int id)
{
ViewBag.IsAjaxRequest = Request.IsAjaxRequest();
var node = CategoriesHandler.Instance.First(x => x.CategoryId == id);
var childCategories = CategoriesHandler.Instance.Where(x => x.ParentId == node.Id).ToList();
ViewBag.Message = node.Name;
return View(childCategories);
}
2) Edit _ViewStart.cshtml like that:
#{
Layout = ViewContext.ViewBag.IsAjaxRequest == true ? null : "~/Views/Shared/_Layout.cshtml";
}
3) Prepare links to be managed via AJAX:
#i.Name
4) Create container for views at _Layout.cshtml
#/*Some layout stuff*/
<div id="bodyContent">
#RenderBody()
</div>
#/*Other layout stuff*/
5) Prepare helper javascript file like that:
$(function () {
var contentShell = $('#bodyContent');
var History = window.History, State = History.getState();
$(".ajaxLink").on('click', function (e) {
e.preventDefault();
var url = $(this).data('href');
var title = $(this).data('title');
History.pushState(null, title, url);
});
function navigateToURL(url) {
$('#bodyContent').html('<div class="loader"> </div>');
$.ajax({
type: "GET",
url: url,
dataType: "html",
cache: false,
success: function (data, status, xhr) {
$('#bodyContent').hide();
contentShell.html(data);
$('#bodyContent').fadeIn(500);
},
error: function (xhr, status, error) {
$('#bodyContent').hide();
alert("TEST_Error");
}
});
}
History.Adapter.bind(window, 'statechange', function () {
State = History.getState();
if (State.url === '') {
return;
}
navigateToURL(State.url);
});});
6) Do not forget to include your javascript files into the bundle!

MVC5: Refresh View on button (hyperlink) click?

When my MVC5 View loads, I am filling a list for a DropDown on the View and setting the View to only display model records that have a [verified_date] field value older than the cutoff, by default 3 Months:
private InventoryTrackerContext db = new InventoryTrackerContext();
// GET: VerifyAssets
public async Task<ActionResult> Index()
{
List<SelectListItem> intervalList = new List<SelectListItem>();
intervalList.Add(new SelectListItem { Text = "Month", Value = "Month" });
intervalList.Add(new SelectListItem { Text = "Day", Value = "Day" });
intervalList.Add(new SelectListItem { Text = "Year", Value = "Year" });
var cutoffDate = DateTime.Now.AddMonths(-3);
var iNV_Assets = db.INV_Assets.Where(i => i.verified_date < cutoffDate).Include(i => i.Location).Include(i => i.Manufacturer).Include(i => i.Model).Include(i => i.Status).Include(i => i.Type).Include(i => i.Vendor);
ViewBag.intervalList = intervalList;
return View(await iNV_Assets.ToListAsync());
}
Now what I'm trying to implement, is allow users to specify an interval value and interval setting (values 1-31, and [Month], [Day], [Year]):
#Html.DropDownList("NumberValueSelection", Enumerable.Range(1, 31).Select(x => new SelectListItem { Text = x.ToString(), Value = x.ToString() }))
#Html.DropDownList("intervalList")
<span class="glyphicon glyphicon-refresh"> REFRESH</span>
When users click the [REFERSH] button (hyperlink), I want to refresh the view based on user specifications:
SCRIPT:
function newCutoffDate() {
var _value = document.getElementById("NumberValueSelection").value;
var _interval = document.getElementById("intervalList").value;
var data = { value: _value, interval: _interval };
$.ajax({
type: "POST",
dataType: "JSON",
url: '#Url.Action("NewCutoffDateInterval", "VerifyAssets")',
data: data,
success: function (resp) {
alert("Sucess! Value: " + resp.value + " | Interval: " + resp.interval);
},
error: function (resp) {
alert("Error! Value: " + resp.value + " | Interval: " + resp.interval);
}
})
}
CONTROLLER ACTION:
[HttpPost]
public async Task<ActionResult> NewCutoffDateInterval(int value, string interval)
{
var cutoffDate = DateTime.Now.AddMonths(-3);
if (interval == "Month")
{
cutoffDate = DateTime.Now.AddMonths(-value);
}
else
{
if (interval == "Day")
{
cutoffDate = DateTime.Now.AddDays(-value);
}
else
{
if (interval == "Year")
{
cutoffDate = DateTime.Now.AddYears(-value);
}
}
}
var iNV_Assets = db.INV_Assets.Where(i => i.verified_date < cutoffDate).Include(i => i.Location).Include(i => i.Manufacturer).Include(i => i.Model).Include(i => i.Status).Include(i => i.Type).Include(i => i.Vendor);
return View(await iNV_Assets.ToListAsync());
}
This however is not working. When I click the [REFRESH] button (hyperlink), my function executes and my controller action gets called, but the View does not refresh. All that occurs is that I get a return of "Error! Value: undefined | Interval: undefined".undefined` makes sense considering I'm not returning anything, but why is the View not refreshing?
$.ajax({ doesn't modify the DOM nor the window.location. It is an asynchronous request, and therefore happens "behind the scenes" so to speak. No navigation takes place, and the page/DOM is not modified without you explicitly doing so.
If you want the entire page to refresh, you have a couple of options.
Navigation via standard href
Make your link point to a valid href so when they user clicks it, they will be navigated. Use javascript as the user modifies selections to modify the href encoding parameters as query string values. You get the "Refresh" effect just by pointing the URL to the same page, but dynamically updating the href as the user makes selections before hitting refresh. When the user clicks the link, the controller should parse the parameters, and return the same page with new filters applied.
<a href='/VerifyAssets/NewCutoffDateInterval?someParameter=someValue'>Refresh</a>
Navigation via window.location
Keep you <a> as is, but modify your javascript such that instead of making an ajax request, you build a URL as described above, and set it as window.location, causing the page to navigate to the new URL:
// assume you build part after ? dynamically
var url = '/VerifyAssets/NewCutoffDateInterval?someParameter=someValue';
window.location.href = url;
Refreshing partial page
If you create an action that returns a partial view, you can refresh a portion of the page by replacing the content of some element with what is returned:
success: function (resp) {
$('#idOfSomeDiv').html(resp); // replace contents with returned
},
If the request points to a partial view, then resp will be the HTML fragment representing the partial view, and we need to take that and insert it somewhere on the page, which is what $('#idOfSomeDiv').html(resp); does.
There's lots of nuances to each of these approaches that's covered pretty thoroughly elsewhere, so I'm not going to get into that. Some versions of jquery for example require you parse the response before setting it in the DOM. I'll leave it up to you to research your method of choice further.
You're returning a ViewResult to a Javascript call. This won't refresh the view. The better option is to make your existing Controller route an HTTP GET that your refresh link reuses and passes the parameters set in your interface as query string parameters.
EDIT: When I've done this before, I've taken the HTML being returned from async route, and appended it into the DOM. This is different because you're looking to replace the DOM, but it's the same basic idea.
Here's some example Javascript that appends AJAX retrieved HTML into the page
window.EditableList = function (containerSelector) {
var $container = $(containerSelector);
var $list = $container.find("ol").eq(0);
$container
.off(".editable-list")
.on("click.editable-list", "a.add-editor-row", function (e) {
$.ajax({
url: e.target.href,
cache: false,
success: function (html) {
$list.append(html);
ShowHideIcons($("#question-style-dropdown").val());
}
});
return false;
})
.on("click.editable-list", "a.delete-editor-row", function (e) {
$(e.target).parents('li.editor-row').remove();
return false;
});
};
sample controller action
public ActionResult AddFragment()
{
var viewModel = new FragmentViewModel(parameters);
return PartialView("Fragment", viewModel);
}

MVC Html.ActionLink parameter values not being passed

I'm testing MVC for a demo and I managed to scrap together some pieces but now I am having trouble with an Html.ActionLink. The intent is to present the user with a series of dropdownlists that they must select before the ActionLink is shown. To do that I've copied some JQuery to hide/show my dropdownlists (as selections are made) and the ActionLink. I added an alert to my JQuery to check my values and via the alert it all looks good. But if I debug the controller the parm values are defaulted to 0. I'm not sure what code to include but I will try to include the relevant parts. I think it's something basic.
Here are the dropdown lists and ActionLink.
#Html.DropDownListFor(m => m.selected_env_ID, new SelectList(Model.Environments, "env_ID", "env_DESC"), "*Select an environment")
#Html.DropDownListFor(m => m.selected_app_ID, new SelectList(Model.Applications, "app_ID", "app_DESC"), "*Select an application",new { #hidden = "hidden" })
#Html.DropDownListFor(m => m.selected_job_ID, Enumerable.Empty<SelectListItem>(), "*Select a job", new { #hidden = "hidden" })
#Html.ActionLink("Submit", "Submit", new { id = Model.selected_job_ID, envid = Model.selected_env_ID }, new {id = "lnkSubmit" })
Here is the convoluted JQuery to hide/show and fill the cascading dropdowns.
<script>
$(document).ready(function ()
{
//Dropdownlist Selectedchange event
$("#selected_app_ID").change(function () {
var id = $('#selected_app_ID').val(); // id value
if (id == 0) {
$('#selected_job_ID').hide();
} else {
$('#selected_job_ID').show();
$("#selected_job_ID").empty();
$.ajax({
type: 'POST',
url: '#Url.Action("SelectJobs")',
dataType: 'json',
data: { id: $("#selected_app_ID").val() },
success: function (jobs) {
// jobs contains the JSON formatted list of jobs passed from the controller
$("#selected_job_ID").append('<option value=0>*Select a job</option>');
$.each(jobs, function (i, job) {
$("#selected_job_ID").append('<option value="'
+ job.job_ID + '">'
+ job.job_DESC + '</option>');
});
},
error: function (ex) {
alert('Failed to retrieve jobs.' + ex);
}
});
}
return false;
});
//ddl select change
$("#selected_env_ID").change(function () {
var name = $('#selected_env_ID option:selected').text(); //Item1
var id = $('#selected_env_ID').val(); // id value
if (id == 0) {
$('#divSubmit').hide();
$('#selected_app_ID').hide();
$('#selected_job_ID').hide();
} else {
$('#selected_app_ID').show();
}
});
//ddl select change
$("#selected_job_ID").change(function () {
var name = $('#selected_job_ID option:selected').text(); //Item1
var id = $('#selected_job_ID').val(); // id value
var envid = $('#selected_env_ID').val(); // id value
if (id == 0) {
$('#divSubmit').hide();
} else {
$('#divSubmit').show();
alert("envid=" + envid + " jobid=" + id);
}
});
}); // end document ready
</script>
My controller has this and id and envid end up being 0:
public ActionResult Submit(int id = 0,int envid = 0) {
If I need to include something else just let me know.
Here is the method that fills the job dropdown list. This works without issues. It's the Html.ActionLink call to Submit that fails to include the parameters.
public JsonResult SelectJobs(int id)
{
db.Configuration.ProxyCreationEnabled = false;
IEnumerable<t_job> jobs = db.t_job.Where(j => j.app_ID == id).ToList();
return Json(jobs);
}
Your link
#Html.ActionLink("Submit", "Submit", new { id = Model.selected_job_ID, envid = Model.selected_env_ID }, new {id = "lnkSubmit" })
is rendered on the server side before you make any selection in the dropdowns. If the initial values of selected_job_ID and selected_env_ID are zero or null, then those values will be passed to the controller (have a look at the rendered html).
If you want to pass the values selected in you drop downs, you could either modify the links href attribute in the drop down change events, or create a button instead of a link, and do a redirect in the buttons click event based on the dropdown values.
You need to use JSON.stringify():
data: JSON.stringify({ id: $("#selected_app_ID").val() }),

Categories

Resources