At the moment I am working on a MVC4 view with multiple submit buttons. To handle the submit of the different buttons, I use this class:
http://blog.maartenballiauw.be/post/2009/11/26/Supporting-multiple-submit-buttons-on-an-ASPNET-MVC-view.aspx
I have three buttons and one label:
Start
Standby
Resume
How can I display a certain text in that label based on which button is pressed?
I wan to use Ajax.BeginForm to update the label text (so I do not have to reload the webpage).
Thank you in advance!
Update:
For example when I click at the Start Button a method will be executed. This method returns true or false. How to catch this bool and display text in the label, based on the result of the method?
Update 2:
<div>
<fieldset>
<legend>Admin Form</legend>
#Html.Label("Options")
<div id="StartBtn">
<input id="Start" type="submit" value="Start" name="action:Start" />
</div>
<div id="StandbyBtn">
<input id="Standby" type="submit" value="Standby" name="action:Standby" />
</div>
<div id="ResumeBtn">
<input id="Resume" type="submit" value="Resume" name="action:Resume" />
</div>
</fieldset>
</div>
[MultipleButton(Name = "action", Argument = "Start")]
public ActionResult Start()
{
if (start())
{
}
else
{
}
}
From your update I would use an ajax call instead of the ajax form
$('.btnSubmit').on('click', function(){
$.ajax({
url: '#Url.Action('Start', 'Controller')',
type: 'post',
data: {
button: this.id
}
dataType: 'json',
success: function(result){
if(result.Success){
$('.lblText').text(result.SetText);
}
}
});
});
I don't know what you want passed to your controller but if you put the same class on all of your buttons (you need to change them to type button instead of submit also) then this.id will will be the id of the clicked button and that will be sent to the controller
then on your controller have an input field matching what is in the data field
public ActionResult Start(string button){
//do something
//from here http://stackoverflow.com/questions/7732481/returning-json-from-controller-never-a-success
return Json(new { Success = "true", SetText = 'SetText' });
//Where SetText is whatever you want your label set to.
}
You can check on this post. http://www.developersnote.com/2013/02/multiple-button-in-mvc-4.html
#using (Html.BeginForm("ActionTaken", "TestController"))
{
<button name="button" value="ActionOne" class="button" style="width: 200px;">
test1</button>
<button name="button" class="button" style="width: 160px;" value="ActionTwo">
test2</button>
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ActionTaken(string butt)
{
string ButtCommand= butt;
switch (ButtCommand)
{
case "ActionOne":
//do stuff here
case "ActionTwo":
//do stuff here
default:
return View();
}
}
Related
I have a View A, View B, and a View _C.
View _C is a partial view that is rendered inside of View A and B:
View A:
<div style="margin-top:20px;">
<div>
#Html.Partial("~/Views/_C.cshtml", null, new ViewDataDictionary { { "WithRedirect", "true" } });
</div>
</div>
View B
<div style="margin-top:20px;">
<div>
#Html.Partial("~/Views/_C.cshtml", null, new ViewDataDictionary { { "WithRedirect", "false" } });
</div>
</div>
View C(partial view) - code fragment:
.
.
<td style="padding-bottom: 8px;">
#Html.EditorFor(model => model.CurrentPassword, new { htmlAttributes = new { #class = "form-control k-textbox checkError", placeholder = "Enter current password" } })
</td>
.
.
When rendering partial view, I need to set a flag "WithRedirect" to refer to it later in the controller to decide if I need to redirect to another view or not:
string withRedirect = this.ViewData.ContainsKey("WithRedirect") ? this.ViewData["WithRedirect"].ToString() : string.Empty;
if(WithRedirect.Equals("true")
return Redirect(returnUrl ?? Url.Action("Index", "Page1"));
else
return Redirect(returnUrl ?? Url.Action("Index", "Page2"));
When debugging the controller, WithRedirect variable is an empty string.
What am I doing wrong and what is the solution?
Inside your partial view(_C.cshtml), you can read the value from ViewDataDictionary and set to an input field inside a form. When you submit the form, this value will be also submitted. You can have a parameter in your http post action method which will receive this input field value and using that you can conditionally redirect to page1 or page 2.
#model LoginViewModel
#using (Html.BeginForm("Login","Home"))
{
<input type="hidden" name="withRedirect" value="#ViewData["WithRedirect"]" />
#Html.LabelFor(f=>f.Password)
#Html.TextBoxFor(x=>x.Password )
<input type="submit" />
}
and now in your action method
[HttpPost]
public ActionResult Login (LoginViewModel model,bool withRedirect)
{
//to do : Do something
if(withRedirect)
{
return RedirectToAction("Index","Page1");
}
return RedirectToAction("Index","Page2");
}
I have a page with multiple forms, each as a partial. I want to post each partial on submit. If there are errors, I want the validation errors to show in the partial as part of the main page i.e. I don't want to just see the partial on it's own page if there are errors. Am I correct in saying this behavior is only possible with an ajax post? How would I return the model state errors WITHOUT an ajax post, just a normal form post?
Edit:
Still seeing the partial on it's own page
Partial -
#using (Html.BeginForm("Login", "Account", FormMethod.Post, new { id = "LoginForm" }))
{
#Html.ValidationMessage("InvalidUserNamePassword")
<fieldset class="fieldset">
<div>
<label for="form-field-user_id">User ID</label>
<span>
#Html.TextBoxFor(x => x.Username, new { #class = "form-field__input form-field__input--text", #id = "form-field-user_id"})
</span>
</div>
</fieldset>
<div class="form-field__button">
<button id="loginButton" type="submit" class="button button--primary">Login</button>
</div>
}
<script>
$('#loginButton').click(function () {
$.ajax({
type: "POST",
url: '#Url.Action("Login", "Account")',
data: $('form').serialize(),
success: function (result) {
if (result.redirectTo) {
window.location.href = result.redirectTo;
} else {
$("#LoginForm").html(result);
}
},
error: function () {
$("#LoginForm").html(result);
}
});
});
</script>
Controller -
[HttpPost]
public ActionResult Login(LoginModel model)
{
if (!ModelState.IsValid)
{
return PartialView("~/Views/Account/_Login.cshtml", model);
}
return Json(new { redirectTo = Url.Action("Index", "Profile") });
}
Yes, you are correct in saying this behavior is only possible with an ajax post.
There are a few problems with your current script meaning that you will not get the desired results.
Firstly your button is a submit button meaning that it will do a normal submit in addition to the ajax call unless you cancel the default event (by adding return false; as the last line of code in your script). However it would be easier to just change the button type to type="button"
<button id="loginButton" type="button" class="button button--primary">Login</button>
The ajax call will now update the existing page, however it will add the returned partial inside the existing <form> element resulting in nested forms which is invalid html and not supported. Change your html to wrap the main views form in another element
<div id="LoginFormContainer">
#using (Html.BeginForm("Login", "Account", FormMethod.Post, new { id = "LoginForm" }))
{
....
<button id="loginButton" type="button" class="button button--primary">Login</button>
}
</div>
and then modify the script to update the html of the outer element
success: function (result) {
if (result.redirectTo) {
window.location.href = result.redirectTo;
} else {
$("#LoginFormContainer").html(result); // modify
}
},
Finally, your rendering dynamic content so client side validation will not work for the returned form. Assuming your properties have validation attributes (for example the [Required] attribute on the Userame property), you need to reparse the validator after loading the content
var form = $('#LoginForm');
....
} else {
$("#LoginFormContainer").html(result);
// reparse validator
form.data('validator', null);
$.validator.unobtrusive.parse(form);
}
You noted that you have multiple forms on the page, in which case your ajax options should be
data: $('#LoginForm').serialize(),
or if your declare var form = $('#LoginForm'); as per the above snippet, then data: form.serialize(), to ensure you are serializing the correct form.
Side note: There is no real need to change the id attribute of the textbox (it will be id=Username" by default and you can simply use
#Html.LabelFor(x => x.UserName, "User ID")
#Html.TextBoxFor(x => x.Username, new { #class = "form-field__input form-field__input--text" })
or just #Html.LabelFor(x => x.UserName) of the property is decorated with [Display(Name = "User ID")]
In angularjs I had the following:
app.directive('ngEnter', function () {
return function (scope, element, attrs) {
element.bind("keydown keypress", function (event) {
if(event.which === 13) {
scope.$apply(function (){
scope.$eval(attrs.ngEnter);
});
event.preventDefault();
}
});
};
});
And the html was:
<input type="text" ng-model="searchText" class="form-control"
placeholder="Search"
ng-enter="search($event, searchText)">
So basically once I have finished typing my text to search on, when I pressed the enter key the search function on my controller would run.
How would I do this in Aurelia?
I am still learning about its features so any help would be appreciated.
I think an alternative to the angular ngEnter would be:
import {customAttribute, inject} from 'aurelia-framework';
#customAttribute('enter-press')
#inject(Element)
export class EnterPress {
element: Element;
value: Function;
enterPressed: (e: KeyboardEvent) => void;
constructor(element) {
this.element = element;
this.enterPressed = e => {
let key = e.which || e.keyCode;
if (key === 13) {
this.value();//'this' won't be changed so you have access to you VM properties in 'called' method
}
};
}
attached() {
this.element.addEventListener('keypress', this.enterPressed);
}
detached() {
this.element.removeEventListener('keypress', this.enterPressed);
}
}
<input type="password" enter-press.call="signIn()"/>
The simplest way would be to wrap the input in a form element and bind to the submit event of the form.
<form role="form" submit.delegate="search()">
<div class="form-group">
<label for="searchText">Search:</label>
<input type="text" value.bind="searchText"
class="form-control" id="searchText"
placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
This will give you the same behavior you have built above. I'm still working to create an example of a custom attribute to do this, but honestly, this is how I would recommend you do this specific thing (capturing when the user presses enter).
This does not seem to be supported out of the box. While not perfect, here is what I intend to do:
Add a hidden submit button at the top of my form with a click.delegate attribute.
Run some code in my VM when its clicked. This code will decide what to do with the enter keypress based on any custom logic I need.
Hope that helps,
Andrew
EDIT:
You could also add a keypress event delegate:
<input keypress.delegate="doSomething($event)" />
And define doSomething() as:
doSomething(event) {
if(event.which == 13) {
alert('Your code goes here!');
}
event.preventDefault();
}
This will be a little cleaner when you have many inputs with differing enter keypress behaviours.
Because of keypress.delegate and keypress.trigger in an <input> block entering text by default, your function must return true to avoid it, like this:
doSomething(event) {
if(event.which == 13) {
console.log('Your code goes here!');
event.preventDefault();
return false;
}
return true;
}
I want to toggle a buttons class depending on the state of a form in Angular. The template is based on Bootstrap.
I've setup a directive called IsDirty.
If the form has the class 'ng-valid', add the class 'btn-success' to the submit button.
Alternatively, if the form is dirty and has the class 'ng-dirty', remove the class 'btn-success' from the submit button.
So far this is what I have but it doesn't work.
var angular = require('angular');
angular.module('myApp')
.directive('isDirty', [function() {
return {
restrict: 'E',
link: function(scope, element, attrs, ctrl) {
var submitButton = element.find('.btn-primary');
if(element.hasClass('ng-valid')) {
submitButton.addClass('btn-success');
} else {
submitButton.removeClass('btn-success');
}
scope.$apply();
}
};
}]);
My form:
<form is-dirty class="form-horizontal" role="form" name="profileForm">
<!-- INPUT FIELDS HERE //-->
<button type="submit" class="btn btn-primary">Save Changes</button>
<button type="reset" class="btn btn-default">Cancel</button>
</form>
This should hopefully fix your problem
.directive('ngDirty', [function() {
return {
restrict: 'AE',
link: function(scope, element, attrs, ctrl) {
var submitButton = element[0].querySelector('.btn-primary');
if(element.hasClass('ng-valid')) {
submitButton.classList.add("btn-danger");
} else {
submitButton.classList.remove("btn-danger");
}
}
};
}]);
Plnkr Example
Update:
It's a little dirty but it seems to work and checks each input, you must bind each input to an ng-model though I have used $scope.input
New Plnkr
2nd Update
I have removed the function and brought in a $timeout you will see from the example how it works.
New update with a $timeout as function
Use ngClass for this (btw, I am confused with your class names. In your description you say add/remove the class .btn-success but in the code you are adding/removing .btn-danger. So in the code below, I am sticking with .btn-success):
<form is-dirty class="form-horizontal" role="form" name="profileForm">
<!-- INPUT FIELDS HERE //-->
<button type="submit"
class="btn btn-primary"
ng-class="{'btn-success' : isValid}">
Save Changes
</button>
<button type="reset" class="btn btn-default">Cancel</button>
</form>
And in your directive:
var angular = require('angular');
angular.module('myApp')
.directive('form', [function() {
return {
restrict: 'EA',
link: function(scope, element, attrs, ctrl) {
scope.isValid = element.hasClass('ng-valid');
}
};
}]);
I would further suggest that you actually make the class ng-valid itself with ng-class and use the variable scope.isValid to change between ng-valid and isDirty.
I have this controller and view:
public ActionResult DynamicView()
{
return View();
}
_
#model ChatProj.Models.GroupNumber
#{
ViewBag.Title = "DynamicView";
}
<h2>DynamicView</h2>
<fieldset>
<legend>Create a room</legend>
<div class="editor-label">
#Html.LabelFor(model => model.GroupId)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.GroupId)
#Html.ValidationMessageFor(model => model.GroupId)
</div>
<input type="submit" value="DynamicView" />
</fieldset>
This is what it looks like on the page.
That's fine and dandy, but I would like to pass that number to a controller, which then passes it to a view. I would like to pass it to this view:
#using PagedList.Mvc;
#using ChatProj.App_Code;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#{
ViewBag.Title = "Grupprum 1";
}
<h2>Grupprum 1</h2>
<style>
ul {list-style-type:circle;}
</style>
<div class="container">
<div class="nano chat">
<div class="content">
<ul id="discussion">
</ul>
</div>
</div>
<input type="text" id="message" />
<input type="button" id="sendmessage" value="Send" disabled="disabled" />
<input type="hidden" id="displayname" />
</div>
#section scripts {
<!--Script references. -->
<!--The jQuery library is required and is referenced by default in _Layout.cshtml. -->
<!--Reference the SignalR library. -->
<script src="~/Scripts/jquery.signalR-1.1.3.js"></script>
<script src="~/Scripts/jquery.nanoscroller.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="~/signalr/hubs"></script>
<!--SignalR script to update the chat page and send messages.-->
<script>
$(function () {
// Reference the auto-generated proxy for the hub.
var chat = $.connection.chatHub;
$(".nano").nanoScroller();
// Create a function that the hub can call back to display messages.
chat.client.addNewMessageToPage = function (name, message) {
// Add the message to the page.
$('#discussion').append('<li><strong>' + htmlEncode(name)
+ '</strong>: ' + htmlEncode(message) + '</li>');
};
$(document).ready(function () {
$("#sendmessage").removeAttr("disabled");
$('#message').keypress(function (e) {
if (e.keyCode == 13)
$('#sendmessage').click();
});
});
// Get the user name and store it to prepend to messages.
// Set initial focus to message input box.
$('#message').focus();
$.connection.hub.qs = { "room": "Grupprum 1" };
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chat.server.send($('#message').val());
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
// This optional function html-encodes messages for display in the page.
function htmlEncode(value) {
var encodedValue = $('<div />').text(value).html();
return encodedValue;
}
</script>
}
Specifically I would want it at $.connection.hub.qs = { "room": "Grupprum 1" }; to replace the 1.
So I've created these controllers which are faulty and incomplete:
[HttpPost]
public ActionResult DynamicView(int? roomNumber)
{
return View(GroupRoom(roomNumber));
}
public ActionResult GroupRoom(int roomNumber)
{
return View();
}
Does anyone know how I should change my controllers and views so that I'm able to insert a number in my DynamicGroup view, and get a view back based on the inserted number and the lastly mentioned view?
You could pass the number from the model to the new action just how #Matt Bodily did. But if you want to use a different model on your new view, you can use the below code instead:
public ActionResult GroupRoom(int roomNumber)
{
ViewBag.RoomNumber = roomNumber;
return View();
}
This way, you can use a different model for this page, if you want to. To display this ViewBag on the page, use this code anywhere you want:
#ViewBag.RoomNumber
I hope that helps you out.
How you have it set up the Model.GroupID will be set on the first view so change your controller like this
[HttpPost]
public ActionResult DynamicView(GroupNumber model)
{
//model.GroupId here will be what was selected on the first view
return RedirectToAction("GroupRoom", "Controller", new { GroupId = model.GroupId });
}
public ActionResult GroupRoom(int GroupId)
{
var model = //build your model based on the selected GroupId
return View(model);
}