MVC controller method not being triggered by jquery in the view - javascript

I have the following method in a controller called "ProductController":
public ActionResult LoadProducts(int prodID)
{
return View();
}
I'm trying to trigger it from a view cshtml page this way:
#section Scripts {
<script type="text/jscript">
$('#MyProducts').change(function () {
var selectedID = $(this).val();
$.get('/Product/LoadProducts/' + selectedID, function (data) {
window.alert(selectedID);
});
});
</script>
}
<div>
#using (Html.BeginForm("Update", "Product", FormMethod.Post, new
{ enctype = "multipart/form-data" }))
{
#Html.DropDownList("MyProducts",
(IEnumerable<SelectListItem>)ViewBag.MyProducts as
IEnumerable<SelectListItem>, "Select")
}
</div>
The call to the jquery works when I change the value in the drop down, as I tested it via the popup box, but getting it to trigger the action method in the controller is not working.
Thanks.

In your controller, you can use HttpGetArribute to define the route. Without specifying the route, the parameter is considered as optional and it should be called as Product/LoadProducts?prodId=1. Example of HttpGetAttribute:
[HttpGet("[controller]\[action]\{prodId}")]
public ActionResult LoadProducts(int prodID)
{
return View();
}

set Url by Url.Action
var Url='#(Url.Action("ActionName","ConttrolerName"))';
and Put the variable name of the sent with the same variable received
SelectedID to ProdID
<script type="text/jscript">
$('#MyProducts').on("change",function () {
var Url='#(Url.Action("LoadProducts","Product"))';
var SelectedProdID = $(this).find("option:selected").val();
$.get( Url,{prodID:SelectedProdID}, function (data) {
window.alert(selectedID);
});
});
</script>

Suggestion: use script type="text/javascript". I am not an expert on which browsers support "jscript" but after 20 years of development I can assure you all browsers support javascript.
Also, I would discourage you from using the code which Farhad Bagherlo posted. If at all possible you should avoid using razor code inside your script tag because you may want to move this code into separate JS files or later on refactor to use TypeScript. Also, why invoke a method on the server to get an endpoint/url if you already know the path which is needed. seems wasteful. However, you could use the method he outlined to ensure that you are actually giving the correct URL. If his code works then what is the value of "Url"? (also, the client side standard for naming variables is camelCase, so url should be lower.)
If you are debugging your code and set a breakpoint in your controller. then you should be able to get it to break on that line by simply navigating to that route.
If you go to http://localhost:post/Product/LoadProducts/1 does it actually break on that line in Visual Studio?
Edit: #Transcendent is correct and would get my vote, need to understand how routing is defined vs arguments/parameters passed to the action method. Nice call Transcendent!

I agree with Collin on that using razor in your javascript marries them and that can be a pain if you try to split the js into its own file. I recently had to go through it.
Edit: This is just to show how to use Url.Action and still be able to separate js into a separate file by the use of data property in the div
What I suggest is doing something like:
#section Scripts {
<script type="text/javascript">
$('#MyProducts').change(function () {
var url = $('#mydiv').data('url') + '?prodId=' + selectedID;
var selectedID = $(this).val();
$.get(url, function (data) {
window.alert(selectedID);
});
});
</script>
}
<div id="mydiv" data-url="#(Url.Action("ActionName","ConttrolerName"))">
#using (Html.BeginForm("Update", "Product", FormMethod.Post, new
{ enctype = "multipart/form-data" }))
{
#Html.DropDownList("MyProducts",
(IEnumerable<SelectListItem>)ViewBag.MyProducts as
IEnumerable<SelectListItem>, "Select")
}
</div>

Related

How can I access Laravel Helper from Javascript?

I have a Laravel web app and want to access my Laravel Helper from any .js file.
In normally I accessed helper from script in blade.php file like this:-
<script>
function getSiteSettings() {
return JSON.parse('<?php echo json_encode(Helper::getSettings()) ?>');
}
</script>
But I want to access this from my script.js file. Is it possible?
I have tried with localStorage and get it very easily but problem is, in site settings have some secured data.
And if possible to hide localStorage data then my problem will be solved.
I found the answer. Basically, there is no direct way to write PHP code in the .js file. Here is 3 short way to get PHP/Laravel variable or Laravel Helper.
Solution 1. Call an ajax and get the return the value from js and use it as needed.
$(function() {
// ajax call
});
Solution 2. Any Laravel Helper/Var can be accessible from a blade file, like this:
<script>
let name = "{{ \Helper::get_name() }}";
</script>
and use this name anywhere in js.
Solution 3. The last and best way which I have been using is:
Add the external script in the blade and add a getter and setter method into the script so that you can set the value from the blade file inside the js.
// in external/script.js script
var GetLaravelHelper = function () {
let options = {
name_from_laravel : ''
}
return {
init: function(){
// code for on load
},
set_options: function(data){
Object.assign( options, data );
},
get_options: function(){
return options;
}
}
}();
window.GetLaravelHelper = GetLaravelHelper; // You can get acces this GetLaravelHelper into balde file where you imort this js
document.onload(function(){
GetLaravelHelper.init();
})
Then in blade, you can set any value into the Object like this:
<script src="external/script.js"></script>
<script>
GetLaravelHelper.set_options({
name_from_laravel: "{{ config('app.name') }}", // or anything from laravel/php
})
</script>
Hope it will help you a lot. Happy Coding <3
I think your best bet is to do what you're doing right now.
Or, Create a resource endpoint that can return JSON of these settings on page load in your script.js file:
// script.js
document.addEventListener("DOMContentLoaded", function() {
// make an ajax request here and load your setting in a js variable
});
// if using jquery
$(function() {
// make an $.ajax request here and load your setting in a js
})
The easiest solution will be call an api using Ajax or Axios(is using vue) inside the getSiteSettings function and in the response of the api you return the data.

C# MVC not calling correct action method when adding form action dynamically through JavaScript

First of all, please forgive me if this looks a silly question but I am not being able to solve this.
I have a MVC page with the following structure (not using HTML.BeginForm):
<form id="frmMiniSearch" method="post">
... search controls are here ...
<%: Html.DropDownListFor(model => model.VehicleCondition, Model.Condition, new {#class = "form-control",#required="true"}) %>
<input type="submit" value="Search" />
</form>
<script type="text/javascript">
jQuery(document).ready(function(){
$("#VehicleCondition").bind("change", function(){
$("#frmMiniSearch").attr("action", "/quick-search/used/honda/civic/1968-2018")
});
});
</script>
When the above JavaScript executes, I can see the form (frmMiniSearch) action changes to /quick-search/used/honda/civic/1968-2018. But when I click on the Submit button, rather than calling the right action method, it is calling some other.
The above URL pattern is set in my RouteConfig.cs file:
routes.MapRoute(
name: "VehicleMiniSearchResult",
url: "quick-search/{cond}/{makeSlug}/{model}/{yearRange}",
defaults: new { controller = "SearchResult", action = "VehicleQuickSearch", id = UrlParameter.Optional }
);
I also tried this:
$(document).ready(function(){
$("submit").click(function( e ) {
e.preventDefault();
$("#frmMiniSearch").submit();
});
});
Result is the same. Still calling wrong method.
What I am doing wrong? Any suggestion would save my life.
NB: If I want to use Html.BeginForm to call the right method, how do I call it using the route pattern (i.e. quick-search/used/honda/civic/1968-2018)? Something like:
<% using(Html.BeginForm, "Action", "Controller", <something here?>) {} %>
I tried Html.BeginRouteForm but that did not seem to work either.
Thank you for your suggestion.
EDIT:
Here are the routes in the order of they appear in RouteConfig.cs
routes.MapRoute(
name: "VehicleSearchResult",
url: "vehicles/search/",
defaults: new { controller = "SearchResult", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "VehicleMiniSearchResult",
url: "quick-search/{cond}/{makeSlug}/{model}/{yearRange}",
defaults: new { controller = "SearchResult", action = "VehicleQuickSearch", id = UrlParameter.Optional }
);
As far as I know route order is something we need to keep in mind in Orchard CMS, but does this true for pure MVC applications as well?
By the way, when I type in the URL in browser directly the route (quick-search/used/honda/civic/1968-2018) works and I get the desired result. Then it is not calling another method. It is only happening when I am trying to submit the form via JS.
This is not the accurate solution, its just a suggestion.
Why can't you use static action and pass the parameter in query string or in body?
in that way you can avoid dynamic uris.
or call the uri from the javascript function as
$(document).ready(function(){
$("submit").click(function( e ) {
e.preventDefault();
//$("#frmMiniSearch").submit();
//Call your uri here like window.location="";
});
});

Pass hidden model data to the controller using ajax in mvc

Once again stumped on a partial view issue using ajax to swap out partial views.
I have a partial view that is loaded onto a page when the page is loaded. This partial view has a button on it that when clicked, will replace that partial view with another partial view. Previously I was accomplishing this with #Ajax.ActionLink and passing in the page's Model as a parameter to be passed to the controller. This was working correctly.
However, the next step was to fill in a form on the new partial view and submit it which would return another partial view. I asked how to do that on here and got it to work using jquery event delegation.
Now I'm trying to replace the #Ajax.ActionLink with a $.ajax function and am running into an issue with my js script where the model data already in the view or partial view is not being passed to the controller by the js.
Here's what I mean:
I have a page called ReviewPage that on load, will call an action that returns a partial view. This partial view is determined based on a value from the page's model that is passed to the controller. More often than not, the view returned is _NoNotes:
#model GuestPointerAppV4.Models.ViewModels.NotesOnCompanyViewModel
<div id="no_company_notes">
<h4>Notes on Company</h4>
<div class="row col-sm-6 col-sm-offset-6">
<div class="text-center">
#Html.HiddenFor(item => Model.RequestId)
<div class="primary-action-bttn-border">
<div class="primary-action-bttn-bkg">
<button data-gp-ajax="true" data-gp-target="#no_company_notes" value="Save" action="#Url.Action("_CompanyNotesEditGet", "NewSignUpRequestReview")" method="get" class="btn btn-default primary-action-bttn">
Add Note
</button>
#*#Ajax.ActionLink("Add Note", "_CompanyNotesEditGet", Model, new AjaxOptions { UpdateTargetId = "company_notes", InsertionMode = InsertionMode.Replace, HttpMethod = "GET" }, new { #class = "btn btn-default primary-action-bttn" })*#
</div>
</div>
</div>
</div>
<div class="row text-center padding-25-top">
<p>There are no notes for this company. Do you want to add some?</p>
</div>
</div>
You'll notice the commented out ajax action link in the code above. This was how I was previously able to get the model passed to the controller.
When the button is clicked on this partial view, the following js responds:
$(document).on('click', 'button[data-gp-ajax="true"]', function () {
var $form = $(this);
var options = {
url: $form.attr("action"),
type: $form.attr("method"),
data: $form.serialize()
};
$.ajax(options).done (function (data) {
var $target = $($form.attr("data-gp-target"));
$target.replaceWith(data);
});
return false;
});
I want to reuse this js as much as possible so I utilize custom data- attributes to help call the right function (data-gp-ajax="true") and tell the script where to return the results (data-gp-target="#targetid").
The problem is that in debugging this, I found that while Model.RequestId is not 0 on the page, this value is not getting picked up by the js and passed to the controller. So, when my controller looks for the data it doesn't find it and returns a null or 0 depending on the type of data I'm trying to pass.
I did a bit of research and tried to encode the Model in the partial view itself using the following at the top of the partial view's code just after the model typing:
#{
var val = Json.Encode(Model);
}
<script type="text/javascript">
var model1 = #Html.Raw(val)
</script>
Then, in my js function, I tried passing model1 to the controller by using it like so:
$(document).on('click', 'button[data-gp-ajax="true"]', function () {
var $form = $(this);
var $model = $(model1);
var options = {
url: $form.attr("action"),
type: $form.attr("method"),
data: $model.serialize()
};
$.ajax(options).done (function (data) {
var $target = $($form.attr("data-gp-target"));
$target.replaceWith(data);
});
return false;
});
I also tried data: $model and data: $model.first() but no such luck. I managed to debug and see the data I was expecting to see in the js on the $model variable so I know it's getting in there but it isn't getting passed to my controller when it's called.
Really what I'm looking for is a clean and reliable way way to pass the Model for the page into the JavaScript and have it pass it along to the Controller to execute some action on before a partial view is returned to replace the original partial view.
Thoughts?
Update
Here is an example of the controller:
[HttpGet]
public ActionResult _CompanyNotesEditGet(NotesOnCompanyViewModel notesOnCompanyViewModel)
{
//Some actions are taken on the model passed in and then repopulated to notesOnCompanyViewModel
return PartialView("~/Views/NotesOnCompany/_CompanyNotesEdit.cshtml", notesOnCompanyViewModel);
}
However, when I debug and the action is called by $.ajax, the notesOnCompanyViewModel is blank. Even when I can see there is data that should be getting passed by the js function.
All you need is to send the unique Id. for that, you can simply override the button click event and make an ajax call with the Id in the request url. If you would like to take advantage of the Url helper method to generate the correct relative path to the action method, you can do that and set the result of that to html5 data attribute on the button.
<button id="addNote"
data-url="#Url.Action("_CompanyNotesEditGet", "NewSignUpRequestReview",
new { id=Model.RequestId})" >
Add Note
</button>
Assuming your _CompanyNotesEditGet action method accepts this Id and return the value as needed
public ActionResult _CompanyNotesEditGet(int id)
{
var vm= new NotesOnCompanyViewModel { RequestId=id };
//load other properties of vm as needed.
return PartialView(""~/Views/NotesOnCompany/_CompanyNotesEdit.cshtml",vm);
}
And the script to ajaxify the click event. You can use the load method to make the ajax call and update the DOM element.
$(function(){
$(document).on("click","#addNote",function(e){
e.preventDefault();
var url=$(this).data("url");
$("#no_company_notes").load(url);
});
});
If you are editing a note, Inside your action method, you can read your note entity from the db using the Id passed in.
This should work assuming you have no script errors in the page.

Getting parameters from query string

I am creating a web page using ASP.Net WebAPi, MVC and Knockout.
I have a normal MVC controller that loads the pages when I need them:
[Authorize]
public class AdminController : Controller
{
public ActionResult Clients()
{
return View();
}
public ActionResult ClientEdit(int? Id)
{
return View();
}
}
And once the page is loaded, my Knockout model takes care of the loading of the data. So, the 'Clients' controller simply loads a list of all clients. When on that screen, a user can click 'Edit' next to a client, and the page is navigated to the 'ClientEdit' controller, which takes an id.
So, my knockout click event looks like this in my knockout view model:
self.EditClick = function () {
if (this.ClientId && typeof this.ClientId !== 'undefined') {
window.location.href = "/Admin/ClientEdit/" + this.ClientId;
}
else
window.location.href = "/Admin/ClientEdit/";
}
(It handles the 'Create New' button and the edit button, hence the 'if')
Once I redirect, the MVC controller loads the page, and the URL is:
http://localhost:49389/Admin/ClientEdit/1
I then load the knockout model, and would like to make an API call to get the data...
After my page loads, I want to bind the view model to the page. Here's my view model at the moment:
function AdminClientEditor() {
var self = this;
self.Name = ko.observable("");
self.ContactName = ko.observable("");
ko.applyBindings(new AdminClientEditor(), $("#clienteditor")[0]);
So, I will create a $.get method that calls a webAPI method that will return me data based on the id. I just need to get the ID somehow.
But, how do I get the Id (In this case, '1', from the URL?
And, is this the right way to achieve what I am trying to do?
You can pass the id value to view via viewbag.
public ActionResult ClientEdit(int? Id)
{
ViewBag.ClientId=id;
return View();
}
and in the view's script section
var clientId="#ViewBag.ClientId";
alert(clientId);
// use this
If your javascript code which accesses this id value is inside a separate external js file, you may set this value to a js variable in your view and access it in your js file. Make sure to use namespacing to avoid global variable overwriting value issues.
So in your view
<script>
var myApp = myApp || {};
myApp.ClientId= "#ViewBag.ClientId";
</script>
<script src="~/Scripts/PageSpecificExternalJsFile.js"></script>
And in the PageSpecificExternalJsFile.js file,
var clientId=myApp.ClientId;
//use this as needed
I'm not sure if this is the best way, but you can get the ID from the URL by using JS:
var id = GetID();
function GetID() {
var href = location.href;
var results = href.split("/");
return results[results.length - 1];
}
I've come up with this solution which works, but I am unsure if it's the best way. It seems pretty good.
I created a MVC ViewModel class in my application code, called 'GenericParameteModel', which at the moment, has a single parameter, "Id".
I then modified my page loading MVC method:
public ActionResult ClientEdit(int? Id)
{
var mv = new GenericParameteModel { Id = Id };
return View(mv);
}
On my View page, I added the model 'GenericParameteModel' to the View.
I created a hidden field, called 'ClientId' on the view.
<input type="hidden" id="clientId" value="#model.Id">
Then, within my knockout view model, I check if $("#clientId").val() has a value. If so, I do the $.get call, using that value, and populate my view model.
In doing so, all my initial page loads from MVC will have the ability to you the GenericParameteModel, and it will be a pattern for other pages. As it's a model, I can add new fields as my application requires.
This seems to work well. I'm unsure if this is an acceptable way as I am new to this (MVC to load views and the Knockout/WebApi to get the data after loading). But it seems neat and manageable.

Call an action from JS file instead of the view (MVC 4)

I'm using the MVC 4.
In my view i can simply get an action's url by using the: #Url.Action
Now i wanted to make a javascript file with all the view's javascript instead of writing it all in the view, the problem is i can't use the razor's stuff anymore.
so my question is how can i get the action's url from a javascript separated file?
You'll need to define a JavaScript variable within your view that you can then use in your script. Obviously this must be declared first.
I use a helper on my layout pages that has all these variables and a section for any I'd want specific to a page. Note these would come before any other script references before the body tag.
#Scripts.Variables()
#RenderSection("ScriptVariables", false)
The Scripts.Variables is something like this
#helper Variables()
{
<script language="javascript" type="text/javascript">
var ActionGetallAdmin = '#Url.Action("GetAll", "Admin")';
var ActionAccountLogin = '#Url.Action("Login", "Account")';
</script>
}
One way I did this before was to create views that served JS files (and CSS files, actually), instead of HTML files. This leverages the fact that views aren't necessarily HTML files all the time in the MVC paradigm.
You could do this by creating a controller for it:
public class AssetController : Controller {
protected void SetMIME(string mimeType) {
// implementation largely removed
this.Response.Headers["Content-Type"] = mimeType;
this.Response.ContentType = mimeType;
}
// this will render a view as a Javascript file
public void ActionResult MyJavascript() {
this.SetMIME("text/javascript");
return View();
}
}
Once you've done that, you can create a view (using the way you normally do it in ASP.NET MVC), and just write it up as Javascript. Remember not to use a layout, as you obviously don't want that.
Everything that views in MVC has to offer is available to you, so feel free to use models, et al.
#model IList<Entity>
#{
Layout = null;
}
(function ($) {
// javascript!
#foreach(var entity in Model) {
$('##entity.Id').on('click', function () {
console.log('#entity.Name');
});
}
})(jQuery);
Then you can wire that up using old-fashioned Razor in your other views.
<script src="#Url.Action("MyJavascript","Asset")"></script>
Which will roll out something like
<script src="http://your.domain/asset/myjavascript"></script>
Works like a charm. The views are dynamically created, of course, so be wary if you're nit-picky about that. However, since they are MVC controller actions and views, you can set cache options on them just as with any other view.
Uhm... I think you can define a special route, like "actionsjs", that points to an action.
routes.MapRoute(name: "actionsJs",
url: "actionsjs",
defaults: new { controller = "Home", action = "GetActions" });
In the action you've to set the content to the right type:
Response.ContentType = "text/javascript";
Then you'll return a specific View that will contains javascript code with some Razor inside.
#{
Layout = "";
}
$(function() {
var a = #(1 + 2);
});
At this point you'll able to add this "script file" to your site:
<script type="text/javascript" scr="#Url.Action("GetActions", "Home")"></script>
Should work.
If you want the root path, use a variable on layout and use that in JavaScript file, say
// In layout view
<script>
var rootPath = #Url.Content("~/")
</script>
User rootPath anywhere in your application JavaScript files
If you want to get full path of a controller with action then
// View
<script>
var url = #Url.Content("ActionName", "ControllerName")
</script>
use url in your JavaScript file.

Categories

Resources