Redirecting in blazor with parameter - c#

Hello how can you redirect to another page in Blazor with a parameter?
#page "/auth"
#using Microsoft.AspNetCore.Blazor.Services;
#inject AuthService auth
#inject IUriHelper urihelper;
<input type="text" bind="#Username" />
<button onclick="#AuthAsync">Authenticate</button>
#functions{
public string Username { get; set; }
public string url = "/home";
public async Task AuthAsync()
{
var ticket=await this.auth.AuthenticateAsync(Username);
urihelper.NavigateTo(url); //i see no overload that accepts parameters
}
}
In this case i want to navigate to the /home page giving it a string as parameter.

Do this:
Create a home.cshtml file page like this:
Note that two #page directive are employed since optional parameters are not supported yet.
The first permits navigation to the component without a parameter. The second #page directive
takes the {username} route parameter and assigns the value to the Username property.
Pages/home.cshtml
#page "/home"
#page "/home/{username}"
<h1>#Username is authenticated!</h1>
#functions {
// Define a property to contain the parameter passed from the auth page
[Parameter]
private string Username { get; set; };
}
Do this in your auth.cshtml
#functions{
public string Username { get; set; }
public string url = "/home";
public async Task AuthAsync()
{
var ticket=await this.auth.AuthenticateAsync(Username);
// Attach the parameter to the url
urihelper.NavigateTo(url + "/" + Username);
}
}
Hope this helps...

You can only pass parameters in the URL at present.
So, if your home component was expecting [Parameter] string Name you would need to provide a URL of /home/fred and fred would be passed into the Name parameter of the home component.
If you are looking to pass more complex data then you would have to look at doing it via some kind of service.
Here is the link to the official docs on routing parameters: https://blazor.net/docs/routing.html#route-parameters

Related

passing global data in partial views without duplicating code

I have an application where I need to display a username and image for the current user. I could pull the data into a viewmodel and display in the view, however this would mean that i would need to do this in every controller that uses the view. Is there a way where I can global set values and call in some partial views without having to repeatedly duplicate it in each controller?
Sounds to me like you need to call a child action from your _Layout.cshtml. Here's how it could look.
First from what you have said I am assuming your viewModel will be this
public class UserDisplayViewModel
{
public string UserName { get; set; }
public string ImageUrl { get; set; }
}
You will need a controller that is responsible for getting the username and image data.
It would look like this
public class UserController : Controller
{
[ChildActionOnly]
public ActionResult _userDisplay()
{
var viewModel = GetUserNameAndImage();
return View(viewModel);
}
private UserDisplayViewModel GetUserNameAndImage()
{
//code for getting the username and image data
}
}
You would call the child action from your _layout.cshtml file like this
#Html.Action("_userDisplay", "User")
It goes wherever you want the username and image to appear in your HTML.
You will also need a partial view called _userDisplay.cshtml which will contain the markup for how you want to display your username and image. Here's a very basic example
#model UserDisplayViewModel
#{
Layout = null;
}
<p>
Username: #Model.UserName
</p>
<p>
<img src="#Model.ImageUrl"/>
</p>

ASP.NET CORE, View model has all fields as null when passed back to Controller

I'm trying to get simple ASP.NET CORE web app going and I'm running into issues with allowing the user to access a file on my server via a html link. I have a model called "TestModel", a view called "TestView" and a controller called "AppController". My view allows the user to input some text in two separate fields as well as select two different files from their hard drive, it binds everything to my model and performs a POST when the user clicks a button. I have verified that the model is correctly being passed back to my controller. My controller correctly saves the files to a folder in my server directory, uses a separate service to manipulate the files, and returns the model back to the view, i.e. when I use the debugger to inspect "return View(model)" in the Testview action the model being passed back has all it's properties populated.
The issue is that I want two links on the view to point to the files on the server so that the user can click the link and receive a prompt to download the files, but I can't seem to get it going. I am using the #Html.ActionLink() capability in razor to point to a "Download descriptions" action and pass the model to it, thinking that it would be passing the current view's model, but the model it passes has all fields as null. Any insight into what I am doing incorrectly?
These are the relevant actions in my AppController:
[HttpPost("~/App/TestView")]
public IActionResult TestView(TestModel Model)
{
if (ModelState.IsValid)
{
Console.WriteLine("Something was sent back from the TestView page");
Model.OnPostAsync();
var x = _descLogicService.Convert(Model);
}
return View(Model);
}
public IActionResult DownloadDescriptions(TestModel model)
{
var cd = new ContentDispositionHeaderValue("attachment")
{
FileNameStar = model.DescriptionFile.FileName
};
Response.Headers.Add(HeaderNames.ContentDisposition, cd.ToString());
byte[] bytes = System.IO.File.ReadAllBytes(model.MeasurementExportFile);
using (FileStream fs = new FileStream(model.MeasurementExportFile, FileMode.Open, FileAccess.Read))
{
fs.Read(bytes, 0, System.Convert.ToInt32(fs.Length));
fs.Close();
}
return File(bytes, "text/csv");
}
Here is my View Model:
public class TestModel
{
[BindProperty]
[Required]
[MinLength(5)]
public string Name { get; set; }
[BindProperty]
[Required]
[MaxLength(10,ErrorMessage ="Input string is too long.")]
public string MyInput { get; set; }
[BindProperty]
[Required]
public IFormFile DescriptionFile { get; set; }
[BindProperty]
[Required]
public IFormFile MeasurementFile { get; set; }
public string DescriptionDirectory { get; set; }
public string DescriptionExportFile { get; set; }
public string MeasurementDirectory { get; set; }
public string MeasurementExportFile { get; set; }
public async Task OnPostAsync()
{
var token = DateTime.Now.Ticks.ToString();
var directory = Directory.CreateDirectory(#"uploads\"+ token + #"\");
DescriptionDirectory = directory.CreateSubdirectory("Descriptions").FullName + #"\";
DescriptionExportFile = directory.CreateSubdirectory(#"Descriptions\Exports\").FullName + DescriptionFile.FileName;
MeasurementDirectory = directory.CreateSubdirectory("Measurements").FullName + #"\";
MeasurementExportFile = directory.CreateSubdirectory(#"Measurements\Exports\").FullName + MeasurementFile.FileName;
var file = Path.Combine(DescriptionDirectory, DescriptionFile.FileName);
using (var fileStream = new FileStream(file, FileMode.Create))
{
await DescriptionFile.CopyToAsync(fileStream).ConfigureAwait(true);
}
file = Path.Combine(MeasurementDirectory, MeasurementFile.FileName);
using (var fileStream = new FileStream(file, FileMode.Create))
{
await MeasurementFile.CopyToAsync(fileStream).ConfigureAwait(true);
}
}
}
And here is the View:
#**I need to add the namespace of C# models I'm creating *#
#using FirstASPNETCOREProject.ViewModels
#*I need to identify the model which 'fits' this page, that is the properties of the model can be
bound to entities on the view page, using "asp-for"*#
#model TestModel
#{
ViewData["Title"] = "Page for File Uploads";
}
#section Scripts{
}
<div asp-validation-summary="ModelOnly" style="color:white"></div>
<form method="post" enctype="multipart/form-data">
<label>Enter a Description File Name</label>
<input asp-for="Name" type="text" />
<span asp-validation-for="Name"></span>
<br>
<label>Select a Description File</label>
<input asp-for="DescriptionFile" type="file" />
<span asp-validation-for="DescriptionFile"></span>
<br>
<label>Enter the Measurement File Name</label>
<input asp-for="MyInput" type="text">
<span asp-validation-for="MyInput"></span>
<br>
<label>Select a Measurement File</label>
<input asp-for="MeasurementFile" type="file">
<span asp-validation-for="MeasurementFile"></span>
<br>
<input type="submit" value="Send Message" />
</form>
#Html.ActionLink("Description File", "DownloadDescriptions", "App")
#Html.ActionLink("Measurement File", "DownloadMeasurements", "App")
ActionLinks are just anchor tags.
So they use GET. So to pass back your model using get you would need to use Query String parameters for example adding "?MyInpu=some cool input" at the end of the url.
You can bind almost ANY complex object like this including Lists and Arrays.
For more information on Model Binding
The file itself you wont be able to pass it like that. For that you will need to POST the form with a submit button or FormData using javascript.
You can also add anchors that call javascript functions that use AJAX to post back all you want to the DownloadDescriptions action in your controller.
Here is an example on how to pass a model using an action link:
#Html.ActionLink("Test Action", "TestAction", "Home", new { myInput = "Some Cool Input" })
In my case the previous ActionLink produces an anchor tag with href set to:
http://localhost:64941/Home/TestAction?myInput=Some Cool Input
Notice how I used an anonymous type to pass the model using the same names of the properties of my model in this case MyInput but in camelized version myInput.
You can compose any model like that.
This is my action in my controller:
public IActionResult TestAction([FromQuery]TestModel input)
{
return View(input);
}
Notice how I used [FromQuery] for the TestModel parameter to indicate that I expect the ASP.NET Core model binder to use the Query String parameters to populate my model.
This is my model class:
public class TestModel
{
public string MyInput { get; set; }
}
This is the result during debugging:
Notice how during debugging I am able to see the populated value.
NOTES:
If your model changes at the client side. You will need to update the Query String parameters in the anchor tag using javascript... for that reason is a good idea to add name to the anchor tag.
Also this might answer your question but might NOT be the best approach to what you are trying to do.

How do I pass a parameter to RazorPage OnGet()?

Using razor pages I am trying to build an ecommerce website for selling clothing and I want to use once page as a template to load both mens and womens pages. To test this could be done in my _Layout.cshtml page I use <a asp-page="/Shop" asp-route-id="Mens" class="nav-link">Mens</a> and then in:
Shop.cshtml.cs
[BindProperties]
public class ShopModel : PageModel
{
public string Status { get; set; }
public void OnGet(string gender)
{
switch (gender)
{
case "Mens":
//get men page
Status = gender;
break;
case "Womens":
//get womens page
break;
}
}
}
Shop.cshtml
#page
#model ECommerce.Pages.Shop.ShopModel
#{
ViewData["Title"] = "Shop";
}
<p>
#Model.Status
</p>
When I debug the value of gender comes up as null, so status is never set. Am I going about this the wrong way? Any help is much appreciated, thanks!
The parameter name is gender, not id, so it should be
<a asp-page="/Shop" asp-route-gender="Mens" class="nav-link">Mens</a>
See more about anchor tag helper's route value.
If you are going to use another method other than OnGet for example OngetView you would have to put
Ver

Rendering LabelFor doesn't display the overwritten getter function

pardon me if this is a noob question but I have already searched on SO, google, and spend over an hour with an ASP.NET MVC 4 PRO book reading on Rendering Stronly Typed Helpers etc. I am just beginning learning ASP.NET 4 MVC so please go easy on me.
I have a model class called USER and a property called Name with an overwritten getter and setter (its me calling it this way, im not sure if this is the proper naming in the case)
//using...
namespace MvcMyApplication1.Models
{
public class User
{
[ScaffoldColumn(false)]
public int Id { get; set; }
public string Name
{
get
{
return WindowsIdentity.GetCurrent().Name.Split('\\')[1];
}
set
{
Name = WindowsIdentity.GetCurrent().Name.Split('\\')[1];
}
}
[Required(ErrorMessage="Password is required")]
public string Password { get; set; }
}
}
So, In my View I am trying to display the result of the get function, but I am lost and not sure how to do this.
#using(Html.BeginForm())
{
#Html.ValidationSummary(excludePropertyErrors: true);
// changed from LabelFor to DisplayFor
#Html.DisplayFor(m => m.Name)<br />
#Html.PasswordFor(m => m.Password)<br />
<input type="submit" value="Log in" />
}
I have tried to add attributes but then I am not sure how to assign the Name= to the get function
[DisplayName(Name="I want to call the get function here")]
public string Name { get; set; }
In my controller I have this code:
[HttpGet]
public ActionResult Index()
{
User newUser = new User();
return View();
}
[HttpPost]
public ActionResult Index(User m)
{
if (ModelState.IsValid)
{
return View("Report", m);
}
{
return View(m);
}
}
And this goes to the Report View
which normally displays the Windows login
#using (Html.BeginForm())
{
#Html.DisplayForModel()
}
EDIT: After swapping the LabelFor with DisplayFor the windows login is rendered but only after clicking the log in button. It does not render the first time I open the page
You're not passing in the model to the initial view:
return View();
should be
return View(newUser);
You should be using DisplayFor not LabelFor if you want to retrieve the property value.
#Html.DisplayFor(m => m.Name)
To get current authenticated user in the view, you can use #User.Identity.Name.
In controller, use User.Identity.Name
To display "I want to call the get function here" in DisplayName attribute in the view, use #Html.DisplayNameFor(m => model.Name)
Use WindowsIdentity in your class is not a good idea. For MVC, better use User.Identity.Name (use this in controller), the reason is this way it is get the user name under current HttpRequest, use WindowsIdentity might be fine, but careful. Depending on how you use it, it might return error or return the service account which ran the application pool.
Normally, getter and setter are written like this by calling a private property:
public class User{
private string _name;
public string Name
{
get{
return _name;
}
set{
_name = WindowsIdentity.GetCurrent().Name.Split('\\')[1];
}
}
It looks like your MVC app is using Windows Authentication, which is an intranet app, so your user will be automatically authenticated, which means logged on already.

MVC3 Model binding in HTTP GET request?

Without customization, can I do something like this in MVC 3?
[HttpGet]
public ViewResult MyAction(ViewModel model)
{
// Do stuff
return View("ViewName", model);
}
The reason I am doing this is to pass data between different pages as part of a work flow. (I.e. when user fnishes what's needed in step 1, pass the form data to step 2...)
It will work as long as you have the same parameter Name as of the Property name of your Model class
Assuming your class is like this
public class ViewModel
{
public string Name { set;get;}
public string Loc{ set;get;}
}
You can do a Get request like this
MyAction?Name=jon&Loc=America
Shyju's answer only works if the members of class in the endpoint's method signature contains only scalar properties. But what if you have nested classes? Let's assume that your ViewModel class looks like this:
public class ViewModel
{
public string Name { get; set; }
public string Title { get; set; }
public Address MyAddress { get; set; }
}
And the Address class looks like this:
public class Address
{
public string Line1 { get; set; }
public string Line2 { get; set; }
}
Now let's say the GET request was done via AJAX and you did something like this in JavaScript:
var address = {
Line1: "123 Nowhere St.",
Line2: "Apt. B5"
}
var getRequestData = {
Name: "Joe",
Title: "Manager",
MyAddress: address
}
var uriString = $.param(getRequestData); //the parameters for the GET request
$.get("/ViewResult?" + uriString, function (data) { /*callback function*/ });
Even though the shape of your address object in JavaScript perfectly matches the C# Address class in the endpoint's method signature, the Line1 and Line2 sub-properties will NOT bind. Their values will come through as null.
There are two workarounds to this.
Workaround 1:
The first is to use dot notation when naming the parameters in the GET request instead of nested JavaScript objects. Using this method, the GET request data in AJAX would look like this:
var getRequestData = {
Name: "Joe",
Title: "Manager",
MyAddress.Line1: "123 Nowhere St.",
MyAddress.Line2: "Apt. B5"
}
MVC model binding will know how to do this, as long as all your property names all match up (they are case-sensitive, so be careful).
If you're not using AJAX, but just a plain HTML form submit, it's even easier. Just name the input elements with that same dot notation. Razor syntax makes this really easy with helper methods like TextBoxFor(), but here's an example in plain HTML:
<form method="get" action="/ViewResult">
<input type="text" name="Name" />
<input type="text" name="Title" />
<input type="text" name="MyAddress.Line1" />
<input type="text" name="MyAddress.Line2" />
<button type="submit">Submit GET request</button>
</form>
Workaround 2:
The other way around this is to simply use a POST request instead of a GET. Beware that it's technically bad practice to perform a POST request without the intent of actually changing some data on the server side, but it is an option.
You can do it; it will automatically bind any values in the query string to properties with matching names.
That said, it's not something that's generally done; it's the [HttpPost] method where you see the model binding performed, as the interfaces for the two actions need to be different somehow. You can solve that by posting back to a different action name, but you may still trigger model validation errors on the (partial) load of the model, which would be really confusing to a user.
For Web API 2:
[HttpGet]
public ActionResult Get([FromUri]ViewModel model)
{
// Do stuff
return View("ViewName", model);
}
You can post a form to a get by setting the PostMethod attribute to get. If the form's input fields match any of the accepting ViewModel then they will be filled. These matches are determined by the name field in an input (<input name="MatchedField"> -> public string MatchedField { get; set; }).
What you should do is pass the form from a post, and then redirect to the get from the post action. This pattern is best practice and is known as the Post-Redirect-Get pattern.
I would advise against this approach. Best solution to just use POST, because if you use GET, once you click back from step 3 to step 2 and the browser cache is not available, you will perform actions on an old version of the ViewModel. Is there a particular reason why you want to use GET?
I can not suggest to use QueryString to pass values.
You can use one of below:
This code will render a partial view with the given model.Be sure you add model to your view. And your view should be placed in Shared folder
public ActionResult myaction(ViewModel model)
{
return PartialView("anotherView", model);
}
Another way to do almost the same thing:
public ActionResult myaction(ViewModel model)
{
return View("someAnotherView", model);
}
if your view is not in the same controller , use the path for view name like "../Controller/viewName"
There is also a different approach which can be done by using TempData:
public ActionResult myaction(ViewModel model)
{
TempData["model"] = model;
return RedirectToAction("someAnotherView");
}
but you should reach your data in the view with the code as shown below:
#{
ViewModel model=(ViewModel)TempData["model"];
}
Hope one of above helps..
Regards

Categories

Resources