I'm working on a C# MVC 5 application that uses Bootstrap. I have a view where the user can fill in some input boxes and confirm entry with a button. It's pretty straight forward stuff.
Anyway, depending on whether the user interaction is an edit or an addition, I show a modal asking them if they like to proceed to the following page that relies on data from the page that they're on.
I.e.
If it's an edit, confirm button posts and sends user back to previous page.
If it's an add, confirm button shows modal dialog asking if they'd like to proceed to adding other stuff which directs the user to another page or sends them to previous page.
I have the following:
<button type="button" id="button_submit" class="btn btn-success" data-toggle="modal" data-target="#questionModal" data-property-one="#Model.Property1" data-property-two="#Model.Property2">
<i class="glyphicon glyphicon-ok" aria-hidden="true"></i>
Accept
</button>
Which uses the following jquery
$('#questionModal').on('show.bs.modal', function(event) {
var button = $(event.relatedTarget);
var modal = $(this);
// Extract info from data-* attributes
var modelProperty1 = button.data('property-one');
var modelProperty2 = button.data('property-two');
// Update the modal's content.
modal.find('#something').val(modelProperty1);
modal.find('#somethingElse').val(modelProperty2);
});
I don't want the question modal to be displayed if required fields are empty.
I have validation in place on all required input fields and a nice warning is shown after a post back to the server. What I need is to be able to do some poor-man's client-side validation and if a required field is not filled-in, post back to the server, ModelState will be invalid which will just return the same view and display error messages and not show this question modal.
Something like
#if (#Model.Property1 != null && #Model.Property2 != null)
{
<button type="button" id="button_submit" class="btn btn-success" data-toggle="modal" data-target="#questionModal" data-property-one="#Model.Property1 " data-property-two=#Model.Property2">
<i class="glyphicon glyphicon-ok" aria-hidden="true"></i>
Accept
</button>
}
else #*post back to controller, reconstruct view, and show errors*#
{
<button type="submit" class="btn btn-success">
<i class="glyphicon glyphicon-ok" aria-hidden="true"></i>
Accept
</button>
}
or perhaps go to a JavaScript function on button click and do something like this instead:
function stuff()
{
var modelProperty1 = //get #Model.Property1 as it is on client somehow
var modelProperty1 = //get #Model.Property2 as it is on client somehow
if (!modelProperty1 || !modelProperty2)//if validation fails
{
$("form").submit();//post and refresh showing errors
}
else
{
//set modal properties
var modal = $('#questionModal');
modal.find('#something').val(modelProperty1);
modal.find('#somethingElse').val(modelProperty2);
//show modal
modal.modal('show');
}
}
But I'm unsure as to how to get the current values of the input fields as they are client-side.
E.g. var modelProperty1 = #Html.Raw(Json.Encode(#Model.Property1)); is null even when the user has typed something into its input box...
I'm not extremely experienced but I'm learning so I hope I'm not appearing to come across silly with these questions.
Thanks so much for you help :)
You could role your own JS validation or use a library (almost always recommended) such as http://formvalidation.io.
You can't get the values of #Model.Property directly, as that is server side. You need to get them from an element on the page on the client side. You can use jQuery val() as you have done on the model: http://api.jquery.com/val/
Something like:
var modelProperty1 = $("#Property1").val();
Not sure if I am missing something. If you don't have Property1 as a form element somewhere you could write it to a hidden field using
#Html.HiddenFor(m => m.Property1)
Related
I'm having some issues with automating a website using PhantomJS/Selenium WebDriver in C#.
Part of the website has a check on addresses; entering in an address it can't verify (or just not entering an address at all) brings up a modal div with buttons in it. Depending on the situation, it will either give you the closest address, or the option to just continue without an address, but the button always has the same id. The HTML for this div (for the "no address entered" version) is here:
<div id="confirmAddress" class="col-xs-5 active-popupbox">
<div class="row vert-offset-top-2">
<p style="margin-left:55px;" class="h3">No Customer Address Provided</p>
<br>
<div style="margin-left:42px;margin-bottom:0px;" class="modal-body">No Customer Address Was Entered. We recommend having the Customer provide an address.</div>
<br>
<div class="col-xs-6">
<button type="submit" class="btn btn-default" style="margin-left:45px;width:200px;" id="confirmBack" name="confirmBack" align="center">Enter Address</button> </div>
<div class="col-xs-6">
<button type="submit" class="btn btn-default" id="confirmPopUp" name="confirmPopUp" align="center">Proceed with No Address</button>
</div>
</div>
</div>
The button I need to click is the last button, <button type="submit" class="btn btn-default" id="confirmPopUp" name="confirmPopUp" align="center">Proceed with No Address</button>
However, despite seemingly being able to find the element in my code and calling Click() on it, grabbing screenshots show that it apparently doesn't register the click since the modal popup stays on the page and the next part errors out due to not finding the elements I'm asking it to find.
Here is the relevant portion of my code:
File.WriteAllText(#"C:\Users\user1\Documents\Testing\source0.html",
_driver.PageSource);
_driver.FindElementById("contactinfo_next").Click();
if (_driver.FindElementByClassName("active-popupbox").Displayed)
{
_driver.GetScreenshot().SaveAsFile(#"C:\users\user1\documents\testing\screenshot01.png", ImageFormat.Png);
_driver.FindElementById("confirmPopUp").Click();
}
_driver.GetScreenshot().SaveAsFile(#"C:\users\user1\documents\testing\screenshot02.png", ImageFormat.Png);
I use the class name to find the div since they apparently have different id's depending on whether the address was "not provided" or "wasn't verifiable but a close match was found", but the class is always the same.
I have PhantomJS grabbing the source before clicking the "contactinfo_next" button, which is what brings up the modal div, and have confirmed that no div with class "active-popupbox" exists before the contactinfo_next button is clicked. "screenshot01.png" does get saved, which means that it is finding the div... but it still doesn't register the button click.
I have tried clicking other elements in the div to bring it "into focus", calling Click() twice on the button, assigning the div to a variable called div and then finding the button using div.FindElements(By.Id("confirmPopUp")) and clicking on each element in the collection, nothing has worked.
Has anyone else run into this issue?
Thanks!
EDIT: I'm using the following NuGet packages for this:
PhantomJS
Selenium WebDriver PhantomJS cross platform
Selenium WebDriver Support Classes
Selenium WebDriver
Okay so I don't know if this is specific to this site that I'm trying to automate, but resizing the PhantomJS viewport worked - in my screenshots, the modal's buttons were overlapping each other. Not sure why it matters but this is what ended up solving my problem. ¯\_(ツ)_/¯
To resize the viewport (requires System.Drawing):
_driver.Manage().Window.Size = new Size(1920, 1080);
Hope this helps anyone else who has a similar problem in the future
I have a very weird problem, and I am looking for guidance on how to best debug similar problems in the future.
I have a Simple form that deletes the current active order.
When the user clicks the button, i open a Modal and ask for confirmation.
<form action="/KundeOrdre/Slet" id="Form-SletKundeOrdre" method="post">
<input type="hidden" value="1010101010" name="KundeOrdre.Hoved.OrdreNummer" />
<input type="submit" id="sletKundeOrdre" data-target="Form-SletKundeOrdre" value="Slet" class="btn btn-primary btn-xs" />
</form>
I have two Javascript methods in place.
1. To catch the click on the button and show the Modal instead
2. Catch the confirm click and submit the from
// Show Confirm Dialog when clicking the Delete Buttont
$(function () {
$('#sletKundeOrdre').on('click', function (e) {
e.preventDefault();
$('#modal-confirmDeleteOrder').modal('show');
});
});
// Bind a Listener to the Confirm button
$('#modal-confirmDeleteOrder').on('click', 'button#confirm', function (e) {
$('#Form-SletKundeOrdre').submit();
});
When i run this setup and click confirm in the Model, i get a Javascript error and the Form is not submitted.
jquery.validate.js:1068 Uncaught TypeError: Cannot read property 'settings' of undefined
I am looking for good ideas on how to debug a situation like this.
A plugin is giving me trouble and im assuming its on my end.
This form is included in a page that consists of 2 Partial Views, each containing multiple forms.
This particular form is in the 2nd View and is nr 3 out of 4 forms on that page.
Another observation:
The 4th Form in the same View is a table containing rows. This error happens when the table contains zero rows.
If i add a single row to the 4th Form, this form (3rd) works as expected.
I do not understand why submitting my 3rd form is in any way related to my 4th form.
Update:
I seem to have resolved the issue.
I had an Input button inside my form, that had a link to the 'GemKundeOrdreLinjer' form (my 4th form).
<input type="button" id="gemKundeOrdre" value="#Resources.Panel_KundeOrdreGem" form="GemKundeOrdreLinjer" class="btn btn-primary btn-xs" />
Moving this button outside my form, solved issue for now.
This leaves me with a display issue about buttons, but ill keep this Question updated
On a web page, I have some "Ignore this person" type of buttons. I'm attempting to use a pop up confirmation form which opens after a user clicks one of these buttons. I have 1 pop up confirmation form per "potential ignoree". These popups work as intended the first time, but once a popup form's action is carried out, I cannot get another such form to open/display by a subsequent button click. Here is my pop up form html and razor code:
<div data-role="popup" id="showIgnoreDialog#(i)" data-theme="a" style="max-width:400px;" class="ui-corner-all">
<form action="~/DisplayNames?ind=#passedIndex&sN=#whoToIgnore&tId=-2" id ="ignoreFormId" method="get">
<div data-role="header" data-theme="a" class="ui-corner-top">
<h1 id="popupHeader">Ignore them</h1>
</div>
<div data-role="content" data-theme="d" class="ui-corner-bottom ui-content">
#{
// make sure the name fits in box below
if(whoToIgnore.Length > 25)
{
whoToIgnore30 = whoToIgnore.Substring(0, 25);
whoToIgnore = whoToIgnore30 + "...";
}
}
<h3 id="popupInnerHeader" class="ui-title">Ignore #whoToIgnore?</h3>
Cancel
<button type="submit" data-inline="true" data-theme="c">Ignore</button>
</div>
</form>
Below this code on the cshtml page I have the following Jquery/Javascript:
function showIgnoreForm(i)
{
// var z = confirm("Works after submission...");
$("#showIgnoreDialog"+ i).popup('open');
}
I have tried to stick the functions inside a "document ready function" like:
jQuery(document).ready(function()
{
// I've had no success placing showIgnoreForm(i) in here
}
I have also tried using document.getElementById as well to no avail. I know the ignore button itself still works after the Form action is taken, because the "confirm" Javascript dialog fires after a submission. Basically, I imagine I need to know how to get the $("#showIgnoreDialog"+ i)... line to work after my Form's action has been completed.
I have a 'mail server configuration' kind of view. On this view are 2 buttons:
[SOME FORM FIELDS]
<input class="button" type="submit" value="#T("Save")" />
<input class="button" type="submit" value="#T("Send Test Email")" />
The first button calls off to my controller and returns the same view with any validation/success messages (this is a form):
[HttpPost]
public ActionResult Index(MailServerSettingsViewModel viewModel)
{
...
That works brilliantly, but obviously the 'Send Test Email' button will do the same.
Assuming I don't want to come away from the page (i.e. load a different view), but want to call off to another controller to send a test e-mail, is the 'proper' way to do this to use the ActionLink helper? From this other controller can I then return this same form view? Or can I somehow use the same controller but determine which button was pressed to decide whether to validate the view model or just call off to another service/class/whatever to send the test e-mail and responding appropriately?
You can probably tell from the way I'm asking this that I come from a WebForms background and it's still a case of me getting used to what's what with MVC.
What I've Tried
For now, I'm actually calling off to this other controller asynchronously with AJAX. It actually works well, and is probably most appropriate, but this won't always be the case so I want to know how I'd achieve the above for other scenarios.
If you don't want ajax you can start a thread to send the email in your controller.
#Html.ActionLink("Send Test Email",
"actionName", "ControllerName",
new { MailServerSettingsViewModel }, new { #class = "button" })
If you set the 'name' attribute on both submit buttons to the same value, you can detect which was clicked in your controller by inspecting the value.
I have a small partial Create Person form in a page above a table of results. I want to be able to post the form to the server, which I can do no problem with ajax.Beginform.
<% using (Ajax.BeginForm("Create", new AjaxOptions { OnComplete = "ProcessResponse" }))
{%>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%=Html.LabelFor(model => model.FirstName)%>
</div>
<div class="editor-field">
<%=Html.TextBoxFor(model => model.FirstName)%>
<%=Html.ValidationMessageFor(model => model.FirstName)%>
</div>
<div class="editor-label">
<%=Html.LabelFor(model => model.LastName)%>
</div>
<div class="editor-field">
<%=Html.TextBoxFor(model => model.LastName)%>
<%=Html.ValidationMessageFor(model => model.LastName)%>
</div>
<p>
<input type="submit" />
</p>
</fieldset>
<%
}
%>
Then in my controller I want to be able to post back a partial which is just a table row
if the create is successful and append it to the table, which I can do easily with jquery.
$('#personTable tr:last').after(data);
However, if server validation fails I want to pass back my partial create person form with the validation errors and replace the existing Create Person form.
I have tried returning a Json array
Controller:
return Json(new
{
Success = true,
Html= this.RenderViewToString("PersonSubform",person)
});
Javascript:
var json_data = response.get_response().get_object();
with a pass/fail flag and the partial rendered as a string using the solition below but that doesnt render the mvc validation controls when the form fails.
SO RenderPartialToString
So, is there any way I can hand my javascript the out of the box PartialView("PersonForm") as its returned from my ajax.form? Can I pass some addition info as a Json array so I can tell if its pass or fail and maybe add a message?
UPDATE
I can now pass the HTML of a PartialView to my javascript but I need to pass some additional data pairs like ServerValidation : true/false and ActionMessage : "you have just created a Person Bill". Ideally I would pass a Json array rather than hidden fields in my partial.
function ProcessResponse(response) {
var html = response.get_data();
$("#campaignSubform").html(html);
}
Many thanks in advance
what I've done in this circumstance is actually render out a partial view that has all the information (e.g. validation info, etc) and then use DOM functions in jquery to extract what I need, if it's more than just for display. Keeps everything in one format that way and I don't have to wory about handling both json and html.
Why do you even need to pass back the partial person object? As far as I'm aware ASP.NET MVC should leave the form filled out as the user filled it in but add validation errors to the form.
They can then correct the errors and submit the form again. Sound more like a problem with the design of the web site and something that you shouldn't be doing in the first place.
If you need to store the data the user entered I would create a json person object and submit this object with the ajax request and then when the validation errors come back you already have the data that was sent.
Ajax enables you to have state in the web client as long as the user is viewing the page.