Switch context to new page in Playwright (c#) - playwright

I want to create a method to Click on given element and open it in new tab. I manage to open new tab on click:
public async Task ClickMenuItemNewTab(string menuItem, string section, string header)
{
var context = BrowserSession.Browser.Contexts[0];
var newPage = await context.RunAndWaitForPageAsync(async () =>
{
await _homePage.ClickMenuItem(menuItem, section, header, new() { Button = MouseButton.Middle });
});
await newPage.WaitForLoadStateAsync();
}
but I don't know how can I switch to page in new tab. Do I have to create new browser context for it. Or is it way to switch to the page in same context.
Thank you for any help.

await Page.BringToFrontAsync();
https://playwright.dev/dotnet/docs/api/class-page#page-bring-to-front

Related

.Net 6 Razor View Not Updating after Post

I am passing a complex model to a Razor View. The data in the model is used in a for-each loop in the view to display a list of products that are for sale in bootstrap cards. There are no forms on the view.
On the left side of the page are list elements that contain nested list elements. These represent product categories. When the user selects a category, I pass the product category to an action method in the controller as an integer. This will build a new model based on the selected category. The new model is then passed back to the view.
At this point, I want to display the categories that are in the category that the user selected. But, the view is not updating even though the model is now different.
I have been reading about this issue, and one forum post I read suggested that this is by design. I read somewhere that in order to achieve what I need to achieve, I have to clear the model state. But I cannot clear the model state because I am not passing the model back to the controller, and you can only pass the model back to the controller by reconstructing the model to be passed in an ajax call for example. I spent all day yesterday trying to get ajax to work and every time I executed the post, the model was populated in the JavaScript function and null or empty in the controller.
Is there another way to force the view to update upon sending a new model to the view? Does anyone have any suggestions?
<script type="text/javascript">
function AddSubCategory(elem) {
event.stopPropagation();
var e = document.querySelectorAll(".products");
e.forEach(box => {
box.remove();
});
var categoryId= $(elem).data('sub-id');
console.log(`Id: ${categoryId}`);
var request;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
}
if (request != null) {
var url = `/Products/GetProductsByCategory?categoryId=${categoryId}`;
request.open("POST", url, false);
};
//request.onreadystatechange = function () {
// if (request.readyState == 4 && request.status == 200) {
// }
//};
request.send();
// model = JSON.stringify(model);
// console.log(model);
// $.ajax({
// type: "Post",
// url: '#Url.Action("GetProductsByCategory", "Products")',
// data: JSON.stringify({"categoryId": categoryId}),
// contentType: 'charset=utf-8;application/json',
// cache: false,
// complete: function (data) {
// }
//});
}
// Load all products
public async Task<IActionResult> Index()
{
ReadCartIDFromCookie();
string SubDomain = GetSubDomain(HttpContext);
var allProducts = await _productService.GetAllProductsWithImagesAsync(SubDomain);
var productCategoryLookup = await _productService.GetAllProductCategoryLookupAsync();
ViewBag.host = SubDomain;
ViewBag.productCategoryLookup = productCategoryLookup;
return View("Index", allProducts);
}
//Load filtered list of products
[HttpPost]
public async Task<IActionResult> GetProductsByCategory(int categoryId = -1)
{
ReadCartIDFromCookie();
string SubDomain = GetSubDomain(HttpContext);
var allProducts = await _productService.GetAllProductsWithImagesAsync(SubDomain, categoryId);
var productCategoryLookup = await _productService.GetAllProductCategoryLookupAsync();
ViewBag.host = SubDomain;
ViewBag.productCategoryLookup = productCategoryLookup;
return View("Index", allProducts);
}
I was able to resolve my issue thanks to this post: https://stackoverflow.com/a/66277911/1561777
I did have a bit of trouble trying to figure it out because it was not completely clear, but I could tell it was what I needed to do. I ended up utilizing a partial view for the display of my products. Here is my final code.
//My index.cshtml razor view
<div class="col" id="product"></div>//partial view will be loaded onto this div
//Javascript function that gets the chosen filter category Id
#section Scripts
{
<script type="text/javascript">
function AddSubCategory(elem) {
event.stopPropagation();
//get the id
var categoryId= $(elem).data('sub-id');
console.log(`Id: ${categoryId}`);
//call controller Products, controller action GetProductsByCategory and pass the categoryId
//The partial view will be returned and load onto the div id #product
$('#product').load(`/Products/GetProductsByCategory?categoryId=${categoryId}`);
}
//when the index view first loads, load all products (i.e. category -1)
$('#product').load(`/Products/GetProductsByCategory`);
</script>
}
public async Task<IActionResult> GetProductsByCategory(int categoryId = -1)
{
ReadCartIDFromCookie();
string SubDomain = GetSubDomain(HttpContext);
var allProducts = await _productService.GetAllProductsWithImagesAsync(SubDomain, categoryId);
var productCategoryLookup = await _productService.GetAllProductCategoryLookupAsync();
ViewBag.host = SubDomain;
ViewBag.productCategoryLookup = productCategoryLookup;
//notice that I am using PartialView method
//Returning View was including the full website (i.e. header and footer).
//_Products is my partial view. allProducts is the
//view model that is being passed to the partial view.
return PartialView("_Products", allProducts);
}
Now, everytime I select a new category, the partial view is reloaded. Also, when first loading the view, all products are displayed as expected.

Update list Aspnet core 2.2

I would like to update a list of items most do not know how to do, when my only method adciona new items to existing inves change.
save change
public async Task<CommandResult> ExecuteAsync(EventsCommandsHandler handler)
{
var listItens = await handler.DbContext.ListItens
.Where(f => f.Id == this.Id)
.SingleOrDefaultAsync();
listItens.SetData(
name: Name,
description: Description
);
handler.DbContext.ListItens.Update(listItens);
if (Items.Length > 0)
{
var itens = Items
.Select(p => p.ToItemList(listItens.Id))
.Where(p => p != null)
.ToArray();
await handler.DbContext.ItemsList.(itens);
}
var rows = await handler.DbContext.SaveChangesAsync();
return await Task.FromResult(new CommandResult(rows));
}
create intens
public static ItemList ToItemList(this ItemCommand command, string listId)
{
if (string.IsNullOrWhiteSpace(command.Description))
return null;
var itens = new ItemList(
id: String.IsNullOrWhiteSpace(command.Id) ? RandomId.NewId(8) : command.Id,
description: command.Description,
listId: listId
);
return itens;
}
I need a way for this method.
I have a list and this list has items, when I edit the list, I want to be able to delete, edit or add more items.
You have a To-Do list. There should be an add button.
A post request should be sent and data should be written to the database, and page - updated when you press any button like remove, edit and add.
If you want it to be async, then you should use JavaScript or Blazor.

How to show MVC logged in username with AngularJs

I am using MVC 5 / WebApi 2 and AngularJs. I want to display the Logged in username in my view. I know how to display that information using razor but how can I do it with Angular? So basically I need to do this with Angular.
<span >Logged In As: #Html.ActionLink(User.Identity.GetUserName(), "Manage", "Account", routeValues: null, htmlAttributes: new { title = "Manage", #style = "color:white;float:right" })</span>
apiUserController
public class apiUserController : ApiController
{
// GET api/<controller>
public List<ApplicationUser> Get()
{
using (var context = new ApplicationDbContext())
{
List<ApplicationUser> users = new List<ApplicationUser>();
users = context.ApplicationUsers
.ToList();
return users;
}
}
}
Updated
public IHttpActionResult Get()
{
using (var context = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
var user = context.FindById(User.Identity.GetUserId());
var loggedInUser = user.UserName;
return Ok(loggedInUser);
}
}
you'll need to create a service that returns your user information
angular.module('app').factory('Authentication', function ($resource) {
var resource = $resource('/user', {}, {
query: {
method: 'GET',
cache: true
}
});
return resource.get().$promise;
});
* note that you'll need to create and endpoint that will send you the user data as json using web api
once you got it done you'll be able to use it in any controller (let's assume you have a homecontroller, it could be a headercontroller or any other)
angular.module('app').controller('HomeController', ['$scope', 'Authentication', function ($scope, Authentication) {
$scope.authentication = Authentication;
}]);
then use it in your view like:
<span >Logged In As: {{authentication.user.username}} </span>
EDIT:
your api controller as you suggested could be like
public HttpResponseMessage Get()
{
var userId = getCurrentUserId(); //something like that
using (var context = new ApplicationDbContext())
{
ApplicationUser user = new ApplicationUser();
user = context.ApplicationUsers.SingleOrDefault(x=>x.id==userId);
return user;
}
}
try to read http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization
for routing try to read this article (I guess you are using web api 2)
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
If you want to cheat a little, you can do this in <head> in your _Layout:
<script type="text/javascript">
(function(myApp) {
myApp.username = "#User.Identity.GetUserName()";
//optional
myApp.otherStuff = "#moreMvcStuff";
})(window.myApp = window.myApp || {});
</script>
Then start your angular app like this:
(function (myApp) {
"use strict";
//var app = set up your angular app
app.run(["$rootScope",
function ($rootScope) {
$rootScope.appSettings = {
username: myApp.username
};
}
]);
})(window.myApp = window.myApp || {});
What you are doing is creating a single value on the window called myApp (or name it whatever you like) and passing it into your IIFE. This gives you access to it inside your angular script, bot only in that on block. So if you want it to stick around, you need to put it in a service or your rootScope.
In the app.run block, you can stick it in your rootScope or wherever you want it.
Now in your views you can display it with {{appSettings.username}}.
I call this "cheating" because it's specifically for MVC or webforms and it's not the "angular way". If you ever migrated to a fully agnostic html/js client (no asp.net mvc) and web APIs, you'd need to do what is in the currently-accepted answer.

How to Prevent Page refresh on select change for dropdownlist in MVC

I have a dropdownlist in my razor view MVC like
#Html.DropDownListFor(n => n.EMP_ID, (SelectList)ViewBag.EmployeeList, new { #id = "ddlemployee" },"---choose an Employee Name--").
I have applied select change event to drop-down using jquery, when select Employee name getting Employee names and realted data, but problem is when i select a value in drop-down, dropdownlist setting again set to default first value,
It is not sticking to particular selected value, in terms of Asp.net terminology, how to prevent postback to dropdownlist?
//Redirected to Controller
<script>
$(document).ready(function () {
$("#ddlemployee").change(function () {
location.href ='#Url.Action("GetEmployeeDetails", "Employer")'
});
});
</script>
//Action Method in Employer Controller
public ActionResult GetEmployeeDetails(Timesheetmodel model)
{
try
{
ViewBag.EmployeeList = objts.getEmployeeNames();
var emps = from n in db.TIMESHEETs
where n.RES_ID == model.EMP_ID
select n;
int count = emps.Count();
foreach (TIMESHEET ts in emps)
{
model.PROJ_ID = ts.PROJ_ID;
model.SUN_HRS = ts.SUN_HRS;
model.MON_HRS = ts.MON_HRS;
model.TUE_HRS = ts.TUE_HRS;
model.WED_HRS = ts.WED_HRS;
model.THU_HRS = ts.THU_HRS;
model.FRI_HRS = ts.FRI_HRS;
model.SAT_HRS = ts.SAT_HRS;
}
}
catch (Exception ex)
{
throw ex;
}
return View("Timesheet", model);
}
ASP.Net Webforms achieve StateFullness by using Some thing called ViewState
It is implemented as hidden fields in the page to hold data between requests.
This way , asp.net webforms achieves post back mechanism and was able to hold values in bewteen the requests.
Since Http is a stateless protocol , which means it has no relation between requests.
View State is absent in ASP.Net MVC.
So, you have to stop postback by partially posting back . Which means that you need to send an asynchronous request with out refreshing whole page.
It is possible by using AJAX. You can either use
MVC Ajax or Jquery Ajax.
By using AJax, we can eliminate the post back and then do the partial post back.
Solution:
$("#dropdownid").change(function(event e)
{
//Make your ajax request here..
});
Hope this helps
Updated:
$("#dropdownid").change(function () {
$.ajax({
type: "post",
contentType: "application/json;charset=utf-8",
url: /*Your URL*/,
success: function (data) {
//do your callback operation
}
});
});
Got it.
Passing Empid as querystring from jquery like:
<script>
$(document).ready(function () {
$("#ddlemployee").change(function () {
debugger;
var empid = $("#ddlemployee").val();
location.href = '#Url.Action("GetEmployeeDetails", "Employer")?empid=' + empid ;
});
});
</script>
and assign "empid " to model "Empid" in Action method before foreach loop like
model.EMP_ID = empid;//in Controller Action Method before foreachloop of my code
// and this model.EMP_ID binded to dropdownlist.
this EMP_ID passes same id to dropdownlist which was selected. Thats it.

ASP.NET MVC two user control

I have two user controls on the page and one of the user control has this text aread.
which is used to add a note and but when they click add note button the page reloads.
I do not want the page to reload ,i was looking for an example which this is done without
postback.
Thanks
i tired doing this using JSON , but it throws the following error
The HTTP verb POST used to access path '/Documents/TestNote/Documents/AddNote' is not allowed.
<script type="text/javascript">
$(document).ready(function() {
$("#btnAddNote").click(function() {
alert("knock knock");
var gnote = getNotes();
//var notes = $("#txtNote").val();
if (gnote == null) {
alert("Note is null");
return;
}
$.post("Documents/AddNote", gnote, function(data) {
var msg = data.Msg;
$("#resultMsg").html(msg);
});
});
});
function getNotes() {
alert("I am in getNotes function");
var notes = $("#txtNote").val();
if (notes == "")
alert("notes is empty");
return (notes == "") ? null : { Note: notes };
}
</script>
</asp:Content>
Something like this would give you the ability to send data to an action do some logic and return a Json result then you can update your View accordingly.
Javascript Function
function AddNote(){
var d = new Date(); // IE hack to prevent caching
$.getJSON('/MyController/MyAction', { data: "hello world", Date: d.getTime() }, function(data) {
alert(data);
// call back update your view here
});
}
MyController Action
public virtual JsonResult MyAction(string data)
{
// do stuff with your data here, which right now my data equals hello world for this example
return Json("ReturnSomeObject");
}
What you want is AJAX update. There will always be a postback (unless you are satisfied with simple Javascript page update that does not save on the server), but it won't be the flashing screen effect any more.

Resources