Use user ID on a layout in ASP.NET Core - asp.net-core

I'm working on a website, and once the user logs in, he is able to check some workouts.
However, I'm trying to pass the user ID from the layout to other pages, as following
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<li>
<a class="page-scroll">Profile</a>
</li>
<li>
#Html.ActionLink("Workouts", "Index", "Routines", new {id = Model.UserID })
</li>
<li>
<a class="page-scroll">Evaluation</a>
</li>
<li>
<a class="page-scroll">Nutrition</a>
</li>
<li>
<a class="page-scroll">Services</a>
</li>
<li>
<a class="page-scroll">Membership</a>
</li>
</ul>
</div>
And it is not working, I get the following error:
'Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable'
does not contain a definition for 'UserID'
However, my Model does have a UserID property. I can see it working properly on SQL and even if I takeout the HTML.ActionLink from there it works, but when I try to pass that value from the layout it doesn't work.
Any suggestions on how to solve this? I've been reading here and there but no solution so far.
I may be approaching the problem the wrong way. Is there a way to properly pass the ID of the logged in user to multiple Controllers in order to access the information of that user?

So rather than pass the userID around, how about making a service to get the logged in user and then grab whatever data you need from your database? That way you can inject the service wherever you need it.
public interface ILoggedInUserService
{
LoggedInUser GetLoggedInUser();
}
public class LoggedInUserService : ILoggedInUserService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public LoggedInUserService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public LoggedInUser GetLoggedInUser()
{
string username = GetLoggedInUsername(_httpContextAccessor.HttpContext.User);
if (!string.IsNullOrEmpty(username))
{
return YourServiceToGetUserData.GetUserData(username);
}
return null;
}
private static string GetLoggedInUsername(ClaimsPrincipal principal)
{
return principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
}
}
And then in Startup.cs where you set up your services for dependency injection add this:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

Related

Razor Pages Tag Helper with Dynamic Parameters Assigning Current Url as Href

I'm running into trouble trying to set anchor tag helper parameters dynamically and looking for some help.
I have a nav that is a view component inside the shared _Layout.cshtml page that populates departments from a model.
#model List<DepartmentModel>
<ul class="nav">
#foreach (var d in Model)
{
<li>
<a asp-page="catalog/departments" asp-route-departmentName="#d.Name" asp-route-departmentId="#d.Id">#d.Name</a>
</li>
}
</ul>
Here is the InvokeAsync() from my View Component class
public async Task<IViewComponentResult> InvokeAsync()
{
var departments = _catalogService.GetNavDepartments();
return View(departments);
}
When I first launch the page, all the hrefs are populating correctly.
"catalog/departments/department-name-1/department-id-1"
"catalog/departments/department-name-2/department-id-2"
"catalog/departments/department-name-3/department-id-3"
If I click on one of the links, like the first link for example, I go to the proper department page "catalog/departments/department-name-1/department-id-1"
However, once I click that first link and navigate to the respective page, all the nav hrefs populate to the current url "catalog/departments/department-name-1/department-id-1" instead of the originally generated hrefs. This makes it so I can't navigate to another department.
Here is my route in the Startup.cs
services.AddRazorPages().AddRazorPagesOptions(options => {
options.Conventions.AddPageRoute("/Catalog/Departments", "{dName}/{dId}");
});
Based on the convention above, it eliminates the "catalog/department" piece of the url but I added it in this description for a sense of what I'm trying to accomplish. Even if I add this template to the page that populates the "catalog/departments" url, I get the same result.
#page "{dName}/{dId}"
Can anyone help me figure out what I am missing? Thanks in advance!
******** UPDATE ********
Currently, the only way I am able to get this to work is by adding the cache tag helper.
<cache>
<ul class="nav">
#foreach (var d in Model)
{
<li>
<a asp-page="catalog/departments" asp-route-departmentName="#d.Name" asp-route-departmentId="#d.Id">#d.Name</a>
</li>
}
</ul>
</cache>
This doesn't seem like a proper fix. Seems more of a hack then anything. Anybody have any ideas? Thanks!

Selenium Webdriver C# Unable to locate an element on the "active" dropdown class list

I am on the beginner level if it goes for the selenium Webdriver in C#. I can locate elements, write simple scripts in order to input some value, withdraw it, compare and etc. I can also get values from the dropdown list by SelectElement class and so far I have not had any issues.
Recently one of our systems was refactored to React JS and most of my automation tests have stopped working.
Right now I have been struggling with a simple logout operation. I will please my issue below, and I would be grateful for any tips or suggestions.
The hard thing is that I can not locate the logout link which is located in the dropdown list, however, the code for it looks as below. Before you click on the link which acts as an action for the drop-down list it looks like this:
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<span class="glyphicon glyphicon-user" aria-hidden="true"></span>
UserName UserLast
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>Logout</li>
</ul>
<div id="user-login" class="hidden">UserName.UserLast</div>
<div id="user-email" class="hidden">UserName.UserLast#companyname.com</div>
</li>
When a User clicks on the the code changes to this one below:
<li class="dropdown open">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="true">
<span class="glyphicon glyphicon-user" aria-hidden="true"></span>
UserName UserLast
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>Logout</li>
</ul>
<div id="user-login" class="hidden">UserName.UserLast</div>
<div id="user-email" class="hidden">UserName.UserLast#companyname.com</div>
</li>
My test is quite simple, locate the Logout "button/link", click on it and perform the logout operation. Now I have started of location the action link which looks like this:
[FindsBy(How = How.XPath, Using = "/html/body/div[1]/div/div[2]/ul[2]/li[2]/a")]
private IWebElement userDropdown;
And my logout method looks like that's,
public void LogOut()
{
userDropdown.Click();
var userDropDownList = Browser.WebDriver.FindElement(By.XPath("/html/body/div[1]/div/div[2]/ul[2]/li[2]/ul"));
var logoutButton = userDropDownList.FindElement(By.LinkText("Logout"));
logoutButton.Click();
}
I perform the click operation on the link, look for the list with the Logout, use the click operation. Still my test does not work and I am not getting any errors at all but the logout operation is not done. I think that the issue here is not the drop-down list is not visible by selenium. I have not tried the SelectElement class because this example is not a select element or maybe I am wrong...
You will need to perform click() on 2 elements to logout:
Identify the dropdown:
[FindsBy(How = How.XPath, Using = "//li[#class='dropdown']/a[#class='dropdown-toggle']/span[#class='glyphicon glyphicon-user']")]
private IWebElement userDropdown;
logOut method:
public void logOut()
{
userDropdown.Click();
var logoutButton = Browser.WebDriver.FindElement(By.XPath("//li[#class='dropdown']//ul[#class='dropdown-menu']/li/a[#href='/Account/Logout']"));
logoutButton.Click();
}
so first things first make sure you've scrolled the item to view
var js = (IJavascriptExecutor)driver;
string idOfLinkToMakeDropDownOpen = "whatever the id is"
IWebElement obj = driver.FindElement(By.XPath("//input[#id='idOfLinkToMakeDropDownOpen']"));
js.ExecuteScript("arguments[0].scrollIntoView(true);", obj);
obj.Click();
Now the drop down should be open so click it
driver.FindElement(By.Xpath("descendant::a[text() = 'Logout']")).Click();
That should click it. You'll probably want to watch it to see if opens the drop down. You also may need to wait a second after the dom to change after having opened the drop down. That would work something like this:
IWebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(15));
after obj.Click(); you do this line:
wait.Until(ExpectedConditions.ElementIsVisible(By.Xpath("descendant::li[#class = 'dropdown open']"));

WebDriver: find element by CSSSelector or XPath

I have a problem to get element <i class="angle-up"></i> on the following page. The selection should be depended on "data-id" value. Thanks in advance.
<li class="recent" data-to="3" data-off="23" data-id="5">
<a class="collapse in" href="/board">
<span>
Board
</span>
<i class="angle-up"></i>
</a>
</li>
<li class="" data-to="3" data-off="23" data-id="7">
<a class="collapse in" href="/set">
<span>
Set
</span>
<i class="angle-up"></i>
</a>
</li>
Well I think if the value is data-id dependant then you might need to split the Xpath or Css selector in 3 parts as below:
Part1=//li[#data_id='
Part2= (Fetch this value at runtime using an appropriate variable)
Part3= ']//i[#class='angle-up']
and now you may use the following code to use the element
public class TestElement {
public static void main(String args[])
{
WebDriver driver= new FirefoxDriver();
driver.get(YOUR INTERNAL URL HERE.);
String elementPart1=//li[#data_id='
String elementPart2=5 //keeping the id you are looking for
String elementPart3=']//i[#class='angle-up']
WebElement element = driver.findElement(By.xpath(elementPart1+elementPart2+elementPart3);
}
}
Here as you may see one example of such xpath could be
//li[#data_id='5']//i[#class='angle-up']
Also if I would advise you to check using firefinder which is a firebug extension to verify that the xpath is correct.

Dynamically Set list item Id

This seems like it should be really easy, but I cannot get this to work.
I have a collection of strings in my view model that's being outputted as list items :
<ul class="dropdown-menu" id="genreDropDown">
#foreach (string subject in Model.SuggestSubjects)
{
<li id="listItem'#subject'"><a onclick="LoadSuggestTitles_BySubject('#subject');">#subject</a></li>
}
</ul>
When the page renders I'm seeing the id of the list items are being formatted as :
<li id="listItem'Mystery'">
My question :
Is there any way I can format the ID without the single quotes, so Razor can still pick up the value and append it to id?
Ultimately I'd like for it to read <li id="listItemMystery">.
Replace:
<li id="listItem'#subject'">
With:
<li id="listItem#(subject)">
Try this:
#foreach (string subject in Model.SuggestSubjects)
{
var id = "listItem" + #subject;
<li id="#id"><a onclick="LoadSuggestTitles_BySubject('#subject');">#subject</a></li>
}

Formatting a list item in jQuery Mobile for a POST link

I have a question about jQuery Mobile formatting, and how to get an <li> to format properly in a listview. The MVC 4 default template for a Mobile application in Visual Studio has a logoff link that uses a GET call to logoff. Here is the markup:
<ul data-role="listview" data-inset="true">
<li>#Html.ActionLink("Change password", "ChangePassword")</li>
<li>#Html.ActionLink("Log off", "LogOff")</li>
</ul>
And here is the call:
// GET: /Account/LogOff
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
With jQuery Mobile, it looks like this:
In contrast, the MVC 4 default template for an internet application uses a POST method for the same logoff:
#using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id =
"logoutForm" })) {#Html.AntiForgeryToken()
<a ref="javascript:document.getElementById('logoutForm').submit()">Log off</a>}
What I would like to do is to use a POST method in my Mobile application to log off instead of the default GET method (in keeping with best practice: Logout: GET or POST?). I can get the link to work properly, but what I am having trouble with is getting the button to look the way it did when it was using the GET method.
My markup looks like this:
<ul data-role="listview" data-inset="true">
<li>#Html.ActionLink("Change password", "ChangePassword")</li>
<li>#using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm" }))
{#Html.AntiForgeryToken()
Log off}
</li>
</ul>
But the result looks like this:
I’ve tried putting the <li> tags around the <a> tag instead, but the results are not any better:
Could someone explain why this <li> tag is being formatted differently than the one above it, and how I might get it to look like the one at the top of this post?
Unless you feel like creating your own custom CSS in that list item don't put the actual <form> inside of it because forms have their own default CSS. Instead keep the form somewhere outside the list item and put the button itself inside the list item
<ul data-role="listview" data-inset="true">
<li>#Html.ActionLink("Change password", "ChangePassword")</li>
<li>Log off</li>
</ul>
#using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm" }))
{
#Html.AntiForgeryToken()
}