I'm still a newwbie in ASP.Net and don't get how to fix the following problem.
I need rich text to be published on my project. I added NicEdit because it seems to be easy to use.
So, as foreseen, i got an error from the server.
A potentially dangerous Request.Form value was detected from the client(compteRendu="blablbab<br><br>test<br>").
I tryed to fix it by using htmlencoder, but I failed at using it.
What I did :
<script type="text/VB">
htmlEncode {
model.compteRendu = HtmlEncode(model.compteRendu)
}
</script>
#Using Html.BeginForm(IsPost)
#Html.ValidationSummary(True)
#<fieldset>
<legend>meeting</legend>
#Html.HiddenFor(Function(model) model.idmeeting)
<div class="editor-label">
#Html.LabelFor(Function(model) model.compteRendu)
</div>
<div class="editor-field">
#Html.TextAreaFor(Function(model) model.compteRendu)
#Html.ValidationMessageFor(Function(model) model.compteRendu)
</div>
<p>
<input type="submit" value="Save" onclick="htmlEncode"/>
</p>
</fieldset>
End Using
So, what have I done wrong? I also tryed to do this inside the controller but I didn't find any method which was supposed to encode the Html
' POST: /Meeting/Edit/5
<HttpPost()>
Function Edit(meeting As meeting) As ActionResult
meeting.compteRendu = HttpEncode(meeting.compteRendu)
If ModelState.IsValid Then
...
ps : I'm not a native english speaker, sorry if my english sucks.
edit :
For the moment, I'm not needing more than something that allows me to replace my "new lines" by .
So, I've found that I could do iit like that :
#Html.Raw(meeting.compteRendu.Replace(System.Environment.NewLine, "<br />"))
For the moment, it's ok for me. But I'm not sure, maybe I'll need to create text with colors, and so on. So if you've an idea on how I can send validated rich text to my database, I'll be very happy.
You could decorate the compteRendu property on your view model with the <AllowHtml> attribute:
<AllowHtml()>
Public Property compteRendu As String
This will accept any characters in this property. Inside your view you don't need to do any encodings:
#ModelType Meeting
#Using Html.BeginForm(IsPost)
#Html.ValidationSummary(True)
#<fieldset>
<legend>meeting</legend>
#Html.HiddenFor(Function(model) model.idmeeting)
<div class="editor-label">
#Html.LabelFor(Function(model) model.compteRendu)
</div>
<div class="editor-field">
#Html.TextAreaFor(Function(model) model.compteRendu)
#Html.ValidationMessageFor(Function(model) model.compteRendu)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
End Using
Neither inside your controller action:
' POST: /Meeting/Edit/5
<HttpPost()>
Function Edit(meeting As meeting) As ActionResult
If ModelState.IsValid Then
...
End If
...
End Function
Related
I am trying to learn the new feature in ASP.NET Blazor. I am using Visual Studio 2019. I am trying to create an Ideas Registration form. So the code for dropdownlist i had took from Bootstrap 4. It was not working as expected. Can you please tell me where i am working wrong?
Just a little overwhelmed here, any advice would be much appreciated.
Given Code:
<!-- Card Body -->
<div class="card-body">
<!-- <form -->
<form>
<div class="form-group">
<label for="exampleFormControlInput1">Title</label>
<input type="email" class="form-control" id="exampleFormControlInput1">
</div>
<div class="form-group">
<label for="exampleFormControlSelect1">Description</label>
<textarea class="form-control" id="exampleFormControlTextarea1" rows="4"></textarea>
</div>
<!-- Basic dropdown -->
<div class="form-group">
<button class="btn btn-primary dropdown-toggle mr-4" type="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
Basic dropdown
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#">.Net</a>
<a class="dropdown-item" href="#">Python</a>
<a class="dropdown-item" href="#">Data Science</a>
<div class="dropdown-divider"></div>
</div>
</div>
<!-- Basic dropdown -->
where i am working wrong
According to the official docs](https://getbootstrap.com/docs/4.0/components/dropdowns/#data-toggledropdown-still-required):
Regardless of whether you call your dropdown via JavaScript or instead use the data-api, data-toggle="dropdown" is always required to be present on the dropdown’s trigger element.
I would suggest you should wrap your Basic dropdown in the following structure
<div class="dropdown">
<button data-toggle="dropdown" class="..." > ...</button>
<div class="dropdown-menu ...>
...
</div>
</div>
You didn't add an event handler for selection. At least you should add a #onclick for the toggle button. When clicking this button, show or hide the dropdown-menu.
Finally, if you want to implement the dropdown component with Blazor(without javascript), you should also replace the text content within the toggle button when someone selects a dropdown list item.
A Demo : How to Create A General Dropdown Component
Rather than simply fixing the issue, I think it's much better to create a general dropdown Component so that we can always invoke them in following way:
#{ var list = new List<string>{ ".NET", "Python","Java" }; }
<Dropdown TItem="string" OnSelected="#OnSelected" >
<InitialTip>This is a dropdown list</InitialTip>
<ChildContent>
<DropdownListItem Item="#list[0]">.NET</DropdownListItem>
<DropdownListItem Item="#list[1]">Python</DropdownListItem>
<div class="dropdown-divider"></div>
<DropdownListItem Item="#list[2]">Java</DropdownListItem>
</ChildContent>
</Dropdown>
#code {
private void OnSelected(string selection)
{
Console.WriteLine(selection);
}
}
Here the TItem is a generic type parameter that is the type of each dropdown list item and can be any .NET type.
Demo
How-To
Add a Shared/Dropdown.razor component:
#using Microsoft.AspNetCore.Components.Web
#typeparam TItem
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle mr-4" data-toggle="dropdown" type="button" #onclick="e => this.show=!this.show "
aria-haspopup="true" aria-expanded="false">
#Tip
</button>
<CascadingValue name="Dropdown" Value="#this">
<div class="dropdown-menu #(show? "show":"")" >
#ChildContent
</div>
</CascadingValue>
</div>
#code {
[Parameter]
public RenderFragment InitialTip{get;set;}
[Parameter]
public RenderFragment ChildContent{get;set;}
[Parameter]
public EventCallback<TItem> OnSelected {get;set;}
private bool show = false;
private RenderFragment Tip ;
protected override void OnInitialized(){ this.Tip = InitialTip; }
public async Task HandleSelect(TItem item, RenderFragment<TItem> contentFragment)
{
this.Tip= contentFragment.Invoke(item);
this.show=false;
StateHasChanged();
await this.OnSelected.InvokeAsync(item);
}
}
Add a Shared/DropdownListItem.razor component:
#using Microsoft.AspNetCore.Components.Web
#typeparam TItem
<a class="dropdown-item" Item="#Item" #onclick="e=> Dropdown.HandleSelect(Item, ChildContent)" >#ChildContent(Item)</a>
#code {
[CascadingParameter(Name="Dropdown")]
public Dropdown<TItem> Dropdown {get;set;}
[Parameter]
public TItem Item{get;set;}
[Parameter]
public RenderFragment<TItem> ChildContent {get;set;}
}
Keep in mind that bootstrap dropdown requires bootstrap javascript to be referenced. And the Blazor template doesn't reference it by default.
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js#1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
The accepted answer is great. However, as many have pointed out, the dropdown does not close if an option is not selected. The suggestion to create an #unblur event does not solve the case, as #unclick and #unblur do not seem to work together in .NET 5 (I read somewhere that it does works in the new .NET 6) - #unblur prevents #unclick to be triggered.
I found this solution (#onblur prevents #onclick in blazor server side app), changing the #onclick to #onmousedown and then creating the #onblur event (as suggested) has fixed the issue for me.
I was using data annotation validation on my Razor Pages which worked fine. For more complex valitaion I am using Fluent Validation, which works fine for everything unless it is in a Modal.
public class MyModel
{
[RegularExpression(#"^Test|Prod", ErrorMessage = "Please Choose A Type")]
public string Type { get; set; }
}
public class MyValidator : MyValidator<MyModel>
{
public MyValidator()
{
RuleFor(x => x.Username)
.Empty()
.When(support => x.Found == false)
.WithMessage("Not Found");
RuleFor(x => x.IsComplete)
.Must(x => x.Equals(true))
.WithMessage("Please confirm");
}
}
Below is example modal code - I have simiplified it so sorry for any mistakes. The code in the main body validates fine, but nothing I put in a modal with Fluent Validation works - it is just submitted without checks. I have several modals all doing the same thing. Data annotation validation works fine.
<div id="" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog-700" role="document">
<div class="modal-content modal-size">
<div class="modal-header">
<h6 class="modal-title">Test</h6>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<div class="modal-body">
<div class="container">
<div class="row justify-content-md-center">
<form asp-page-handler="update" method="post">
<div class="form-group">
<label asp-for="MyModel.Username" class="control-label">Test Code</label>
<textarea asp-for="MyModel.Username" class="form-control" rows="2"></textarea>
<span asp-validation-for="MyModel.Username" class="text-danger"></span>
</div>
<div class="form-group">
<button class="btn">
<span class="text">Update</span>
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
I have also tried:
RuleFor(support => support.Found.ToString())
.Matches(#"^True$")
The real reason is that FluentValidation does not support all rules for client side validation.
Quoted from FluentValidation docs:
Note that not all rules defined in FluentValidation will work with ASP.NET’s client-side validation. For example, any rules defined using a condition (with When/Unless), custom validators, or calls to Must will not run on the client side. Nor will any rules in a RuleSet (although this can be changed - see the section below on “RuleSet for client-side messages”). The following validators are supported on the client:
NotNull/NotEmpty
Matches (regex)
InclusiveBetween (range)
CreditCard
Email
EqualTo (cross-property equality comparison)
MaxLength
MinLength
Length
So this is a behavior by design.
FluentValidation is a server-side framework, and does not provide any client-side validation directly. The following validators are supported on the client:
NotNull/NotEmpty
Matches (regex)
InclusiveBetween (range)
CreditCard
Email
EqualTo (cross-property equality comparison)
MaxLength
MinLength
Length
If you want to use validation rules on client-side, you can try FormHelper. Form Helper transforms server-side validations to client-side.
Nuget Package: https://www.nuget.org/packages/FormHelper
GitHub: https://github.com/sinanbozkus/formhelper
I have a form control "ConnectorType" which I turned into a dropdown list with pre-defined values (just 3qty currently)
When the user selects and item from this dropdown list, depending on the value selected I then want to populate another text box form control underneath.
To better explain, please see image below:
Example, if TCP Server IN is selected then the form control underneath (textbox)should automatically say "Inbound"
Ideally this text box should also have an attribute/configuration that prevents the user from entering their own text, grayed out perhaps. Once the create form is submitted, the textbox that contains this value "Inbound" will then be added to the SQL Table using Enitity Framework.
The solution requires that this field dynamically changes each time a new item is selected from the list.
Current code for the drop down list:
Page Model Class:
public IEnumerable<SelectListItem> ConnectorTypeList { get; private set; } // temp
public IActionResult OnGet()
{
// prepare the list in here
ConnectorTypeList = new SelectListItem[]
{
new SelectListItem ("TCP Server IN", "TCP Server IN"),
new SelectListItem ("TCP Server OUT", "TCP Server OUT"),
new SelectListItem ("SMTP Server IN", "SMTP Server IN")
};
return Page();
}
Page View:
<div class="form-group">
<label asp-for="ConnectorModel.ConnectorType" class="control-label"></label>
<select asp-for="ConnectorModel.ConnectorType" class="form-control" asp-items="#Model.ConnectorTypeList"></select>
<span asp-validation-for="ConnectorModel.ConnectorType" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConnectorModel.DataFlow" class="control-label"></label>
<input asp-for="ConnectorModel.DataFlow" class="form-control" />
<span asp-validation-for="ConnectorModel.DataFlow" class="text-danger"></span>
</div>
Note the current form-control I'm wanting to modify is the "ConnectorModel.DataFlow" in the above page view code. At the moment it's just a simple textbox that the user can enter their own choice of text.
I'm going round in circles having read up on page handlers etc. It seems there is a onchange event but unsure how to implement this and somehow link it back to the page model class, run a method then postback the result. I'm not looking for a JQuery script as it seems this should not be required in the newer framework, not sure I just don't want a complicated long solution given I will be using a lot of these throughout the app. Thanks in advance...
The easiest way is to use onchange() on your <select> tag and assign data to input using js.(Add id attribute for <select> and <input> before)
If you would like to prevent the user from entering their own text, just use readonly attribute for you input.
<input asp-for="DataFlow" id="dataFlow" class="form-control" readonly/>
The Sample Page View:
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ConnectorModel.ConnectorType" class="control-label"></label>
<select asp-for="ConnectorModel.ConnectorType" id="connectorTypeList" class="form-control" asp-items="#Model.ConnectorTypeList" onchange="assignData()">
<option>Select ConnectorType</option>
</select>
<span asp-validation-for="ConnectorModel.ConnectorType" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConnectorModel.DataFlow" class="control-label"></label>
<input asp-for="ConnectorModel.DataFlow" id="dataFlow" class="form-control" readonly />
<span asp-validation-for="ConnectorModel.DataFlow" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
#section Scripts{
<script>
function assignData() {
var contentType = $("#connectorTypeList").val();
if (contentType == "TCP Server IN") {
$("#dataFlow").val("Inbound");
}
}
</script>
}
Could someone help me resolve this issue. I'm trying to limit over posting with bind param action but it seems that it doesn't work at all. When I removed the Bind keyword, everything started to work as a charm.
Here is the code sample:
View Model:
public class ProductCreateViewModel
{
public Product Product { get; set; }
public ICollection<IFormFile> Images { get; set; }
}
Action:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Product.Id,Product.CategoryId,Product.Description,Product.Title")] ProductCreateViewModel productVM)
{
if (ModelState.IsValid)
{
_context.Add(productVM.Product);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewData["CategoryId"] = new SelectList(_context.Categories.Include(c => c.Categories).Where(c => c.ParentCategoryId == null), "Id", "Name", productVM.Product.CategoryId);
return View(productVM);
}
View:
#model CatalogWebApp.Models.ProductsViewModels.ProductCreateViewModel
#{
ViewData["Title"] = "Add Product";
ViewData["BigPageTitle"] = "Products";
ViewData["PageBoxTitle"] = "Add New Product";
}
<form asp-action="Create">
<div class="form-horizontal">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Product.CategoryId" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select name="Product.CategoryId" class ="form-control">
#foreach(Category item in (ViewBag.CategoryId as SelectList).Items)
{
<option value="#item.Id">#item.Name</option>
if (item.Categories != null && item.Categories.Count > 0)
{
foreach (var subCat in item.Categories)
{
<option value="#subCat.Id">--#subCat.Name</option>
}
}
}
</select>
</div>
</div>
<div class="form-group">
<label asp-for="Product.Description" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Product.Description" class="form-control" />
<span asp-validation-for="Product.Description" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Product.Title" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Product.Title" class="form-control" />
<span asp-validation-for="Product.Title" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Could someone pelase indicate if I have a problem or it is only a known asp.net core issue?
I'm not quite sure why you using Bind for your case.
Just create sepatate ViewModel with only properties you need like ProductCreateStort.
Then use this ViewModel in your controller signature and inherit your main model from it.
This way you won't mess with Bind and limit your params on POST
While I'm fairly new to ASP.NET Core myself (and coming to this question 7 months late), I ran into this same issue. I think the key here is that you have to bind "Product" for it to be considered. Binding "Product.Id" by itself doesn't appear to be good enough. So this should work:
[Bind("Product,Product.Id,Product.CategoryId,Product.Description,Product.Title")]
Of course, Hamid Mosalla's comment is a better option if ALL of your bound properties are on the nested object (which leads to wonder why you need a view model in the first place). In my case, I have a nested object AND a local property, so using the "Prefix" solution wasn't the right thing to do.
Anyway, hope this helps someone.
You need to pass your values as params string[], not as a single string separated by commas:
[Bind("Product.Id","Product.CategoryId","Product.Description","Product.Title")]
See Source
I am trying append EJS templates using jQuery, I am not sure if what I am doing is right.
views/form/room-form.ejs
<form>
...
<div class="add_room_inputs">
<%- include partials/add_room %>
</div>
<div id="NewAddRoomForm">
<p>Add new form</p>
</div>
...
</form>
\assets\linker\js\custom-functions.js
$(document).ready(function(){
$('#NewAddRoomForm').click(function() {
$('.add_room_inputs').append('<%- include /assets/linker/templates/add-room-input.ejs %>');
});
}
/assets/linker/templates/room-input.ejs
<input name="rooms[]" class="form-control" placeholder="Room Name" type="text">
alternative solution (custom-functions.js file) which fails on file not found
var html = new EJS({url: '/assets/linker/templates/add-room-input.ejs.ejs'}).render();
$('#NewAddRoomForm').click(function() {
$('.add_room_inputs').append(html);
});
How can I implement such thing?
Did you include jst.js in the layout? by default it should be there as you are using Sails JS.
What you're trying to do wont work...
The problem is that the server has already rendered the template and sent the result to the client. Also the client doesn't have access to the view files on the server.
An Ajax framework like KnockoutJS is probably closer to what you are looking for, but there are others.
Here...I decided to make you a fiddle
http://jsfiddle.net/8D34n/23/
SO wants code too so here is a repaste
<form>
<h1 data-bind="text: form_title"></h1>
Add Form
<br><br>
<div id="NewAddRoomForm" data-bind="foreach: form_list">
<div data-bind="text: 'Form '+($index() + 1)"></div>
<input data-bind="value: name" /><br>
<input data-bind="value: age" />
</div>
<br><br>
</form>
View results