How do I display a delete confirmation dialog before deleting a row in a DevExtreme DataGrid on a Razor page? - asp.net-core

I am using DevExtreme and razor pages in a Asp.NetCore web application.
I would like to inject client side delete confirmation dialog when deleting a row.
Currently I'm using an asp-action on the controller to make the call to do the delete.
I'm not sure how/where to do this in client side (cshtml file).
I assume that I'd be somehow using javascript/jquery for this?
Are there existign 3rd party open source libraries that I should use for client side dialogs/message boxes?
DevExtreme DataGrid definition in cshtml file.
#(Html.DevExtreme().DataGrid<Customer>()
.DataSource(Model)
.Columns(columns => {
columns.AddFor(m => m.CustomerName);
columns.AddFor(m => m.CustomerId).CellTemplate(
<form asp-action="DeleteCustomer" method="post">
<input type="hidden" value="<%- data.CustomerId %>" name="CustomerId" />
<input type="image" src="/icon/close.png" />
</form>
</text>).Caption("");
});
Server side control action code:
[HttpPost]
public async Task<IActionResult> DeleteCustomer(Guid customerId)
{
// Call WebApi Service to delete row
return RedirectToAction("Index");
}

I believe the datagrid has a built-in function for confirming the delete action on the client side. Might not be exactly what you were looking for but it could be a good start.
Here is the link to the demo page: https://demos.devexpress.com/ASPNetCore/Demo/DataGrid/RowEditingAndEditingEvents/

Related

Oppeniddict - How to skip logout prompt?

I am using velusia sample, I want the client app to skip the log out prompt page, is there any specific way to achieve this, or should I implement it my self ?
How you handle logout requests is up to you. To trigger a redirection to the client application (when a post_logout_redirect_uri is set) without displaying a consent form, trigger an ASP.NET Core Logout operation pointing to OpenIddict:
// Returning a SignOutResult will ask OpenIddict to redirect the user agent
// to the post_logout_redirect_uri specified by the client application or to
// the RedirectUri specified in the authentication properties if none was set.
return SignOut(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties
{
RedirectUri = "/"
});
That said, I wouldn't recommend doing that: not requiring user consent or a form of anti-forgery protection - the id_token_hint can help, use AuthenticateAsync() to retrieve the principal from it - may make targeted DOS attacks possible.
According to your description, I suggest you could try to set a js code to automatically click the logout button in the server side.
More details, you could refer to below codes:
Modify the server's logout view as below:
#using Microsoft.Extensions.Primitives
<div class="jumbotron">
<h1>Log out</h1>
<p class="lead text-left">Are you sure you want to sign out?</p>
<form asp-controller="Authorization" asp-action="Logout" method="post">
#* Flow the request parameters so they can be received by the LogoutPost action: *#
#foreach (var parameter in Context.Request.HasFormContentType ?
(IEnumerable<KeyValuePair<string, StringValues>>) Context.Request.Form : Context.Request.Query)
{
<input type="hidden" name="#parameter.Key" value="#parameter.Value" />
}
<input class="btn btn-lg btn-success" id="Confirm" name="Confirm" type="submit" value="Yes" />
</form>
</div>
#section scripts{
<script>
$(document).ready(function() {
console.log("Fired");
document.getElementById("Confirm").click();
});
</script>
}
You can also change the HTTP method to GET instead of POST based on Velusia sample:
[HttpGet("logout")]
public async Task<IActionResult> LogoutPost()
{
await HttpContext.SignOutAsync(Clients.CmsApp);
await HttpContext.SignOutAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
return SignOut(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties
{
RedirectUri = "/"
});
}

Disable request verification token in ASP.NET Core

ASP.NET Core MVC seems to inject a request verification token in all of my forms:
<form class="actions" method="post">
<input type="submit" class="btn btn-primary" value="Yes">
<a class="btn btn-secondary" href="/some/url">No</a>
<input name="__RequestVerificationToken" type="hidden" value="...">
</form>
I'm handling CSRF in Ajax and don't want this extra input element in all of my forms. Any way to disable it?
The element is added even without a call to AddAntiforgery in Startup.cs. I'm running on ASP.NET Core 3.1.
Antiforgery middleware is added to the Dependency injection container when one of the following APIs is called in Startup.ConfigureServices:
AddMvc
MapRazorPages
MapControllerRoute
MapBlazorHub
Details please check this document
To disable it, try below IgnoreAntiforgeryToken attribute
[Authorize]
[AutoValidateAntiforgeryToken]
public class ManageController : Controller
{
[HttpPost]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> DoSomethingSafe(SomeViewModel model)
{
// no antiforgery token required
}
}
Details can be found here
The token is appended by the Form Tag Helper. If you don't need the other features of the Tag Helper, it can be removed using #removeTagHelper (in view or globally by adding to _ViewImports.cshtml):
#removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
See ASP.NET Core documentation for further details/options.
Just idea I would make reference to that [IgnoreAntiforgeryToken] can be used to disable the global [AutoValidateAntiForgeryToken] attribute on certain actions if needed.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add(new IgnoreAntiforgeryTokenAttribute());
});
}
}

How to sign out over HttpContext in server-side Blazor

I access the HttpContext in a Blazor server-side view to manually log out. I added this line to Startup.cs: services.AddHttpContextAccessor(); and inject it in the view with #inject IHttpContextAccessor HttpContextAccessor.
I've got a log out button which tries to execute this code:
await HttpContextAccessor.HttpContext.SignOutAsync("Cookies");
but I get the following error message:
System.InvalidOperationException: 'Headers are read-only, response has already started.'
How can I prevent this error?
If you scaffolded Identity and overridden the old "LogOut.cshtml" from when you created the project via template, the Logout button won't logout. Assume you've used the default IdentityUser model. I ran into this issue and just wanted to add this if anyone else had this problem as well. I'm using Blazor Server with .Net 5.0.3's template. You can remove the Logout.cshtml.cs after as well.
Replace this \Areas\Identity\Pages\Account\LogOut.cshtml
#page
#model LogoutModel
#{
ViewData["Title"] = "Log out";
}
<header>
<h1>#ViewData["Title"]</h1>
#{
if (User.Identity.IsAuthenticated)
{
<form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="#Url.Page("/", new { area = "" })" method="post">
<button type="submit" class="nav-link btn btn-link text-dark">Click here to Logout</button>
</form>
}
else
{
<p>You have successfully logged out of the application.</p>
}
}
</header>
Replace with
#page
#using Microsoft.AspNetCore.Identity
#attribute [IgnoreAntiforgeryToken]
#inject SignInManager<IdentityUser> SignInManager
#functions {
public async Task<IActionResult> OnPost()
{
if (SignInManager.IsSignedIn(User))
{
await SignInManager.SignOutAsync();
}
return Redirect("~/");
}
}
This tripped me up too, but you need the logout functionality to be on a Razor Page (not a Blazor component). Create a Logout page and put your logout code in the OnGetAsync() method.
Your logout button can then link to the logout page.
http://lightswitchhelpwebsite.com/Blog/tabid/61/EntryId/4316/A-Demonstration-of-Simple-Server-side-Blazor-Cookie-Authentication.aspx - this is a helpful example
Don't use IHttpContextAccessor.
I guess that you're using ASP.NET Core Blazor authentication and authorization new system. If not, then start with it right now. Live is too short to be wasted over other things. This is the best product created so far for Blazor's authentication and authorization, and it is based on the Identity UI (This is not Blazor, of course). Additionally, there are a couple of Components which enable controlling the flow of authentication and authorization in your application, such as displaying a "Log in" button and a "Log out" button in your layout, interchangeably altering depending on your authentication state, etc.
Please, go to this page and start learning this excellent system, and then come here for specific issues you face:
https://learn.microsoft.com/en-us/aspnet/core/security/blazor/?view=aspnetcore-3.0&tabs=visual-studio
Hope this helps...

How to set the focus to an input field that didn't pass the validation

I have a simple ASP.NET Core 1.1 web application with a form with some input fields that are validated. when the user submits the form and the submitted input doesn't pass the validation, i'd like that the focus is set automatically to the (first) input field that didn't pass the validation.
Is there a way to do it in ASP.NET Core?
in webforms there were various server controls for validation (e.g. the RequiredFieldValidator) that referred to an input field and had a property named SetFocusOnError that allowed this function. so i was wondering if there is something similar for ASP.NET Core...
With ASP.NET Core you have to generate the required HTML and javascript to do this.
You have to add a Tag Helper to add a validation class
<div class="editor-field #Html.AddValidationClass("Email", "has-error")">
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
and then you could use a jQuery class selector:
$(function() {
$('.has-error :input').focus();
});

How can I use cshtml files with Durandal?

I got the DurandalJS StarterKit template on VS2012... All works great...
But in some views I need to do something like that:
#if (Roles.IsUserInRole("Administrators"))
{
<p>Test</p>
}
However with durandal all my views are '.html' files... Is that possible to use '.cshtml' files to access some information like that?
Or is there any other way to do that with durandal?
Junior
I am doing it like this:
Create a generic controller for Durandal views:
public class DurandalViewController : Controller
{
//
// GET: /App/views/{viewName}.html
[HttpGet]
public ActionResult Get(string viewName)
{
return View("~/App/views/" + viewName + ".cshtml");
}
}
Register a route:
routes.MapRoute(
name: "Durandal App Views",
url: "App/views/{viewName}.html",
defaults: new { controller = "DurandalView", action = "Get" }
);
Copy Views/web.config to /App/views/web.config (so Razor views work in this location).
This lets me use the normal Durandal conventions (even the html extension for views), and put durandal views as cshtml files in their normal location without adding any more server code.
If you also have static html views, you can also place the cshtml views in a subfolder or use the normal MVC /Views folder.
I wouldn't recommend using ASP.NET MVC with Durandal.
What you are probably looking to do is use the Razor view engine (to get the benefits of a compiler, strong typing etc.) which exists independently from ASP.NET MVC. Just WebAPI for data I/O is more than enough to very efficiently create a Durandal.js application.
If you are interested in using Razor/CSHTML with Durandal and Knockout there is an open source option out there called FluentKnockoutHelpers that may be exactly what you are looking for. It offers much of the 'nice' parts of ASP.NET MVC allowing you to use the awesome abilities of Durandal and Knockout with almost no downfalls.
Source
Live demo using Durandal.js
In a nutshell it provides a bunch of features which makes doing Durandal/Knockout development just as easy as ASP.NET MVC. (You simply provide a C# type that your JavaScript model is based off of for most of the features.) You only have to write JavaScript and un-compiled markup for complicated cases which is unavoidable and no different than MVC! (Except in MVC your code would also likely end up would also be a big jQuery mess which is why you are using Durandal/Knockout in the first place!)
Features:
Painlessly generate Knockout syntax with strongly typed, fluent, lambda expression helpers similar to ASP.NET MVC
Rich intellisense and compiler support for syntax generation
Fluent syntax makes it a breeze to create custom helpers or extend whats built in
OSS alternative to ASP.NET MVC helpers: feel free to add optional features that everyone in the community can use
Painlessly provides validation based on .NET types and DataAnnotations in a few lines of code for all current/future application types and changes
Client side JavaScript object factory (based on C# types) to create new items in for example, a list, with zero headaches or server traffic
Example without FluentKnockoutHelpers
<div class="control-group">
<label for="FirstName" class="control-label">
First Name
</label>
<div class="controls">
<input type="text" data-bind="value: person.FirstName" id="FirstName" />
</div>
</div>
<div class="control-group">
<label for="LastName" class="control-label">
Last Name
</label>
<div class="controls">
<input type="text" data-bind="value: person.LastName" id="LastName" />
</div>
</div>
<h2>
Hello,
<!-- ko text: person.FirstName --><!-- /ko -->
<!-- ko text: person.LastName --><!-- /ko -->
</h2>
Provide FluentKnockoutHelpers with a .NET type and you can do this in style with Intellisense and a compiler in Razor / CSHTML
#{
var person = this.KnockoutHelperForType<Person>("person", true);
}
<div class="control-group">
#person.LabelFor(x => x.FirstName).Class("control-label")
<div class="controls">
#person.BoundTextBoxFor(x => x.FirstName)
</div>
</div>
<div class="control-group">
#person.LabelFor(x => x.LastName).Class("control-label")
<div class="controls">
#person.BoundTextBoxFor(x => x.LastName)
</div>
</div>
<h2>
Hello,
#person.BoundTextFor(x => x.FirstName)
#person.BoundTextFor(x => x.LastName)
</h2>
Take a look at the Source or Live Demo for an exhaustive overview of FluentKnockoutHelper's features in a non-trivial Durandal.js application.
Yes, you can absolutely use cshtml files with Durandal and take advantage of Razor on the server. I assume that also means you want MVC, so you can do that too and use its routing.
If you don;t want the routing then you can set the webpages.Enabled in the web.config, as the other comments suggest.
<add key="webpages:Enabled" value="true" />
I don't recommend that you use .cshtml files as views directly. You're better off placing the .cshtml files behind a controller.
For example, take the HotTowel sample, edit /App/main.js, and replace the function definition with the following:
define(['durandal/app',
'durandal/viewLocator',
'durandal/system',
'durandal/plugins/router',
'durandal/viewEngine',
'services/logger'],
function (app, viewLocator, system, router, viewEngine, logger) {
Note that we added a reference to the Durandal viewEngine. Then we need to replace
viewLocator.useConvention();
with
viewLocator.useConvention('viewmodels', '../../dynamic');
viewEngine.viewExtension = '/';
The first argument to viewLocation.useConvention sets the /Apps/viewmodels/ directory as the location for the view models js files, but for the view location, uses the URL http://example.com/dynamic/, with an extension of '/'. So that if Durandal is looking for the view named 'shell', it will reference http://example.com/dynamic/shell/ (this is because the view directory is mapped relative to the viewmodels directory, hence /App/viewmodels/../../dynamic will give you simply /dynamic).
By convention, this previous URL (http://example.com/dynamic/shell/) will be mapped to the controller DynamicController, and the action "Shell".
After this, you simply add a controller - DynamicController.cs, like this:
// will render dynamic views for Durandal
public class DynamicController : Controller
{
public ActionResult Shell()
{
return View();
}
public ActionResult Home()
{
return View();
}
public ActionResult Nav()
{
return View();
}
public ActionResult Details()
{
return View();
}
public ActionResult Sessions()
{
return View();
}
public ActionResult Footer()
{
return View();
}
}
Create .cshtml files for each of the above actions. This way you get to use controllers, server side IoC et al to generate dynamic views for your SPA.
DurandaljS is a client framework which forms mainly a solid base for single-page apps (SPA).
I assume you are using asp.net web API as your server technology. In that case, you can determine the user's role inside your API controller and based on that return data to the client. On the client you can use Knockout "if" binding in order to show / hide certain areas of your page.
What you perhaps can do is placing this code in the Index.cshtml.
Following link shows how to customize moduleid to viewid mapping
http://durandaljs.com/documentation/View-Location/
by convention durandal tries to find view url in following steps
1) Checke whether object has getView() function which returns either dom or a string ( url for the view)
2) If object does not have getView function then checks whether object has viewUrl property
3) If above two steps fails to produce url or a DOM view drundal falls to default convention
which maps moduleid xyz.js to view xyz.html using view url ( path of Views folder ) defined in main.js
so for moduleid xyz.js path of the view will be views/xyz.html
you can overwrite this default mapping behavior by overwriting convertModuleIdToViewId function.
So there are many ways you can customize your view url for specific model (.js object)
I made an extension to Durandal which gives you the ability to place an applicationContent div in your cshtml file together with the applicationHost div. In applicationContent you can now use both ASP .Net MVC syntax together with knockout bindings.
Only thing I did was put some extra code in the viewLocator.js file which looks for an applicationContent div:
locateViewForObject: function(obj, area, elementsToSearch) {
var view;
if (obj.getView) {
view = obj.getView();
if (view) {
return this.locateView(view, area, elementsToSearch);
}
}
if (obj.viewUrl) {
return this.locateView(obj.viewUrl, area, elementsToSearch);
}
view = document.getElementById('applicationContent');
if (view) {
return this.locateView(view, area, elementsToSearch);
}
var id = system.getModuleId(obj);
if (id) {
return this.locateView(this.convertModuleIdToViewId(id), area, elementsToSearch);
}
return this.locateView(this.determineFallbackViewId(obj), area, elementsToSearch);
},
Your original cshtml file can now do something like this:
<div class="row underheader" id="applicationContent">
<div class="small-5 columns">
<div class="contentbox">
#using (Html.BeginForm("Generate", "Barcode", FormMethod.Post, Attributes.Create()
.With("data-bind", "submit: generateBarcodes")))
{
<div class="row formrow">
<label for="aantalBijlagen">#Translations.Label_AantalBijlagen</label>
</div>
<div class="row">
<select name="aantalBijlagen" class="small-6 columns">
<option>0</option>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
</div>
<div class="row">
<button class="button right" type="submit" id="loginbutton"><span class="glyphicon glyphicon-cog"></span> #Translations.Action_Generate</button>
</div>
}
</div>
</div>
<div class="small-7 columns" data-bind="if: hasPdfUrl">
<div class="contentbox lastcontent">
<iframe data-bind="attr: {src: pdf_url}"></iframe>
</div>
</div>
You can find my fork of the durandal project here and a small blogpost of what and how I did this here.
I'm not very familiar with DurandalJS but because it's a client-side system, it should make no difference what technology is used on the server to generate the HTML markup. So if you use Razor CSHTML files to generate the HTML on the server, DurandalJS should work just fine with it.
If you're getting a particular error then please share that error, but I can't think of any reason why it wouldn't work.