Hidden value not being set by jQuery? - javascript

I'm following along with an example from a Pluralsight class on Single Page in MVC and the instructor is using a hidden field to hold the "mode" the page is in. When the user click's the Add button it should set the "EventCommand" using some jQuery. However, I can't get mine to set.
Looking in the dev tools I don't see any errors. When I set some alerts inside the jQuery they will fire off so I know the jQuery is being called. When I veiw the page source I can see and input field with an a name of "EventCommand". It looks like it should be setup correctly but it's not setting the hidden field.
Anyone have an idea why this wouldn't be working?
ViewModel showing the properties as well as the HanndleRequest() which looks at the EventCommand to decide what to do but is NULL when the add button is clicked.
public string Mode { get; set; }
public string EventCommand { get; set; }
public string EventArgument { get; set; }
public void HandleRequest()
{
switch (EventCommand.ToLower())
{
case "list":
GetCalls();
break;
case "add":
Add();
break;
case "edit":
IsValid = true;
Edit();
break;
}
}
Top of View that has the HiddenFor and the Add button.
#using (Html.BeginForm())
{
<!-- BEGIN HIDDEN FIELDS AREA -->
#Html.HiddenFor(m => m.EventCommand)
#Html.HiddenFor(m => m.Mode)
#Html.HiddenFor(m => m.EventArgument)
<!-- END HIDDEN FIELDS AREA -->
<button id="btnAdd" class="btn btn-sm btn-success" data-cpp-action="add">
<i class="glyphicon glyphicon-plus"></i> Create New
</button>
jQuery that is at the bottom of the View. I get the alert that the click event happened but the alert with the data-cpp-action says undefined.
#section scripts {
<script>
$(document).ready(function () {
$("[data-cpp-action]").on("click", function (e) {
e.preventDefault();
alert("in click");
alert("action: " + $(this).data("data-cpp-action"));
$("#EventCommand").val(
$(this).data("data-cpp-action"));
$("#EventArgument").val(
$(this).attr("data-cpp-val"));
$("form").submit();
});
});
</script>
}

No need for the "data" in the data function. Only use "cpp-action":
<script>
$(document).ready(function() {
$("[data-cpp-action]").on("click", function(e) {
e.preventDefault();
alert("in click");
alert("action: " + $(this).data("cpp-action"));
$("#EventCommand").val($(this).data("cpp-action"));
$("#EventArgument").val($(this).attr("cpp-val"));
$("form").submit();
});
});
</script>
Also see the jQuery documentation: https://api.jquery.com/data/

Change
$(this).data("data-cpp-action"));
for
$(this).attr("data-cpp-action"));
This is why: jQuery Data vs Attr?

Related

Prevent javascript firing on load page

I have MVC application with JavaScript in the body of the cshtml page. In Model, I have a method that returns a string, and I want that string to add in some div on a page on click of a button. It works, but, the method is triggered every time I load the page (and I want it to be triggered only on click.
Here is code:
Model:
public class TestJS
{
public string Tekst1 { get; set; }
public string Tekst2 { get; set; }
public TestJS()
{
Tekst1 = "one";
Tekst2 = "two";
}
public string AddTekst()
{
return "three (additional text from method)";
}
}
Controller:
public class TestJSController : Controller
{
// GET: TestJS
public ActionResult Index()
{
Models.TestJS tjs = new Models.TestJS();
return View(tjs);
}
}
View:
#model TestJavaScript.Models.TestJS
#{
ViewBag.Title = "Index";
}
<script type="text/javascript">
function faddtekst() {
whr = document.getElementById("div3");
var t = '#Model.AddTekst()';
whr.innerHTML += t;
}
</script>
<h2>Testing JavaScript Firing</h2>
<p>
First to fields:
#Model.Tekst1;
<br />
#Model.Tekst2;
</p>
<form>
<input type="button" value="Click to show Tekst3" onclick="faddtekst()" />
</form>
<br />
<hr />
<div id="div3">
</div>
I tried to wrap JS in $(document).ready() with same result.
Somebody may think of this as a strange approach, but, a model method that I'm trying to execute takes over 10 seconds in real code, so, I want to prevent waiting every time page loads (waiting should be only if the user clicks button).
The strangest thing is that Model.AddTekst() is executed EVEN if I comment it in javascript function with '//'.
Anyone knows how to avoid unwanted execution of Model.Method?
The behavior you are experiencing is not strange at all. #Model.AddText() executes on the backend once the view is compiled which is normal behaviour.
A comment in razor would look like this
#* Comment goes here *#
But this is not what you want to achieve.
I'm afraid your approach wont work since you can't execute a method on a model asynchronously.
I suggest you take a look at Ajax.BeginForm - more info here
You could implement a controller action on the backend which would return the text you want to display on the submitting of the form.
Try to use e.preventDefault() for button click.
<form>
<input type="button" value="Click to show Tekst3" id="Show" />
</form>
Try with jQuery
$(document).on("click", "#Show", function (e) {
e.preventDefault();
faddtekst();
});

How can I generate a PartialView for each click of a button? [duplicate]

The problem I will be describing is very similar to ones I already found (e.g. this post with nearly identical name) but I hope that I can make it into something that is not a duplicate.
I have created a new ASP.NET MVC 5 application in Visual Studio. Then, I defined two model classes:
public class SearchCriterionModel
{
public string Keyword { get; set; }
}
public class SearchResultModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
}
Then I created the SearchController as follows:
public class SearchController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult DisplaySearchResults()
{
var model = new List<SearchResultModel>
{
new SearchResultModel { Id=1, FirstName="Peter", Surname="Pan" },
new SearchResultModel { Id=2, FirstName="Jane", Surname="Doe" }
};
return PartialView("SearchResults", model);
}
}
as well as views Index.cshtml (strongly typed with SearchCriterionModel as model and template Edit) and SearchResults.cshtml as a partial view with model of type IEnumerable<SearchResultModel> (template List).
This is the Index view:
#model WebApplication1.Models.SearchCriterionModel
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>SearchCriterionModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Keyword, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Keyword, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Keyword, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="button" id="btnDisplaySearchResults" value="Search" onclick="location.href='#Url.Action("DisplaySearchResults", "SearchController")'" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
<div id="searchResults">
</div>
As you can see, I added a div with id="searchResults" below the standard template and edited the button. What I want is to display the partial view SearchResults.cshtml in the div on the bottom, but only after the button is clicked. I have succeeded in showing a partial view there by using #Html.Partial("SearchResults", ViewBag.MyData), but it is rendered when the parent view is loaded for the first time and I set ViewBag.MyData in the Index() method already, which is not what I want.
Summary: On clicking the button, I will obtain some List of SearchResultModel instances (via database access) and then the partial view should be rendered, using this newly obtained data as model. How can I accomplish this? I already seem fail at the first step, that is reacting to the button click with the above code. Right now, I navigate to the URL ~/Search/DisplaySearchResults, but of course there's nothing there and no code-behind method is called.
In traditional ASP.NET I'd just have added a server-side OnClick handler, set the DataSource for a grid and show the grid. But in MVC I already fail with this simple task...
Update: Changing the button to #Html.ActionLink I can finally enter the controller method. But naturally since it returns the partial view, it's displayed as the whole page content. So the question is: How do I tell the partial view to be rendered inside a specific div on the client side?
Change the button to
<button id="search">Search</button>
and add the following script
var url = '#Url.Action("DisplaySearchResults", "Search")';
$('#search').click(function() {
var keyWord = $('#Keyword').val();
$('#searchResults').load(url, { searchText: keyWord });
})
and modify the controller method to accept the search text
public ActionResult DisplaySearchResults(string searchText)
{
var model = // build list based on parameter searchText
return PartialView("SearchResults", model);
}
The jQuery .load method calls your controller method, passing the value of the search text and updates the contents of the <div> with the partial view.
Side note: The use of a <form> tag and #Html.ValidationSummary() and #Html.ValidationMessageFor() are probably not necessary here. Your never returning the Index view so ValidationSummary makes no sense and I assume you want a null search text to return all results, and in any case you do not have any validation attributes for property Keyword so there is nothing to validate.
Edit
Based on OP's comments that SearchCriterionModel will contain multiple properties with validation attributes, then the approach would be to include a submit button and handle the forms .submit() event
<input type="submit" value="Search" />
var url = '#Url.Action("DisplaySearchResults", "Search")';
$('form').submit(function() {
if (!$(this).valid()) {
return false; // prevent the ajax call if validation errors
}
var form = $(this).serialize();
$('#searchResults').load(url, form);
return false; // prevent the default submit action
})
and the controller method would be
public ActionResult DisplaySearchResults(SearchCriterionModel criteria)
{
var model = // build list based on the properties of criteria
return PartialView("SearchResults", model);
}
So here is the controller code.
public IActionResult AddURLTest()
{
return ViewComponent("AddURL");
}
You can load it using JQuery load method.
$(document).ready (function(){
$("#LoadSignIn").click(function(){
$('#UserControl').load("/Home/AddURLTest");
});
});
source code link

Render partial view with form in Modal on Html.ActionLink click

I have a page with table where each row has a link "Move".
On clicking of the link I am trying to get the controller GET method called which will in turn render the _MoveReportPartial view in a modal.
Once the user makes the selections in the modal the submit button should post to the Post method of the controller.
If I remove the class attribute (move-modal) from Html.ActionLink(...), it in effect disengages the js file and ignores it. Then it works by opening the _MoveReportPartial in a new window and then consequently posting to the correct method if user clicks submit.
I am trying to get it to open in the modal, but the js I have doesn't work and routes to the POST method instead on "Move" click.
EDIT
Why does the .load call the POST method instead of the GET? How can I change the js? (added event.preventDefault();, still the same behavior is observed)
The move link on the originating view looks like this:
<div class="d20 actionLink">
#Html.ActionLink("Move", "MoveReport", "ReportsWeb", new {id = item.ReportDefinitionId, newReport = false}, new {#class = "move-modal"})
</div>
I have a js file:
$(function () {
$('.move-modal').click(function () {
event.preventDefault();
$('<div/>').appendTo('body').dialog({
close: function (event, ui) {
dialog.remove();
},
modal: true
}).load(this.href, {});
});
});
My ReportsWebController looks like this:
[HttpGet]
public ActionResult MoveReport(Guid id, bool newReport)
{
//some code
return PartialView("_MoveReportPartial", model);
}
[HttpPost]
public ActionResult MoveReport(MoveReportModel Model)
{
try
{
//some code
}
catch (Exception exc)
{
InternetReportingTrace.Source.WriteError(exc);
throw;
}
return RedirectToAction("ListReports");
}
and my _MoveReportPartial looks like this:
<div id="dialog-confirm">
<div align="center">
<h2>Please Confirm</h2>
</div>
#using (Html.BeginForm("MoveReport", "ReportsWeb", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div tabindex="-1" role="dialog">
<div class="modal-content">
<p>Report #Model.Report.Name with ID #Model.Report.ReportDefinitionId </p>
<p>Will be moved to:</p>
#for (int i = 0; i < Model.MoveOptions.Count; i++)
{
<div class="radio">
<label><input type="radio" name="optradio">#Model.MoveOptions[i]</label>
</div>
}
<div>
<input type="submit" value="Move Report" />
</div>
</div>
</div>
}
I don't think you're preventing the default behaviour of the ActionLink properly.
Try:
$('.move-modal').click(function (event) {
event.preventDefault();
$('<div/>').appendTo('body').dialog({
close: function (event, ui) {
dialog.remove();
},
modal: true
}).load(this.href, {});
});
Because it's a jQuery handler, you can use event.preventDefault() instead of return false;. If your click handler uses return false to prevent browser navigation, it opens the possibility that the interpreter will not reach the return statement and the browser will proceed to execute the anchor tag's default behavior before that point. Using event.preventDefault() as the first line in the handler means you can guarantee that the default navigation behavior will not fire.
Secondly, your .load method call is wrong.
Change
.load(this.href, {});
to
.load(this.href);
The documentation at api.jquery.com/load states "The POST method is used if data is provided as an object; otherwise, GET is assumed." Because you're sending it an empty object, it assumes you want to use POST. There's no need to do this.

Custom Validation in AJAX popup in mVC

I need to perform custom validation in AJAX popup in MVC. I have created a CustomValidator and am overriding the IsValid() method. Problem lies that the popup doesn't render the custom validations properly.
My code:
_Layout.cshtml
<!DOCTYPE html>
<html>
<head>
#Styles.Render("~/Content/css")
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.css" />
#Scripts.Render("~/bundles/jquery")
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script src="https://cdn.jsdelivr.net/jquery.ajax.unobtrusive/3.2.4/jquery.unobtrusive-ajax.min.js"></script>
#Scripts.Render("~/bundles/modernizr")
<script>
$(function () {
$("#modalDialog").dialog({
autoOpen:false,
width: 600,
height: 300,
modal: true
});
$("#opener").click(function () {
$("#modalDialog").dialog("open");
});
});
function OnSuccess(response)
{
$("#modalDialog").text(response);
}
</script>
</head>
Index.cshtml:
#model MvcPopup.Controllers.HomeController.SomeModel
#{ViewBag.Title = "Home Page";}
<p>This is page content !!!</p>
<button id="opener">Open Dialog</button>
<div id="modalDialog" title="Basic Modal Dialog">
<p>This is a basic modal dialog</p>
#using (Ajax.BeginForm("Index", new AjaxOptions { UpdateTargetId = "ID", OnSuccess = "OnSuccess" }))
{
<div>
<fieldset>
<legend>Info</legend>
#Html.LabelFor(m => m.EmailAddress)
#Html.TextBoxFor(m => m.EmailAddress)
#Html.ValidationMessageFor(m => m.EmailAddress)
<input type="submit" value="Submit" />
</fieldset>
</div>
}
</div>
HomeController.cs:
public class HomeController : Controller
{
public class SomeModel
{
[CustomValidator]
[Display(Name = "Email address")]
public string EmailAddress { get; set; }
}
public ActionResult Index()
{
var model = new SomeModel();
return View();
}
[HttpPost]
public ActionResult Index(SomeModel model)
{
if (ModelState.IsValid)
{
return PartialView();
}
return PartialView();
}
CustomValidator.cs:
public class CustomValidator : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
if (value.ToString().Contains("X"))
{
return ValidationResult.Success; ;
}
else
{
return new ValidationResult("Enter an email address with letter X");
}
}
else
{
return new ValidationResult("" + validationContext.DisplayName + " is mandatory.Please enter it.");
}
}
}
I can see that the Custom Validations are fired, but the render on the popup page is little bit crooked. Appears like below:
Now, I don't enter any value in the Email textbox, and click on Submit, I can see my custom validation "Email Address is mandatory. Please enter it." gets fired and is evident on the popup page as below:
I mean, its pretty evident that the custom validation should appear near to the email textbox and things should look like a popup. Please help.
EDIT
Validations appear in the popup page, but the content is somewhat crooked. I mean the content of the parent page too appear on the popup page. Snapshot below. How to get rid of it? Please help.
This is a typical and seen quite some time often. The reason that validation didn't fire is because the content like modal dialog is dynamic which is added/appeared on page later. The form validation has to parse the form again to validate the controls that are added dynamically to the page.
You have setup OnSuccess() so you can use it to parse the form.
function OnSuccess()
{
if($.validator)
{
$.validator.unobtrusive.parse("form");
}
$("#modalDialog").html(response);
}
Visit here for more info.
Note that, this solution assumes that your validation is working fine.

Step by step application with partial views

So I have an application broken down into sections. These sections I put in there own partial views(keep in mind I can do it what ever way is best just though partial view might be that way for content management). I have a main view that contains all of these partials. Now I would like a way to only view one at a time based on a user clicking on a button to go to the next step.
Fill in name
Name:
Steve
button: Next Step
when the client clicks the button next step it will cause the partial view to change from step 1 to step 2. etc etc.
I am having a lot of trouble wrapping my head around this. I have tried calling a viewbag.step = "0" and in the onclick for the buttons doing a javascript for viewbag.step = "1" and in the layout view doing a condition for if viewbag.step == "0" show step 1 if viewbag.step == "1" show step 2 etc etc but that doesn't work because of a reference issue.
You could render a div with an ID within each partial and then have the onclick set the next partial to visible, so to speak. You'd have to include jQuery for this example.
Something like this:
Main CSHTML
#using(Html.BeginForm())
{
#Html.RenderPartial("_PartialView1");
#Html.RenderPartial("_PartialView2");
....
<button onclick="setPage()" >Click me</button>
<script type="text/javascript">
var pageNum = 1;
function setPage()
{
var oldPageId = "#Partial" + pageNum;
pageNum++;
var idToSet = "#Partial" + pageNum;
// toggles visibility
$(oldPageId).toggle();
$(idToSet).toggle();
}
</script>
}
And then your partials like:
<div id="Partial1">
<input type="text" id="Text1"></input>
</div>
<div id="Partial2" style="visibility:hidden">
<input type="text" id="Text2"></input>
</div>
Etc...
Considering you have 3 sections Section 1,Section 2,Section 3.
Write 3 action methods that return partial view.
[HttpPost]
public ActionResult Section1Details(Section1 data,string prevBtn, string nextBtn)
{
if (nextBtn != null)
{
if (ModelState.IsValid)
{
// Do the logic
return View("Section 2");
}
}
return View();
}
[HttpPost]
public ActionResult Section2Details(Section2 data,string prevBtn, string nextBtn)
{
if (prevBtn!=null)
{
// wirte logic here
return View("Section1",bd);
}
if (nextBtn != null)
{
if (ModelState.IsValid)
{
// Do the logic
return View("Section3");
}
}
return View();
}
[HttpPost]
public ActionResult Section3Details(Section3 data,string prevBtn, string nextBtn)
{
if (prevBtn!=null)
{
// wirte logic here
return View("Section2",bd);
}
if (nextBtn != null)
{
if (ModelState.IsValid)
{
// Do the logic
// Save changes
return View("Success");
}
}
return View();
}
In your view,
#using (Html.BeginForm("Section1", "Home", FormMethod.Post))
{
<h1>Step 1 : Basic Details</h1>
#Html.LabelFor(m=>m.Name)<br />
#Html.TextBoxFor(m=>m.Name)
#Html.ValidationMessageFor(m=>m.Name)<br />
<br />
<input type="submit" name="nextBtn" value='Next Step' />
}

Categories