How to manipulate forms in Angular Dart? - dart

I'm can't find any doc that explains how to manipulates forms in Angular Dart.
I've got the following code in my_component.html:
<form name="form" ng-submit="comp.save()">
<input type="text" required ng-model="comp.user.firstname">
<input type="text" required ng-model="comp.user.lastname">
<button ng-disabled="form.invalid">Save</button>
</form>
And the following one in my_component.dart:
#NgComponent(
selector : 'my-component',
templateUrl : 'my_component.html',
publishAs : 'comp'
)
class MyComponent {
#NgTwoWay('user')
User user;
#NgTwoWay('form')
NgForm form;
MyComponent() {};
void save() {
print(form);
}
}
The validation works well, but when clicking on the button, the print(form) statement always prints null.
Any idea?
Thanks

I guess this is what you want:
class MyComponent {
#NgTwoWay('user')
User user = new User();
Scope scope;
MyComponent(this.scope) {}
void save() {
var form = (scope.context['form'] as NgForm);
print(form.invalid);
}
}

Related

ASP Net Core - hidden input field data is not passed in the Controller

I need to pass which button is clicked for the form submit. But the hidden input is not received in the controller. Below are my code snippets
View Model:
public class DocumentViewModel
{
public int Id { get; set; }
public int ActionId { get; set; }
}
razor(cshtml):
<form....>
<input id="docActionId" name="docActionId" asp-for="ActionId" type="hidden" value="initialValue" />
</form>
JavaScript:
$("#save_btn").on("click", function ()
{
$("#docActionId").val("test1");
});
$("#submit_btn").on("click", function ()
{
$("#docActionId").val("test2");
});
I tried showing the current value in alert function if it is change and it did.
In my Controller/Action when I debug, I get a null value for the ActionId variable. I think this is very simple but I don't know what I did wrong.
Please help. I'm stucked in this for 2 hours now. Thanks in advance.
remove the "Id" attribute... that's all you need. "asp-for" does that. adding yours duplicates it kind
Remove the name="docActionId" html attributes, they will be generated automatically by Razor.
Specifically by the asp-for="ActionId" attribute.
Furthermore the reason this doesn't work is because the name attribute which is docActionId != ActionId which is the name of the property of your object. So it doesn't know where to bind it to.
<form id="myform">
<input id="docActionId" asp-for="ActionId" type="hidden" value="initialValue" />
<button type="submit" id="save">Submit</button>
</form>
Try this javascript perhaps the form is submitted before you attach the value
var saveButton = documnet.getElementById('save')
saveButton.addEventListener('click', function(e) {
e.stopPropagation();
documnet.getElementById('docActionId').value = "test value"
documnet.getElementById('myform').submit()
})
<input type="hidden" id="tag" >
type="hidden" didn't work for me with .net core 7 but only hidden keyword worked.
<input hidden id="tag">

How to fetch HTML Input Id in code behind ASP.Net MVC ? Either on Controller or on Model? [duplicate]

Is there some easy way to handle multiple submit buttons from the same form? For example:
<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" value="Send" />
<input type="submit" value="Cancel" />
<% Html.EndForm(); %>
Any idea how to do this in ASP.NET Framework Beta? All examples I've googled for have single buttons in them.
Here is a mostly clean attribute-based solution to the multiple submit button issue based heavily on the post and comments from Maarten Balliauw.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class MultipleButtonAttribute : ActionNameSelectorAttribute
{
public string Name { get; set; }
public string Argument { get; set; }
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
{
var isValidName = false;
var keyValue = string.Format("{0}:{1}", Name, Argument);
var value = controllerContext.Controller.ValueProvider.GetValue(keyValue);
if (value != null)
{
controllerContext.Controller.ControllerContext.RouteData.Values[Name] = Argument;
isValidName = true;
}
return isValidName;
}
}
razor:
<form action="" method="post">
<input type="submit" value="Save" name="action:Save" />
<input type="submit" value="Cancel" name="action:Cancel" />
</form>
and controller:
[HttpPost]
[MultipleButton(Name = "action", Argument = "Save")]
public ActionResult Save(MessageModel mm) { ... }
[HttpPost]
[MultipleButton(Name = "action", Argument = "Cancel")]
public ActionResult Cancel(MessageModel mm) { ... }
Update: Razor pages looks to provide the same functionality out of the box. For new development, it may be preferable.
Give your submit buttons a name, and then inspect the submitted value in your controller method:
<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="submitButton" value="Send" />
<input type="submit" name="submitButton" value="Cancel" />
<% Html.EndForm(); %>
posting to
public class MyController : Controller {
public ActionResult MyAction(string submitButton) {
switch(submitButton) {
case "Send":
// delegate sending to another controller action
return(Send());
case "Cancel":
// call another action to perform the cancellation
return(Cancel());
default:
// If they've submitted the form without a submitButton,
// just return the view again.
return(View());
}
}
private ActionResult Cancel() {
// process the cancellation request here.
return(View("Cancelled"));
}
private ActionResult Send() {
// perform the actual send operation here.
return(View("SendConfirmed"));
}
}
EDIT:
To extend this approach to work with localized sites, isolate your messages somewhere else (e.g. compiling a resource file to a strongly-typed resource class)
Then modify the code so it works like:
<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="submitButton" value="<%= Html.Encode(Resources.Messages.Send)%>" />
<input type="submit" name="submitButton" value="<%=Html.Encode(Resources.Messages.Cancel)%>" />
<% Html.EndForm(); %>
and your controller should look like this:
// Note that the localized resources aren't constants, so
// we can't use a switch statement.
if (submitButton == Resources.Messages.Send) {
// delegate sending to another controller action
return(Send());
} else if (submitButton == Resources.Messages.Cancel) {
// call another action to perform the cancellation
return(Cancel());
}
You can check the name in the action as has been mentioned, but you might consider whether or not this is good design. It is a good idea to consider the responsibility of the action and not couple this design too much to UI aspects like button names. So consider using 2 forms and 2 actions:
<% Html.BeginForm("Send", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Send" />
<% Html.EndForm(); %>
<% Html.BeginForm("Cancel", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Cancel" />
<% Html.EndForm(); %>
Also, in the case of "Cancel", you are usually just not processing the form and are going to a new URL. In this case you do not need to submit the form at all and just need a link:
<%=Html.ActionLink("Cancel", "List", "MyController") %>
Eilon suggests you can do it like this:
If you have more than one button you
can distinguish between them by giving
each button a name:
<input type="submit" name="SaveButton" value="Save data" />
<input type="submit" name="CancelButton" value="Cancel and go back to main page" />
In your controller action method you
can add parameters named after the
HTML input tag names:
public ActionResult DoSomeStuff(string saveButton, string
cancelButton, ... other parameters ...)
{ ... }
If any value gets posted to one of
those parameters, that means that
button was the one that got clicked.
The web browser will only post a value
for the one button that got clicked.
All other values will be null.
if (saveButton != null) { /* do save logic */ }
if (cancelButton != null) { /* do cancel logic */ }
I like this method as it does not rely on the value property of the submit buttons which is more likely to change than the assigned names and doesn't require javascript to be enabled
See:
http://forums.asp.net/p/1369617/2865166.aspx#2865166
Just written a post about that:
Multiple submit buttons with ASP.NET MVC:
Basically, instead of using ActionMethodSelectorAttribute, I am using
ActionNameSelectorAttribute, which allows me to pretend the action name is whatever I want it to be. Fortunately, ActionNameSelectorAttribute does not just make me specify action name, instead I can choose whether the current action matches request.
So there is my class (btw I am not too fond of the name):
public class HttpParamActionAttribute : ActionNameSelectorAttribute {
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) {
if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
return true;
if (!actionName.Equals("Action", StringComparison.InvariantCultureIgnoreCase))
return false;
var request = controllerContext.RequestContext.HttpContext.Request;
return request[methodInfo.Name] != null;
}
}
To use just define a form like this:
<% using (Html.BeginForm("Action", "Post")) { %>
<!— …form fields… -->
<input type="submit" name="saveDraft" value="Save Draft" />
<input type="submit" name="publish" value="Publish" />
<% } %>
and controller with two methods
public class PostController : Controller {
[HttpParamAction]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SaveDraft(…) {
//…
}
[HttpParamAction]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Publish(…) {
//…
}
}
As you see, the attribute does not require you to specify anything at all. Also, name of the buttons are translated directly to the method names. Additionally (I haven’t tried that) these should work as normal actions as well, so you can post to any of them
directly.
it is short and suite:
It was answered by Jeroen Dop
<input type="submit" name="submitbutton1" value="submit1" />
<input type="submit" name="submitbutton2" value="submit2" />
and do like this in code behinde
if( Request.Form["submitbutton1"] != null)
{
// Code for function 1
}
else if(Request.Form["submitButton2"] != null )
{
// code for function 2
}
Good luck.
I would suggest interested parties have a look at Maarten Balliauw's solution. I think it is very elegant.
In case the link dissapears, it's using the MultiButton attribute applied to a controller action to indicate which button click that action should relate to.
You should be able to name the buttons and give them a value; then map this name as an argument to the action. Alternatively, use 2 separate action-links or 2 forms.
You could write:
<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Send" />
<input type="submit" name="button" value="Cancel" />
<% Html.EndForm(); %>
And then in the page check if the name == "Send" or name == "Cancel"...
I've came across this 'problem' as well but found a rather logical solution by adding the name attribute. I couldn't recall having this problem in other languages.
http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2
...
If a form contains more than one submit button, only the activated submit button is successful.
...
Meaning the following code value attributes can be changed, localized, internationalized without the need for extra code checking strongly-typed resources files or constants.
<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="send" value="Send" />
<input type="submit" name="cancel" value="Cancel" />
<input type="submit" name="draft" value="Save as draft" />
<% Html.EndForm(); %>`
On the receiving end you would only need to check if any of your known submit types isn't null
public ActionResult YourAction(YourModel model) {
if(Request["send"] != null) {
// we got a send
}else if(Request["cancel"]) {
// we got a cancel, but would you really want to post data for this?
}else if(Request["draft"]) {
// we got a draft
}
}
Something I don't like about ActionSelectName is that IsValidName is called for every action method in the controller; I don't know why it works this way. I like a solution where every button has a different name based on what it does, but I don't like the fact that you have to have as many parameters in the action method as buttons in the form. I have created an enum for all button types:
public enum ButtonType
{
Submit,
Cancel,
Delete
}
Instead of ActionSelectName, I use an ActionFilter:
public class MultipleButtonsEnumAttribute : ActionFilterAttribute
{
public Type EnumType { get; set; }
public MultipleButtonsEnumAttribute(Type enumType)
{
EnumType = enumType;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
foreach (var key in filterContext.HttpContext.Request.Form.AllKeys)
{
if (Enum.IsDefined(EnumType, key))
{
var pDesc = filterContext.ActionDescriptor.GetParameters()
.FirstOrDefault(x => x.ParameterType == EnumType);
filterContext.ActionParameters[pDesc.ParameterName] = Enum.Parse(EnumType, key);
break;
}
}
}
}
The filter will find the button name in the form data and if the button name matches any of the button types defined in the enum, it will find the ButtonType parameter among the action parameters:
[MultipleButtonsEnumAttribute(typeof(ButtonType))]
public ActionResult Manage(ButtonType buttonPressed, ManageViewModel model)
{
if (button == ButtonType.Cancel)
{
return RedirectToAction("Index", "Home");
}
//and so on
return View(model)
}
and then in views, I can use:
<input type="submit" value="Button Cancel" name="#ButtonType.Cancel" />
<input type="submit" value="Button Submit" name="#ButtonType.Submit" />
Here is what works best for me:
<input type="submit" value="Delete" name="onDelete" />
<input type="submit" value="Save" name="onSave" />
public ActionResult Practice(MyModel model, string onSave, string onDelete)
{
if (onDelete != null)
{
// Delete the object
...
return EmptyResult();
}
// Save the object
...
return EmptyResult();
}
If your browser supports the attribute formaction for input buttons (IE 10+, not sure about other browsers) then the following should work:
#using (Html.BeginForm()){
//put form inputs here
<input id="sendBtn" value="Send" type="submit" formaction="#Url.Action("Name Of Send Action")" />
<input id="cancelBtn" value="Cancel" type="submit" formaction="#Url.Action("Name of Cancel Action") />
}
If you do not have restrictions on the use of HTML 5, you can use the <button> tag with formaction Attribute:
<form action="demo_form.asp" method="get">
First name: <input type="text" name="fname" /><br />
Last name: <input type="text" name="lname" /><br />
<button type="submit">Submit</button><br />
<button type="submit" formaction="demo_admin.asp">Submit as admin</button>
</form>
Reference: http://www.w3schools.com/html5/att_button_formaction.asp
There are three ways by which you can solve the above issue
HTML way
Jquery way
“ActionNameSelectorAttribute” way
Below is a video which summarizes all the three approaches in a demonstrative way.
https://www.facebook.com/shivprasad.koirala/videos/vb.100002224977742/809335512483940
HTML way :-
In the HTML way we need to create two forms and place the “Submit” button inside each of the forms. And every form’s action will point to different / respective actions. You can see the below code the first form is posting to “Action1” and the second form will post to “Action2” depending on which “Submit” button is clicked.
<form action="Action1" method=post>
<input type=”submit” name=”Submit1”/>
</form>
<form action="Action2" method=post>
<input type=”submit” name=”Submit2”>
</form>
Ajax way :-
In case you are a Ajax lover this second option would excite you more. In the Ajax way we can create two different functions “Fun1” and “Fun1” , see the below code. These functions will make Ajax calls by using JQUERY or any other framework. Each of these functions are binded with the “Submit” button’s “OnClick” events. Each of these function make call to respective action names.
<Script language="javascript">
function Fun1()
{
$.post(“/Action1”,null,CallBack1);
}
function Fun2()
{
$.post(“/Action2”,null,CallBack2);
}
</Script>
<form action="/Action1" method=post>
<input type=submit name=sub1 onclick=”Fun2()”/>
</form>
<form action="/Action2" method=post>
<input type=submit name=sub2 onclick=”Fun1()”/>
</form>
Using “ActionNameSelectorAttribute”:-
This is a great and a clean option. The “ActionNameSelectorAttribute” is a simple attribute class where we can write decision making logic which will decide which action can be executed.
So the first thing is in HTML we need to put proper name’s to the submit buttons for identifying them on the server.
You can see we have put “Save” and “Delete” to the button names. Also you can notice in the action we have just put controller name “Customer” and not a particular action name. We expect the action name will be decide by “ActionNameSelectorAttribute”.
<form action=”Customer” method=post>
<input type=submit value="Save" name="Save" /> <br />
<input type=submit value="Delete" name="Delete"/>
</form>
So when the submit button is clicked , it first hits the “ActionNameSelector” attribute and then depending on which submit is fired it invokes the appropriate action.
So the first step is to create a class which inherits from “ActionNameSelectorAttribute” class. In this class we have created a simple property “Name”.
We also need to override the “IsValidName” function which returns true or flase. This function is where we write the logic whether an action has to be executed or not. So if this function returns true then the action is executed or else it is not.
public class SubmitButtonSelector : ActionNameSelectorAttribute
{
public string Name { get; set; }
public override bool IsValidName(ControllerContext controllerContext, string actionName, System.Reflection.MethodInfo methodInfo)
{
// Try to find out if the name exists in the data sent from form
var value = controllerContext.Controller.ValueProvider.GetValue(Name);
if (value != null)
{
return true;
}
return false;
}
}
The main heart of the above function is in the below code. The “ValueProvider” collection has all the data that has been posted from the form. So it first looks up the “Name” value and if its found in the HTTP request it returns true or else it returns false.
var value = controllerContext.Controller.ValueProvider.GetValue(Name);
if (value != null)
{
return true;
}
return false;
This attribute class can then decorated on the respective action and the respective “Name” value can be provided. So if the submit is hitting this action and if the name matches of the HTML submit button name it then executes the action further or else it does not.
public class CustomerController : Controller
{
[SubmitButtonSelector(Name="Save")]
public ActionResult Save()
{
return Content("Save Called");
}
[SubmitButtonSelector(Name = "Delete")]
public ActionResult Delete()
{
return Content("Delete Called");
}
}
This script allows to specify a data-form-action attribute which will work as the HTML5 formaction attribute in all browsers (in an unobtrusive way):
$(document).on('click', '[type="submit"][data-form-action]', function(event) {
var $this = $(this),
var formAction = $this.attr('data-form-action'),
$form = $($this.closest('form'));
$form.attr('action', formAction);
});
The form containing the button will be posted to the URL specified in the data-form-action attribute:
<button type="submit" data-form-action="different/url">Submit</button>
This requires jQuery 1.7. For previous versions you should use live() instead of on().
David Findley writes about 3 different options you have for doing this, on his ASP.Net weblog.
Read the article multiple buttons in the same form to see his solutions, and the advantages and disadvantages of each. IMHO he provides a very elegant solution which makes use of attributes that you decorate your action with.
This is the technique I'd use and I don't see it here yet. The link (posted by Saajid Ismail
) that inspires this solution is http://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the-same-form.aspx). It adapts Dylan Beattie's answer to do localization without any problems.
In the View:
<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<button name="button" value="send"><%: Resources.Messages.Send %></button>
<button name="button" value="cancel"><%: Resources.Messages.Cancel %></button>
<% Html.EndForm(); %>
In the Controller:
public class MyController : Controller
{
public ActionResult MyAction(string button)
{
switch(button)
{
case "send":
this.DoSend();
break;
case "cancel":
this.DoCancel();
break;
}
}
}
[HttpPost]
public ActionResult ConfirmMobile(string nameValueResend, string nameValueSubmit, RegisterModel model)
{
var button = nameValueResend ?? nameValueSubmit;
if (button == "Resend")
{
}
else
{
}
}
Razor file Content:
#using (Html.BeginForm()
{
<div class="page registration-result-page">
<div class="page-title">
<h1> Confirm Mobile Number</h1>
</div>
<div class="result">
#Html.EditorFor(model => model.VefificationCode)
#Html.LabelFor(model => model.VefificationCode, new { })
#Html.ValidationMessageFor(model => model.VefificationCode)
</div>
<div class="buttons">
<button type="submit" class="btn" name="nameValueResend" value="Resend">
Resend
</button>
<button type="submit" class="btn" name="nameValueSubmit" value="Verify">
Submit
</button>
</div>
</div>
}
Here's an extension method I wrote to handle multiple image and/or text buttons.
Here's the HTML for an image button:
<input id="btnJoin" name="Join" src="/content/images/buttons/btnJoin.png"
type="image">
or for a text submit button :
<input type="submit" class="ui-button green" name="Submit_Join" value="Add to cart" />
<input type="submit" class="ui-button red" name="Submit_Skip" value="Not today" />
Here is the extension method you call from the controller with form.GetSubmitButtonName(). For image buttons it looks for a form parameter with .x (which indicates an image button was clicked) and extracts the name. For regular input buttons it looks for a name beginning with Submit_ and extracts the command from afterwards. Because I'm abstracting away the logic of determining the 'command' you can switch between image + text buttons on the client without changing the server side code.
public static class FormCollectionExtensions
{
public static string GetSubmitButtonName(this FormCollection formCollection)
{
return GetSubmitButtonName(formCollection, true);
}
public static string GetSubmitButtonName(this FormCollection formCollection, bool throwOnError)
{
var imageButton = formCollection.Keys.OfType<string>().Where(x => x.EndsWith(".x")).SingleOrDefault();
var textButton = formCollection.Keys.OfType<string>().Where(x => x.StartsWith("Submit_")).SingleOrDefault();
if (textButton != null)
{
return textButton.Substring("Submit_".Length);
}
// we got something like AddToCart.x
if (imageButton != null)
{
return imageButton.Substring(0, imageButton.Length - 2);
}
if (throwOnError)
{
throw new ApplicationException("No button found");
}
else
{
return null;
}
}
}
Note: For text buttons you have to prefix the name with Submit_. I prefer this way becuase it means you can change the text (display) value without having to change the code. Unlike SELECT elements, an INPUT button has only a 'value' and no separate 'text' attribute. My buttons say different things under different contexts - but map to the same 'command'. I much prefer extracting the name this way than having to code for == "Add to cart".
I don't have enough rep to comment in the correct place, but I spent all day on this so want to share.
While trying to implement the "MultipleButtonAttribute" solution ValueProvider.GetValue(keyValue) would incorrectly come back null.
It turned out I was referencing System.Web.MVC version 3.0 when it should have been 4.0 (other assemblies are 4.0). I don't know why my project didn't upgrade correctly and I had no other obvious problems.
So if your ActionNameSelectorAttribute is not working... check that.
I'm pretty late to the party, but here goes...
My implementation borrows from #mkozicki but requires less hardcoded strings to get wrong. Framework 4.5+ required. Essentially, the controller method name should be the key to the routing.
Markup. The button name must be keyed with "action:[controllerMethodName]"
(notice the use of the C#6 nameof API, providing type-specific reference to the name of the controller method you wish to invoke.
<form>
... form fields ....
<button name="action:#nameof(MyApp.Controllers.MyController.FundDeathStar)" type="submit" formmethod="post">Fund Death Star</button>
<button name="action:#nameof(MyApp.Controllers.MyController.HireBoba)" type="submit" formmethod="post">Hire Boba Fett</button>
</form>
Controller:
namespace MyApp.Controllers
{
class MyController
{
[SubmitActionToThisMethod]
public async Task<ActionResult> FundDeathStar(ImperialModel model)
{
await TrainStormTroopers();
return View();
}
[SubmitActionToThisMethod]
public async Task<ActionResult> HireBoba(ImperialModel model)
{
await RepairSlave1();
return View();
}
}
}
Attribute Magic. Notice the use of CallerMemberName goodness.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class SubmitActionToThisMethodAttribute : ActionNameSelectorAttribute
{
public SubmitActionToThisMethodAttribute([CallerMemberName]string ControllerMethodName = "")
{
controllerMethod = ControllerMethodName;
actionFormat = string.Concat(actionConstant, ":", controllerMethod);
}
const string actionConstant = "action";
readonly string actionFormat;
readonly string controllerMethod;
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
{
var isValidName = false;
var value = controllerContext.Controller.ValueProvider.GetValue(actionFormat);
if (value != null)
{
controllerContext.Controller.ControllerContext.RouteData.Values[actionConstant] = controllerMethod;
isValidName = true;
}
return isValidName;
}
}
I've tried to make a synthesis of all solutions and created a [ButtenHandler] attribute that makes it easy to handle multiple buttons on a form.
I've described it on CodeProject Multiple parameterized (localizable) form buttons in ASP.NET MVC.
To handle the simple case of this button:
<button type="submit" name="AddDepartment">Add Department</button>
You'll have something like the following action method:
[ButtonHandler()]
public ActionResult AddDepartment(Company model)
{
model.Departments.Add(new Department());
return View(model);
}
Notice how the name of the button matches the name of the action method. The article also describes how to have buttons with values and buttons with indexes.
//model
public class input_element
{
public string Btn { get; set; }
}
//views--submit btn can be input type also...
#using (Html.BeginForm())
{
<button type="submit" name="btn" value="verify">
Verify data</button>
<button type="submit" name="btn" value="save">
Save data</button>
<button type="submit" name="btn" value="redirect">
Redirect</button>
}
//controller
public ActionResult About()
{
ViewBag.Message = "Your app description page.";
return View();
}
[HttpPost]
public ActionResult About(input_element model)
{
if (model.Btn == "verify")
{
// the Verify button was clicked
}
else if (model.Btn == "save")
{
// the Save button was clicked
}
else if (model.Btn == "redirect")
{
// the Redirect button was clicked
}
return View();
}
Modified version of HttpParamActionAttribute method but with a bug fix for not causing an error on expired/invalid session postbacks. To see if this is a problem with your current site, open the your form in a window and just before you go to click Save or Publish, open a duplicate window, and logout. Now go back to your first window and try to submit your form using either button. For me I got an error so this change solves that problem for me. I omit a bunch of stuff for the sake of brevity but you should get the idea. The key parts are the inclusion of ActionName on the attribute and making sure the name passed in is the name of the View that shows the form
Attribute Class
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class HttpParamActionAttribute : ActionNameSelectorAttribute
{
private readonly string actionName;
public HttpParamActionAttribute(string actionName)
{
this.actionName = actionName;
}
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
{
if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
return true;
if (!actionName.Equals(this.actionName, StringComparison.InvariantCultureIgnoreCase))
return false;
var request = controllerContext.RequestContext.HttpContext.Request;
return request[methodInfo.Name] != null;
}
}
Controller
[Authorize(Roles="CanAddContent")]
public ActionResult CreateContent(Guid contentOwnerId)
{
var viewModel = new ContentViewModel
{
ContentOwnerId = contentOwnerId
//populate rest of view model
}
return View("CreateContent", viewModel);
}
[Authorize(Roles="CanAddContent"), HttpPost, HttpParamAction("CreateContent"), ValidateAntiForgeryToken]
public ActionResult SaveDraft(ContentFormModel model)
{
//Save as draft
return RedirectToAction("CreateContent");
}
[Authorize(Roles="CanAddContent"), HttpPost, HttpParamAction("CreateContent"), ValidateAntiForgeryToken]
public ActionResult Publish(ContentFormModel model)
{
//publish content
return RedirectToAction("CreateContent");
}
View
#using (Ajax.BeginForm("CreateContent", "MyController", new { contentOwnerId = Model.ContentOwnerId }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(x => x.ContentOwnerId)
<!-- Rest of your form controls -->
<input name="SaveDraft" type="submit" value="SaveDraft" />
<input name="Publish" type="submit" value="Publish" />
}
this is the best way that i have found:
http://iwayneo.blogspot.co.uk/2013/10/aspnet-mvc-action-selector-with-list.html
Here is the code:
/// <summary>
/// ActionMethodSelector to enable submit buttons to execute specific action methods.
/// </summary>
public class AcceptParameterAttribute : ActionMethodSelectorAttribute
{
/// <summary>
/// Gets or sets the value to use to inject the index into
/// </summary>
public string TargetArgument { get; set; }
/// <summary>
/// Gets or sets the value to use in submit button to identify which method to select. This must be unique in each controller.
/// </summary>
public string Action { get; set; }
/// <summary>
/// Gets or sets the regular expression to match the action.
/// </summary>
public string ActionRegex { get; set; }
/// <summary>
/// Determines whether the action method selection is valid for the specified controller context.
/// </summary>
/// <param name="controllerContext">The controller context.</param>
/// <param name="methodInfo">Information about the action method.</param>
/// <returns>true if the action method selection is valid for the specified controller context; otherwise, false.</returns>
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
Func<NameValueCollection> formGetter;
Func<NameValueCollection> queryStringGetter;
ValidationUtility.GetUnvalidatedCollections(HttpContext.Current, out formGetter, out queryStringGetter);
var form = formGetter();
var queryString = queryStringGetter();
var req = form.AllKeys.Any() ? form : queryString;
if (!string.IsNullOrEmpty(this.ActionRegex))
{
foreach (var key in req.AllKeys.Where(k => k.StartsWith(Action, true, System.Threading.Thread.CurrentThread.CurrentCulture)))
{
if (key.Contains(":"))
{
if (key.Split(':').Count() == this.ActionRegex.Split(':').Count())
{
bool match = false;
for (int i = 0; i < key.Split(':').Count(); i++)
{
if (Regex.IsMatch(key.Split(':')[0], this.ActionRegex.Split(':')[0]))
{
match = true;
}
else
{
match = false;
break;
}
}
if (match)
{
return !string.IsNullOrEmpty(req[key]);
}
}
}
else
{
if (Regex.IsMatch(key, this.Action + this.ActionRegex))
{
return !string.IsNullOrEmpty(req[key]);
}
}
}
return false;
}
else
{
return req.AllKeys.Contains(this.Action);
}
}
}
Enjoy a code-smell-less multi submit button future.
thank you
Based on mkozicki answer I come up with a bit different solution. I still use ActionNameSelectorAttribute But I needed to handle two buttons 'Save' and 'Sync'. They do almost the same so I didn't want to have two actions.
attribute:
public class MultipleButtonActionAttribute : ActionNameSelectorAttribute
{
private readonly List<string> AcceptedButtonNames;
public MultipleButtonActionAttribute(params string[] acceptedButtonNames)
{
AcceptedButtonNames = acceptedButtonNames.ToList();
}
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
{
foreach (var acceptedButtonName in AcceptedButtonNames)
{
var button = controllerContext.Controller.ValueProvider.GetValue(acceptedButtonName);
if (button == null)
{
continue;
}
controllerContext.Controller.ControllerContext.RouteData.Values.Add("ButtonName", acceptedButtonName);
return true;
}
return false;
}
}
view
<input type="submit" value="Save" name="Save" />
<input type="submit" value="Save and Sync" name="Sync" />
controller
[MultipleButtonAction("Save", "Sync")]
public ActionResult Sync(OrgSynchronizationEditModel model)
{
var btn = this.RouteData.Values["ButtonName"];
I also want to point out that if actions do different things I would probably follow mkozicki post.
My JQuery approach using an extension method:
public static MvcHtmlString SubmitButtonFor<TController>(this HtmlHelper helper, Expression<Action<TController>> action, string value) where TController : Controller
{
RouteValueDictionary routingValues = Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression(action);
var onclick = string.Format("$('form').first().attr('action', '/{0}')", string.Join("/", routingValues.Values.ToArray().Where(x => x != null).Select(x => x.ToString()).ToArray()));
var html = "<input type=\"submit\" value=\"" + value + "\" onclick=\"" + onclick + "\" />";
return MvcHtmlString.Create(html);
}
You can use it like this:
#(Html.SubmitButtonFor<FooController>(c => c.Save(null), "Save"))
And it renders like this:
<input type="submit" value="Save" onclick="$('form').first().attr('action', '/Foo/Save')" >
For each submit button just add:
$('#btnSelector').click(function () {
$('form').attr('action', "/Your/Action/);
$('form').submit();
});
I've created an ActionButton method for the HtmlHelper. It will generate normal input button with a bit of javascript in the OnClick event that will submit the form to the specified Controller/Action.
You use the helper like that
#Html.ActionButton("MyControllerName", "MyActionName", "button text")
this will generate the following HTML
<input type="button" value="button text" onclick="this.form.action = '/MyWebsiteFolder/MyControllerName/MyActionName'; this.form.submit();">
Here is the extension method code:
VB.Net
<System.Runtime.CompilerServices.Extension()>
Function ActionButton(pHtml As HtmlHelper, pAction As String, pController As String, pRouteValues As Object, pBtnValue As String, pBtnName As String, pBtnID As String) As MvcHtmlString
Dim urlHelperForActionLink As UrlHelper
Dim btnTagBuilder As TagBuilder
Dim actionLink As String
Dim onClickEventJavascript As String
urlHelperForActionLink = New UrlHelper(pHtml.ViewContext.RequestContext)
If pController <> "" Then
actionLink = urlHelperForActionLink.Action(pAction, pController, pRouteValues)
Else
actionLink = urlHelperForActionLink.Action(pAction, pRouteValues)
End If
onClickEventJavascript = "this.form.action = '" & actionLink & "'; this.form.submit();"
btnTagBuilder = New TagBuilder("input")
btnTagBuilder.MergeAttribute("type", "button")
btnTagBuilder.MergeAttribute("onClick", onClickEventJavascript)
If pBtnValue <> "" Then btnTagBuilder.MergeAttribute("value", pBtnValue)
If pBtnName <> "" Then btnTagBuilder.MergeAttribute("name", pBtnName)
If pBtnID <> "" Then btnTagBuilder.MergeAttribute("id", pBtnID)
Return MvcHtmlString.Create(btnTagBuilder.ToString(TagRenderMode.Normal))
End Function
C# (the C# code is just decompiled from the VB DLL, so it can get some beautification... but time is so short :-))
public static MvcHtmlString ActionButton(this HtmlHelper pHtml, string pAction, string pController, object pRouteValues, string pBtnValue, string pBtnName, string pBtnID)
{
UrlHelper urlHelperForActionLink = new UrlHelper(pHtml.ViewContext.RequestContext);
bool flag = Operators.CompareString(pController, "", true) != 0;
string actionLink;
if (flag)
{
actionLink = urlHelperForActionLink.Action(pAction, pController, System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(pRouteValues));
}
else
{
actionLink = urlHelperForActionLink.Action(pAction, System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(pRouteValues));
}
string onClickEventJavascript = "this.form.action = '" + actionLink + "'; this.form.submit();";
TagBuilder btnTagBuilder = new TagBuilder("input");
btnTagBuilder.MergeAttribute("type", "button");
btnTagBuilder.MergeAttribute("onClick", onClickEventJavascript);
flag = (Operators.CompareString(pBtnValue, "", true) != 0);
if (flag)
{
btnTagBuilder.MergeAttribute("value", pBtnValue);
}
flag = (Operators.CompareString(pBtnName, "", true) != 0);
if (flag)
{
btnTagBuilder.MergeAttribute("name", pBtnName);
}
flag = (Operators.CompareString(pBtnID, "", true) != 0);
if (flag)
{
btnTagBuilder.MergeAttribute("id", pBtnID);
}
return MvcHtmlString.Create(btnTagBuilder.ToString(TagRenderMode.Normal));
}
These methods have various parameters, but for the ease of use you can create some overload that take just the parameters you need.

is the parameter in #NgTwoWay("param") really does something?

I have created a angular dart component "Range" that a can be used in a form.
the form is a component as well
In my code I bind the value return by the component in the form but after renaming things it seems like the parameter given to #NgTwoWay(param), in my case #NgTwoWay("v1") is not doing anything it's still mandatory though.
Here is my code:
my_form.dart
library my_form;
import "package:angular/angular.dart";
#Component(selector: "my-form", templateUrl: "my_form.html", exportExpressions: const ["submit"])
class DateFormComponent {
#NgOneWay("random")
bool random;
#NgTwoWay("v1")
String years = "eee";
submit() {
print(random);
print(years);
}
}
class DateFormModule extends Module {
DateFormModule() {
bind(DateFormComponent);
}
}
my_form.html
<form>
<range name="years" value="years"></range>
<div>
<label>Random:</label> <input type="checkbox" ng-model="random">
</div>
<div>
<input type="submit" value="Go !" ng-click="submit()">
</div>
</form>
range.dart
import "package:angular/angular.dart";
#Component(selector: "range", templateUrl: "range.html")
class RangeComponent {
#NgAttr("name")
String name;
#NgTwoWay("value")
String value;
#NgOneWay("range_begin")
String range_begin;
#NgOneWay("range_end")
String range_end;
List<String> ranges = [];
addRange() {
ranges.add("$range_begin-$range_end");
range_begin = "";
range_end = "";
value = ranges.reduce((begin, item) {
return begin += ",$item";
});
}
info() {
print(range_begin);
print(range_end);
print(ranges);
print(value);
}
}
class RangeComponentModule extends Module {
RangeComponentModule() {
bind(RangeComponent);
}
}
range.html
<div>
<label>{{name}}:</label> <input type="text" placeholder="begin"
size="8" ng-model="range_begin"> <input type="text"
placeholder="end" size="8" ng-model="range_end"> <input
type="submit" value="add" ng-click="addRange()"> <input
type="submit" value="info" ng-click="info()">
</div>
I thought that parameter had to bind a DOM attribute with a property of an instance ?
You need these attributes to bind the HTML-attribute of your custom element (component) to a field of your components class.
<body ng-app>
<my-component someattrib="somevalue"></my-component>
when you MyComponent class has a field
#NgAttr('someattrib')
String someField;
then 'someField' will be initialized with 'somevalue'.
You don't need such an attribute for each binding only for binding to attributes.

pretty much simple things in DartAngular 1.0 & Dart 1.7

I'm trying to create a component in AngularDart 1.0:
index.html:
<!DOCTYPE html>
<html ng-app>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>data_generator_front</title>
</head>
<body>
<my-form></my-form>
<label>Name: </label>
<input type="text" ng-model="myProperty" placeholder="Enter your name here">
<hr>
<h1 ng-cloak>Hello {{myProperty}}</h1>
<input type="submit" ng-click="method1()">
<script type="application/dart" src="index.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
index.dart:
import "package:angular/angular.dart";
import "package:angular/application_factory.dart";
import "my_form.dart";
#Injectable()
class RootContext{
String myProperty = "jeahhhhhhhh";
void method1(){
print("method1");
}
}
main(){
applicationFactory()
.rootContextType(RootContext)
.addModule(new DateFormModule())
.run();
}
my_form.html
<form>
<label>Years:</label>
<input type="text" placeholder="begin">
<input type="text" placeholder="end">
<input type="checkbox">
<input type="submit">
</form>
my_form.dart
import "package:angular/angular.dart";
#Component(
selector: "my-form",
templateUrl: "my_form.html")
class DateFormComponent {}
class DateFormModule extends Module {
DateFormModule(){
bind(DateFormComponent);
}
}
Please correct what i'm saying.
I have a class "RootContext" that holds the property 'myProperty' and a method "method1". This class is annotated by #Injectable() which enable the class to be handle by angulardart. like that all the properties/methods are availlable in the html in {{}}, ng-model, ng-click.... last, rootContextType(RootContext) makes RootContext the rootcontext of the app. Is that correct ?
Now I would like to create a Component. (my_form.dart and my_form.html)I succeed to display the html of my component which is great.
I would to pre fill the input (the one with placeholder="begin") with a value like "1980"
here is my attempt:
my_form.dart:
import "package:angular/angular.dart";
#Component(
selector: "my-form",
templateUrl: "my_form.html")
class DateFormComponent {
String years_begin = "1980";
}
class DateFormModule extends Module {
DateFormModule(){
bind(DateFormComponent);
}
}
my_form.html
<form>
<label>Years:</label>
<input type="text" placeholder="begin" ng-model="years_begin">
<input type="text" placeholder="end">
<input type="checkbox">
<input type="submit">
</form>
And that crash with :
...
Missing getter: (o) => o.years_begin
...
ok let's create a getter then: String get years_begin => _years_begin;
but still have the same error...
so #Component() is not like #Injectable() it doesn't turn on all the field/methods to be handle by the view. so how to turn them on ? it's seems like #NgAttr(attrName),
#NgOneWay(attrName), #NgTwoWay(attrName) do the job
let's try this
class DateFormComponent {
#NgAttr("years_begin")
String years_begin = "1980";
}
--> no more exception but the field is not filled
class DateFormComponent {
#NgOneWay("years_begin")
String years_begin = "1980";
}
--> do the job !
class DateFormComponent {
#NgTwoWay("years_begin")
String years_begin = "1980";
}
--> as well
is NgOneWay synchronize model to view ?
is NgTwoWay synchronize model to view and view to model ?
ngAttr seems more to be for the attribute passed in in the HTML like and just at init time ?
let's switch to method now here is my change
my_form.html
<form>
<label>Years:</label>
<input type="text" placeholder="begin" ng-model="years_begin">
<input type="text" placeholder="end">
<input type="checkbox">
<input type="submit" ng-click="method2()">
</form>
my_form.dart
import "package:angular/angular.dart";
#Component(
selector: "my-form",
templateUrl: "my_form.html")
class DateFormComponent {
#NgTwoWay("years_begin")
String years_begin = "1980";
method2(){
print("method2");
}
}
class DateFormModule extends Module {
DateFormModule(){
bind(DateFormComponent);
}
}
it crash with : No getter for 'method2'.
What is the equivalent of NgOneWay ngTwoway for methods ?
Do not hesitate to give me UP TO DATE links and information about angulardart & dart as there are not a lot of documentation up to date even the angulardart website...
Thanks to an answer here I found an answer regarding to the No getter for 'method2' problem.
The transformer struggled so we need to pass extra information: in my component I set the exportExpressions property like this:
my_form.dart
import "package:angular/angular.dart";
#Component(
selector: "my-form",
templateUrl: "my_form.html",
exportExpressions: const ["method2"])
class DateFormComponent {
#NgTwoWay("years_begin")
String years_begin = "1980";
method2(){
print("method2");
}
}
class DateFormModule extends Module {
DateFormModule(){
bind(DateFormComponent);
}
}
I think it's related to tree shaking and that way the transformer exact the 'method2'
I have a class "RootContext" that holds the property 'myProperty' and a method "method1". This class is annotated by #Injectable() which enable the class to be handle by angulardart. like that all the properties/methods are availlable in the html in {{}}, ng-model, ng-click.... last, rootContextType(RootContext) makes RootContext the rootcontext of the app. Is that correct ?
#Injectable() has nothing much to do with Angular binding ({{}}), it is only for the dependency injection system used in Angular and the transformer that generates code to avoid Mirrors/Reflection in production code (which has performance and code bloat issues)
You also need to add a transformers configuration to make this work
see https://github.com/vicb/angular.dart.tutorial/blob/1013-v1/Chapter_06/pubspec.yaml#L12 for an example.
(Angular.dart transformers need to be applied for development too, not only for production)

How to pass object reference to an event handler within a dart polymer element

Can I pass not only String attributes but also object references to an event handler?
Sample gwt_contacts from https://github.com/sethladd/dart-polymer-dart-examples:
contacts_view.html:
<div id="contact-list">
<template repeat="{{contacts}}">
<div>
<input type="checkbox">
<span on-click="selectContact" data-id="{{id}}">{{name}}</span>
</div>
</template>
</div>
handler in contacts_view.dart
void selectContact(MouseEvent event, var detail, SpanElement target) {
String id = target.attributes["data-id"];
selectedContact = contacts.firstWhere((Contact contact) => contact.id == id);
}
How can I pass the contact reference instead of the ID?
similar question: How do I pass arbitrary data to a click event handler function from a Dart polymer web component
CHANGED:
With the hint of Christophe Herreman I added a new element:
html:
<polymer-element name="contact-view-item">
<template>
<span on-click="select">{{contact.name}}</span>
</template>
</polymer-element>
dart:
#CustomTag("contact-view-item")
class ContactViewItem extends PolymerElement {
#observable #published Contact contact;
void select(MouseEvent event, var detail, var target) {
dispatchEvent(new CustomEvent("contactselected", detail: contact));
}
}
and changed the list to:
<div id="contact-list">
<template repeat="{{contact in contacts}}">
<div>
<input type="checkbox">
<contact-view-item contact="{{contact}}" on-contactselected="contactSelectedHandler"></contact-view-item>
</div>
</template>
</div>
and added the handler to ContactsView:
void contactSelectedHandler(CustomEvent event) {
selectedContact = event.detail;
}
But dispatching the custom event will throw an error:
Uncaught Error: unsupported object type for conversion
Exception: unsupported object type for conversion
undefined (undefined:0:0)
The connection seems to be fine, because if I don't pass contact as detail than contactSelectedHandler will be called, but detail is - of course - null.
I don't think that is possible declaratively from within the HTML definition. You can dispatch an object in the detail property of a CustomEvent, when you dispatch it from the element class. Note that the detail property is actually just a reference to event.detail when event is a CustomEvent. If not, the detail property is null.
Component definition
<div id="contact-list">
<template repeat="{{contacts}}">
<div>
<input type="checkbox">
<span on-click="contact_clickHandler" data-id="{{id}}">{{name}}</span>
</div>
</template>
</div>
void contact_clickHandler(MouseEvent event, var detail, SpanElement target) {
String id = target.attributes["data-id"];
Contact contact = contacts.firstWhere((Contact c) => c.id == id);
dispatchEvent(new CustomEvent("contactselected", detail: selectedContact));
}
Component usage
<contact-list on-contactselected="contactList_contactSelectedHandler"></contact-list>
void contactList_contactSelectedHandler(CustomEvent event) {
var contact = event.detail;
// do something with selected contact
}

Resources