This kind of a bizarre issue and I can't figure out a solution how I want.
I'm using .net core 2.1. I have a orders view model like this:
public class OrdersFilterViewModel
{
[Display(Name = "Account Numbers:")]
public IEnumerable<SelectListItem> AccountNumbers { get; set; }
}
My viewmodel and SelectList in my orders controller is called like this:
var vm = new OrdersFilterViewModel
{
AccountNumbers = new SelectList(_context.Account.Where(m => m.UserID == userId), "AccountNumber", "AccountNumber", account)
};
return PartialView("_FilterOrders", vm);
The problem lies when trying to get a dropdown list in the view which looks like this:
<form asp-action="FilterOrders" asp-controller="Order" id="ordersFilterForm" method="post">
<div class="form-group">
<label asp-for="AccountNumbers" class="control-label"></label>
<select asp-for="AccountNumbers" class="form-control" asp-items="#Model.AccountNumbers">
</select>
</div>
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-default" />
</div>
</form>
This somewhat works but gives me a textarea type display where multiple = "multiple" is always tacked on in the browser. I've discovered that if I add something like the following to my viewmodel:
public int? AccountId { get; set; }
Then change my view to:
<select asp-for="AccountId" class="form-control" asp-items="#Model.AccountNumbers">
I can then have my dropdown list. However, I don't need that property for anything as far as I know. I tried a million things so it's possible I made some other slight changes I'm forgetting to get that to work, but that's the gist of it.
Is there any way around adding that extra property? Or do I need it for something I'm not aware of? Or is there any way to set multiple = "false" or something to that effect so I can get my dropdown list with my original viewmodel and such?
I haven't dealt with the post back to the controller yet, so maybe that will reveal the gotchas. I'm basically trying to create a modal type query filter that doesn't really do much other than modify some parameters and send them back to my query to update it. Thanks.
Is there any way around adding that extra property? Or do I need it
for something I'm not aware of?
Yes, you need this extra property, because in your select there are many items, and the user will select one or multiple items, and on the server side you'll need to know what the user selected, this is the purpose of the select tag.
And the multiple = "multiple" depends on what you put in the asp-for in the case of asp-for="AccountId" it is a single int value, so it won't use multiple, is you have an array in the asp-for then it will use the multiple.
Here is a pretty detailed description about the select tag helper:
Select Tag Helper in ASP.NET Core MVC
Related
I have a page with many required fields. So when I click submit button required validation is firing for the first field then the second then the third and so on...
What I need to do here is , When I click on submit I have to show all errors on a page in one shot.
My requirement is to achieve this only by validating client side.
I am using an .Net core MVC application.
Below is the screenshot of my page
Can I achieve this.. Please help me..
Thanks !!
I can give you an idea to do your job using jquery custom validation.Please refer my solution.
Add custom style class to your required fields.
Example :
<input type="text" class="req-cls" >
Write Jquery function to Check Validation
$(document).ready(function () {
$('#btn1').click(function (e) {
var isValid = true;
$('.req-cls').each(function () {
if ($.trim($(this).val()) == '') {
isValid = false;
$(this).css({
"border": "1px solid red",
"background": "#FFCECE"
});
}
else {
$(this).css({
"border": "",
"background": ""
});
}
});
if (isValid == false)
e.preventDefault();
});
});
See Example here : https://jsfiddle.net/Shalitha/q2n8L9wg/24/
Just add this line in your .cshtml
<div class="validation-summary-valid" data-valmsg-summary="true">
<ul>
<li style="display: none;"></li>
</ul>
</div>
Since you need client side we are talking about JS. But with razor you can validate a few results using the model annotations. For example let's say you have this object.
public class UserCreationVO
{
[Required]
[StringLength(255)]
public string Username { get; set; }
}
Now what you need to do in your frontend (meaning your .cshtml file) is to tell asp.net to use this properties to validate. So for example:
#model UserCreationVO
<form method="post">
<input asp-for="UserName" />
<span asp-validation-for="UserName"></span>
</form>
As you can see above using asp-for is a great way to create validations using your models. Be careful you must pass as a model the object you want to validate. The asp-for tag shows a model property. So you can't pass it in a Viewbag or something. This produces some automatic html and js for you and handles it.
Furthermore you should always validate the result nevertheless in the controller. Because client side validation is for performance reasons and user experience and doesn't offer any kind of security:
public IActionResult CreateUser(UserCreationVO user)
{
if(!ModelState.IsValid)
return your_error;
}
Last but not least: You must include the JQuery unobtrusive validation library. Furthermore if you have some extra requirements like checking if a username exists (Which can't be done without contacting the server) then you can use the [Remote] attribute.
More info and reading about front-end validation with razor: here
How to use a remote attribute: Using remote validation with ASP.NET Core
EDIT:
So generally I advise to use models and create them. As you say policy is required in one form but not in another. What you should do to have a maintanable code where you simply change the attribute of your model and the validation happens you need to create a different VO. For example:
public class CreatePolicyVO
{
[Required]
public string PolicyNumber {get; set;}
}
And another object for example updating:
public class UpdatePolicyVO
{
public string PolicyNumber {get; set;}
}
Because you also need to validate them in the controller. So passing a different object allows you to use ModelState.IsValid and other MVC and razor features. Generally if a field is required in one case and not in another then you need a different model.
First, we need to add the JQuery,jquery.validate & jquery.validate.unobtrusive in our views.
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.16.0/jquery.validate.min.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js"></script>
Then in View add required data-* attributes like:
<label for="Name">Name</label>
<input type="text" data-val="true" data-val-length="Length must be between 10 to 25" data-val-length-max="25" data-val-length-min="10" data-val-required="Please enter the name" id="Name" name="Name" value="" />
<span class="field-validation-valid" data-valmsg-for="Name" data-valmsg-replace="true"></span>
<br />
You could see that it has added the several attributes starting with data-*.
The data-* attributes are part of the HTML5, which allow us the add extra information (metadata) to the HTML element.
The Javascript unobtrusive library reads the data-val attributes and performs the client side validation in the browser when the user submits the form. These Validations are done before the form is sent over an HTTP. If there is a validation error, then the request will not be sent.
[See updates at bottom]
I have a Razor page with a form on it. I want to have two buttons on that form, that perform a slightly different action - both using the same posted form data.
I tried using the asp-page-handler helper on the second button, but it doesn't seem to add anything to the HTML (I would expect it to add a formaction attribute to the <button> element, but it doesn't add anything at all).
Here's an example page:
#page "{id?}"
#model IndexModel
#tagHelperPrefix x:
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Current value is #Model.Foo</p>
<x:form method="post">
<input type="text" name="foo" />
<button type="submit">Default</button>
<button type="submit" x:asp-page-handler="Alternative">Alternative</button>
</x:form>
... and here's the corresponding page model:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MyWebApplication.Pages
{
public class IndexModel : PageModel
{
[BindProperty]
public string Foo { get; set; }
public void OnGet(int? id)
{
}
public void OnPostAsync(string foo)
{
Foo = foo;
}
public void OnPostAlternativeAsync(string foo)
{
Foo = foo.ToUpper();
}
}
}
This is rendered as:
...where the generated HTML for the form is:
<form method="post">
<input type="text" name="foo" />
<button type="submit">Default</button>
<button type="submit" x:asp-page-handler="Alternative">Alternative</button>
</form>
The fact that the x:asp-page-handler attribute is still in the generated HTML makes me think that the Razor engine hasn't recognized it. I've tried taking off the x: prefix, but that didn't help.
What am I doing wrong?
UPDATE
OK, I tried removing the tag prefix and removing the #tagHelperPrefix line, and that made a difference. A formaction is added to the second <button> element as expected.
However:
that's really annoying - the #tagHelperPrefix is not something I want to lose, and
now both buttons are triggering the "Alternative" action, even though only one of them has the formaction!
Here's the new generated HTML:
<form method="post">
<input type="text" name="foo" />
<button type="submit">Default</button>
<button type="submit" formaction="/?handler=Alternative">Alternative</button>
</form>
SECOND UPDATE
OK, so If I put asp-page-handler="" on the "default" button, then each button goes to the correct handler, which is fine.
The last question that remains, then, is: how can I make this work with the tag helper prefix?
[Answering my own question in case this helps others.]
It turns out that:
The tag-helper-prefix only applies to elements, not attributes, so it should be asp-page-handler="..." rather than x:asp-page-handler="..." even if the tag-helper-prefix is x:.
Those asp- attributes are only recognized within a tag that is tag-helper-enabled - which is all elements when no tag-helper-prefix is specified, or only elements with the tag-helper-prefix where one is specified. In my case, I had to change <button ...> to <x:button ...>.
If you specify asp-page-handler for one button, you need to specify it on all the buttons, even if you specify it as "" to get the default action.
I don't know how much code is needed in order for you to understand my problem so I'm starting with explaining what I want to do.
I have a form in my View where I create a new object.
In this form I want to use a dropdownlist and I've tried a lot with #Html.Dropdownlistfor() but realized I don't want to use that since I don't know how to use som Jquery UI-code on this and I don't know how to send a different value than what is shown in the dropdownlist.
Does any of you know how i just send the value selected in the dropdownlist to my controller?
DropDownListFor helper method esssentially render a SELECT element. So if you do not want to use the helper method, You may write the RAW HTML code
#model CreateUserVM
#using(Html.BeginForm())
{
<select name="SelectedCity" id="SelectedCity">
<option value='1'>Ann Arbor</option>
<option value='2'>Novi</option>
<option value='3'>Detroit</option>
</select>
}
Make sure your SELECT elements name property value is same as what you use in your Model, so that Model binding will work.
public class CreateUserVM
{
public int SelectedCity { set;get;}
//other properties
}
In view model:
puclic class ViewModel
{
int SelectId{get;set;}
}
In view
<select name="SelectId" id="SelectId">
<option value='1'>one</option>
<option value='2'>Two</option>
</select>
After post form ViewModel.SelectId will contain value of selected item in dropdown
From reading the posts in this thread - and being unable to post the question there for some bizarre reason :( I will ask it here in hope of getting a solution
Am I write in saying that I have to do validation like below..
I add the html5 attribute (data-required-msg/validationMessage) to the textbox and the required attribute as well..
I make a span for the invalid msg and tie it to the field with the "data-for" attribute. The message "Please enter name" should appear in this span then.
Questions
Is this the only way to work with this?
Is there no way for me to display the proper error message ("Error Message I want to show"), as in any way to tie to the mvc attributes on the ViewModel. As another poster said this is a lot more scalable/re-usable and better design.
Using a data-for="Name" is very brittle as a change in the Model field name will not reflect there and that could be forgotten about hence delivering buggy software. You are losing the type safety of something like
#Html.ValidationMessageFor(m=> m.Name)
Code
public class AViewModel
{
[Required(ErrorMessage="Error Message I want to show")]
public string Name { get; set; }
}
<div class="validation-wrapper">
<div class="input-wrapper">
#Html.TextBoxFor(m => m.Name, new { placeholder = "eg. John Smith", data_required_msg="PleaseEnter name", required="required" } )
</div>
<span class="k-invalid-msg" data-for="Name"></span>
</div>
Cheers,
J
In order to be able to do what you are saying, you need to be using Kendo UI for ASP.NET MVC. With that you can continue using your DataAnnotations attributes and Html.ValidationMessageFor() helpers as before. All you will need to do is call $('[your_form_selector]').kendoValidator() after your form (or on document.ready()).
During my first steps in .NET MVC 4 I'm creating a web site and I would like to implement users authentication/authorization.
In my implementation I would like to be able to link controls to roles. For example, if I have 2 Roles in my system : Admin and User, the in some view say I have 3 inputs:
<input class="search-field rounded" id="field1" type="text"/>
<input type="submit" name="submit"/>
<input class="search-field rounded" id="field2" type="text"/>
<input type="submit" name="submit"/>
<input class="search-field rounded" id="field3" type="text"/>
<input type="submit" name="submit"/>
I would like that an Admin will be able to see and edit all 3 fields in this view, but a User should only see 2 of them, and be able to edit one of them (this is just an example).
So basically, I would like to be able to define permissions for controls, and a role should be consisted of a collection of permissions (If you can think of a better approach I would love to hear it).
So those are my constraints, and I see quite a few packages out there (such as Fluent Security, and Security Guard) that relates to the subject, but I'm not quite sure which is best to tackle my challenge if at all.
Is there a best practice to overcome this demand?
Any help is highly appreciated.
idlehands23 has shown how you might access and check roles but i'm guessing you want to use this functionality at the view level.
Within an action method, I usually pass HttpContext.User into the ViewBag or in the case of a strongly typed view you can pass the principal into your model and parse these values out as you wish. Then pass the model to the view like so.
return View(new ModelClass(HttpContext.User))
From here you can add additional code into the view logic to parse/check the roles and render the html with greater specificity using for example:
If (Model.User.IsInRole("Admin"))
{
//Render admin controls
//...
}
else
{
//Render User Controls
//...
}
Using the [Authorize(Role="Admin||User")] attribute on your action method would restrict access to an entire group of users. In such a case you would need two action methods and one or possibly two distinct views to render the content. However if you just want to limit it to ANY authorized user you can do like so:
[Authorize]
public ActionResult Index(){}
Then you can implement the logic at the view level with the certainty that they are authorized.
I have done it this way:
//In Controller
ViewBag.Roles = Roles.GetRolesForUser(User.Identity.Name);//viewbag because I'm assuming this isn't what you want to strongly type to your page.
//In View
#{
var roles = (Roles)ViewBag.Roles;
}
if(roles.contains("admin')) //put whatever you want in place of "admin"
{
//do something
}
in your controller you can give access to certain views or partial views this way
//in controller if you want it.
[Authorize(Roles = "Admin, ImportExport, Search")] //this is just for security
public ActionResult whatever(){}
*I use razor. replace # with <% %> if your not.
I ended up creating my own custom membership provider and role provider.
In my Role Provider I added a method
public bool UserHasPermission(string username, string permission) {...}
And in my view I'm doing:
#{
var roleProvider = Roles.Provider as MyCustomRoleProvider;
bool addressEditPermission = roleProvider.UserHasPermission(User.Identity.Name, "addressEditPermission");
}
Then I can manipulate my control:
#Html.TextBoxFor(model => model.Name, new { #readonly = addressEditPermission })
You just need to make sure your control has overloads that take HTML attributes.
I hope this helps someone..