Facing problem in blazor component while uploading a file - c#

I am using .net core 6. I created a MVC web application and used Blazor component.
I created a component named 'NewUser'.
#code {
private ApplicationUsers userModel = new ApplicationUsers();
private ApiResponseMasterData _response = new ApiResponseMasterData();
private async Task HandleSignatureChange(ChangeEventArgs e)
{
//signature = (IFormFile)e.Value;
}
}
Here's the razor code:
#using Microsoft.AspNetCore.Components;
#using Microsoft.AspNetCore.Components.Web;
#using MODELS.DBModels;
#using Microsoft.AspNetCore.Http;
#using Microsoft.Extensions.DependencyInjection;
#using Microsoft.Extensions.Http;
#using Newtonsoft.Json;
#inject HttpClient http;
#inject IConfiguration Configuration;
<EditForm Model="#userModel" OnValidSubmit="#HandleSubmit">
<div class="custom-file">
<input type="file" class="custom-file-input" id="ProfilePicture" #onchange="HandleProfilePictureChange">
</div>
</EditForm>
Problem: on chageeventargs I want to save the uploaded file to my local folder. I tried but I'm getting only the path.

Related

Asp.net MVC TagHelper not registering

I have been following this blog entry https://blog.maartenballiauw.be/post/2020/04/14/building-an-aspnet-core-tag-helper-to-show-hide-ui-elements-based-on-authorization.html to make a custom TagHelper.
However, I can't seem to get the TagHelper to be recognised.
AuthRolesTagHelper.cs:
using System;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Xml.Linq;
using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
namespace DHS_Intranet.Helpers
{
[HtmlTargetElement("*", Attributes = "asp-authroles")]
public class AuthRolesTagHelper : TagHelper
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly MyManager<ApplicationUser> _myManager;
public AuthRolesTagHelper(
IHttpContextAccessor httpContextAccessor, MyManager<ApplicationUser> myManager)
{
_httpContextAccessor = httpContextAccessor;
_myManager = myManager;
}
[HtmlAttributeName("asp-authroles")]
public string AuthRoles { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
await base.ProcessAsync(context, output);
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext != null)
{
//Get ApplicationUser from HTTP context
var _user = await _myManager.GetUserAsync(httpContext.User);
// check if user roles are in supplied roles.
if (!(await _myManager.IsInRoleAsync(_user, AuthRoles)))
{
output.SuppressOutput();
}
}
}
}
}
Essentially, I want to supply a role in an attribute asp-authroles="ExampleRole" in a tag and if the user is not in the role for the output to be suppressed (essentially hidden).
I know that this is possible using code blocks within the razor page, but I'm trying to keep things streamlined.
I have used #addTagHelper *, DHS_Intranet in the _ViewImports.cs
However when I use it (example below), it doesn't recognised the TagHelper out just outputs the Html with the attribute visible.
I've even dropped a codebreak on the TagHelper code and it never gets triggered.
Example page:
<div class="">
<h1 class="display-4">Directory</h1>
</div>
<div class="input-group">
<input id="search" type="text" class="form-control rounded" placeholder="Search" />
<i class="bi bi-plus-square-fill"></i>
</div>
<div asp-authroles="CanDeleteDirectory">Can delete</div>
Any help or suggestions would be really welcome.
Looking again, it turns out that in my _ViewImports.cs I needed:
#addTagHelper *, DHS-Intranet rather than #addTagHelper *, DHS_Intranet
DHS-Intranet is the app name, I thought I needed the use the namespace.

Blazor Server side, ExternalRegister buttons at .razor page

Is possible to have the buttons "External Registration" placed inside .razor page (server side)?
The below code is from ExternalRegister.cshtml but I would like to have that two registration buttons (Google, Facebook) as part of the Start.razor page. Is that possible?
#model Aplication.Areas.Identity.Pages.Account.ExternalRegisterModel
<form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="#Model.ReturnUrl" method="post" class="form-horizontal">
<div>
<p>
#foreach (var provider in Model.ExternalLogins)
{
<button type="submit" class="btn btn-primary" name="provider" value="#provider.Name" title="Log in using your #provider.DisplayName account">#provider.DisplayName</button>
}
</p>
</div>
</form>
Yes, it is possible to have the buttons in your razor page.
Of course, to do this, you need to be able to enumerate the available providers, which means you need to pass them in to your Blazor application from
_Host.cshtml (or wherever you host the Blazor application)
note: you cannot pass a list of AuthenticationScheme because .NET will not serialise them, which is why I transform them into a DTO ExternalProvider
#inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
#inject SignInManager<IdentityUser> _signInManager
#{
var state = new InitialApplicationState
{
XsrfToken = Xsrf.GetAndStoreTokens(HttpContext).RequestToken,
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
.ToList()
.Select(scheme=>
new ExternalProvider {Name=scheme.Name, DisplayName=scheme.DisplayName}
)
};
}
<component type="typeof(App)" param-InitialState="state" render-mode="ServerPrerendered" />
The InitialApplicationState and ExternalProvider are simple DTO classes
public class InitialApplicationState
{
public string XsrfToken { get; set; }
public IEnumerable<ExternalProvider> ExternalLogins { get; set; }
}
public class ExternalProvider
{
public string Name { get; set; }
public string DisplayName { get; set; }
}
Now, you need to receive this data in your Blazor code as a Parameter on the App.razor component
#inject InitialApplicationState InitialStateService
#code {
[Parameter] public InitialApplicationState InitialState { get; set; } = default;
protected override Task OnInitializedAsync()
{
InitialStateService.XsrfToken = InitialState.XsrfToken;
InitialStateService.ExternalLogins = InitialState.ExternalLogins;
return base.OnInitializedAsync();
}
}
All we are doing here is declaring the Parameter InitialState that will receive our InitialApplicationState - and then we store that state in a service InitialStateService which is configured in startup.cs as a Scoped dependency.
builder.Services.AddScoped<InitialApplicationState>();
Now, we have a service in our DI container for Blazor that contains a list of available external authentication providers and our forgery protection token.
We can inject the InitialApplicationState anywhere we need it in Blazor e.g. Index.razor and enumerate the ExternalLogins to render buttons
The form is declared slightly differently in Blazor as we don't have the asp* directives:
#inject InitialApplicationState InitialStateService
<form id="external-account"
action="/Identity/Account/ExternalLogin"
method="post">
<div>
<p>
#foreach (var provider in InitialStateService.ExternalLogins)
{
<button type="submit"
name="provider"
value="#provider.Name"
title="Log in using your #provider.DisplayName account">
#provider.DisplayName
</button>
}
</p>
</div>
<input name="__RequestVerificationToken" type="hidden"
value="#InitialStateService.XsrfToken">
</form>
I think the best strategy is to define two OnPost method in your Razor PageModel (Code-Behind). For example:
public void OnPostFaceBook(ExternalLogin provider)
{
//your code here
}
public void OnPostGoogle(ExternalLogin provider)
{
//your code here
}
And in your .cshtml file place two separate form for each one, and add parameter
asp-page-handler
to each submit button. For example:
<button type="submit" class="btn btn-primary" value="FaceBook" value="FaceBook" asp-page-handler="FaceBook">Log in using your FaceBook account</button>
and in other form:
<button type="submit" class="btn btn-primary" value="Google" value="Google" asp-page-handler="Google">Log in using your Google account</button>

How to use partials to "get" stuff in asp.net-core razor pages?

I have the following partial located at Pages/Partials/
Search.cshtml:
#model SearchModel
<body>
<form method="get">
<div class="d-flex flex-row">
<div id="search-div" class="form-group">
<input asp-for="#Model.SearchString" value="#Model.SearchString" class="form-control" id="search-bar" placeholder="Enter ID" />
</div>
<div>
<button class="btn btn-primary" type="submit">Search</button>
</div>
</div>
</form>
</body>
Search.chshtml.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AdminPortal.Web.Pages.Partials
{
public class SearchModel : PageModel
{
[BindProperty(SupportsGet = true)]
public string SearchString { get; set; }
public void OnGet()
{
}
}
}
Home.cshtml.cs:
#page "/Home"
#using AdminPortal.Web.Pages
#model HomeModel
<body>
<partial name="Partials/Search" model="new Pages.Partials.SearchModel()" />
<partial name="Partials/Map" model="new Pages.Partials.MapModel()" />
</body>
startup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
namespace AdminPortal.Web
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
}
However, when I click submit, it does not call the OnGet() method. What am I doing wrong?
However, when I click submit, it does not call the OnGet() method.
What am I doing wrong?
That is because partial view is a razor view instead of a razor pages.Razor view is just a view and it works with controller.More detailed explanation you could refer to:
https://stackoverflow.com/a/50158395/11398810
The correct way is to add the backend code in your Home page:
Home.cshtml.cs:
public class HomeModel : PageModel
{
[BindProperty(SupportsGet = true)]
public string SearchString { get; set; }
public void OnGet()
{
}
}
Search.cshtml:
#model HomeModel
<body>
<form method="get">
//...
</form>
</body>
Or if you do not want to use the default OnGet method in Home page,you could use page handler:
Home.cshtml:
#page "/Home/{handler?}" //change here
#using AdminPortal.Web.Pages
#model HomeModel
<body>
<partial name="Partials/Search" model="new Pages.Partials.SearchModel()" />
<partial name="Partials/Map" model="new Pages.Partials.MapModel()" />
</body>
Home.cshtml.cs:
public class HomeModel : PageModel
{
[BindProperty(SupportsGet = true)]
public string SearchString { get; set; }
public void OnGet()
{
}
public void OnGetSearch()
{
//do your stuff...
}
}
Search.cshtml:
#model HomeModel
<body>
<form method="get" asp-page-handler="Search">
//...
</form>
</body>
You need to add the action value manually or set it via form tag helper methods like asp-page.
<form method="get" action="/Search">
</form>
Without an action value, the browser will send it to the current URL. This works fine when you have a page with a form and it should send it to the same page model, like GET (display the page) and POST, send the form.
However, with your partial - that is used across different pages - you need to specify the page by setting the correct URL.

Change button label text dynamically in razor pages

I am using Razor Pages in my ASP.NET Core web application.
I would like to change the button text from code-behind. Even if i add runat="server" in Html markup and I can't access the button in *.cshtml.cs file.
Also, I can not use <asp:button> in razor pages. Is there any way to do without using the javascript approach?
<input type="submit" value="Create" class="btn btn-primary"/>
MyPage.cshtml.cs
public IActionResult OnGet(Guid id)
{
if(id == Guid.Empty) //Make button create
else // make it update
}
Razor Pages in .net core are quite a bit different than classic aspx pages. There are a number of good intro articles out there in this one from MS. Using your question and some sample code from that article this what the page might look like.
The cshtml file might look like:
#page
#using RazorPagesIntro.Pages
#model IndexModel2
<h2>Separate page model</h2>
<p>
<input type="submit" value="#Model.Label" class="btn btn-primary"/>
</p>
The cshtml.cs page model:
using Microsoft.AspNetCore.Mvc.RazorPages;
using System;
namespace RazorPagesIntro.Pages
{
public class IndexModel2 : PageModel
{
[BindProperty]
public string Label { get; set; }
public void OnGet()
{
if (id == Guid.Empty) {
Label = "Create";
} else {
Label = "Update";
}
}
}
}

Saving post from textarea to database in Razor pages WebApplication

I'm trying to build a social network on a ASP.NET Core Web Application in Visual Studio.
There is membership and a wall, where members can make posts.
I'm using Tinymce for a textarea with editing tools, and I'm supposed to save the text in the textarea in HTML form to my local Visual Studio database, along with the UserId of the connected user who is posting.
When I run the application and try to post, neither of these seem to work. I'm getting a null entry in the database for both the Text and the UserId.
While UserManager works properly on the .cshtml file and does return the UserId, I can't get it to work on the .cs file
Related code so far, of the .cshtml file:
#using Microsoft.AspNetCore.Identity
#inject UserManager<ApplicationUser> UserManager
#model IndexModel
#{
ViewData["Title"] = "Home page";
}
<form asp-route-returnUrl="#Model.ReturnUrl" method="post" enctype="multipart/form-data" asp-controller="UploadFiles" asp-action="Index">
<div class="form-group">
<p>You are connected and can post as #UserManager.GetUserId(User)</p>
<textarea name="txt" id="mytextarea" rows="2" cols="80" asp-for="Posts.Text" >
</textarea>
</div>
<br />
<div class="form-group">
<input type="submit" value="Post" class="btn btn-default" />
</div>
</form>
Related code of the .cs file:
private readonly proj.Data.ApplicationDbContext _context;
public IndexModel(proj.Data.ApplicationDbContext context)
{
_context = context;
}
public Models.Posts Posts { get; set; }
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
UserManager<Data.ApplicationUser> UserManager;
var post = new Models.Posts {UserId = UserManager.GetUserId(User), Text = Posts.Text };
_context.Posts.Add(post);
await _context.SaveChangesAsync();
return Page();
}
I get an "unassigned local variable" error for UserManager.
Can you please help me get the content of the textarea correctly, and define UserManager properly?
You seem to have used Depend injection so you can try constructor injection
private readonly proj.Data.ApplicationDbContext _context;
private UserManager<Data.ApplicationUser> _userMannger;
public IndexModel(proj.Data.ApplicationDbContext context,
UserManager<Data.ApplicationUser> user)
{
_userMannger= user;
_context = context;
}
public Models.Posts Posts { get; set; }
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
var post = new Models.Posts {UserId = _userMannger.GetUserId(User), Text = Posts.Text };
_context.Posts.Add(post);
await _context.SaveChangesAsync();
return Page();
}
I figured out the textarea problem.
The line should be like this instead:
<textarea asp-for="Posts.Text" id="mytextarea" rows="2" cols="80" class="form-control"></textarea>
I forgot to include class="form-control" and name="txt" was somehow causing a problem and needed to be omitted.

Categories