Unable to locate element using ID, XPath and CSS Selector - c#

I am using atata framework with C#. I am trying to locate web element to select all rows but neither Id, CSS Path or XPath are able to find.
I have used ID, XPath and CSS Selector
[FindById("com.kronos.wfc.ngui.genies.selectall")]
public Button<_> SelectAllRows { get; private set; }
[FindByXPath("div[#id=com.kronos.wfc.ngui.genies.selectall]")]
public Button<_> SelectAllRows { get; private set; }
My page object should be located. Details of paths are:
Element:
<div class="widget-button btn-group margin-mini shrinkable" title="Select All Rows"
id="com.kronos.wfc.ngui.genies.selectall" style="display: inline-block;">
<div class="top-bar"><span></span></div>
<button type="button" class="btn btn-rounded widget-button-icon" id="com.kronos.wfc.ngui.genies.selectall_btn">
<i class="icon-k-select-all"></i></button><div class="icon-label"><span>Select All Rows</span></div></div>
Selector: #com.kronos.wfc.ngui.genies.selectall
XPath: //*[#id="com.kronos.wfc.ngui.genies.selectall"]

For the first <div> element:
<div class="widget-button btn-group margin-mini shrinkable" title="Select All Rows"
id="com.kronos.wfc.ngui.genies.selectall" style="display: inline-block;">
As it is a div, not a button element then use general puprose Control type:
[FindById("com.kronos.wfc.ngui.genies.selectall")]
public Control<_> SelectAllRows { get; private set; }
For the second <button> element:
<button type="button" class="btn btn-rounded widget-button-icon" id="com.kronos.wfc.ngui.genies.selectall_btn">
The following should find the element if it's actually visible:
[FindById("com.kronos.wfc.ngui.genies.selectall_btn")]
public Button<_> SelectAllRows { get; private set; }
If the element is not visible:
[FindById("com.kronos.wfc.ngui.genies.selectall_btn", Visibility = Visibility.Any)]
public Button<_> SelectAllRows { get; private set; }
Anyway, figure out which element is actually visible and should be interacted with.

I think you should perform the click action on the button element instead of the div element. Try the below code:
[FindById("com.kronos.wfc.ngui.genies.selectall_btn")]
public Button<_> SelectAllRows { get; private set; }

Related

How to load a non-routable component in Blazer

I work on a project that uses Blazer for the front-end and Cosmos DB in the back-end. I am looping through a list of items and have a button/ anchor tag that OnClick receives a parameter. Currently, I am using the query string parameters to redirect the user from one page to another page. However, I do not want to use query string. I want to make the component non-routable and load it on a button click. Is there a way to pass the parameter on button click and load a non-routable component?
Thank you very much for your explanation, I am using .net 5. Here I am going to write a sample code, so you could help.
My project uses Azure Cosmos DB and Blazor Server.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-html -->
///////////////////////////
MoviesListComponent.razor
////////////////////////
#page "/movies"
foreach(movie in ListOfMovies){
<div> movie.ReleaseeDate</div>
<div> movie.Country</div>
<div> movie.Title</div>
<button type="button" #onClick="()=>LoadAnotherComponent(movie.title)"> on button click redirect to(or load) a non-routable compnent and send movie.title as parameter for example MovieTitleAnalysis component</button>
}
#code{
public List<Movie> ListOfMovies {get;set;}
}
on button click inside MoviesListComponent load
MovieTitleAnalysis.razor and pass the parameter. Dispose MovieListComponent
///////////////////////
MovieTitleAnalysis.razor is a non routable component
//////////////////
<div>welcome to movie Title analysis</div>
#code{
[paramter]
public string movieTitle {get;set;}
void PreformSomeOpration(){}
}
As this is conceptual - you have provided no code context - this should provide a good starting point and demo the basic concept. Comment on how well/poorly it fits!
I've pointed to the "routed" components but it will work with any component.
DynamicPage.razor
#if (ComponentType != null)
{
<DynamicComponent Parameters=this.Parameters Type=this.ComponentType />
}
else
{
#this.ChildContent
}
#code {
[Parameter] public Type? ComponentType { get; set; }
[Parameter] public RenderFragment? ChildContent { get; set; }
[Parameter] public IDictionary<string, object>? Parameters { get; set; }
}
And Index.razor
#page "/"
#*Example Menu*#
<div class="p-2 m-2">
<button class="btn btn-primary" #onclick="() => this.ChangePage(null)">Change to Index</button>
<button class="btn btn-secondary" #onclick="() => this.ChangePage(typeof(Counter))">Change to Counter</button>
<button class="btn btn-info" #onclick="() => this.ChangePage(typeof(FetchData))">Change to FetchData</button>
</div>
<DynamicPage ComponentType=this.page>
<PageTitle>Index</PageTitle>
<div>Hello World from Blazor</div>
</DynamicPage>
#code {
private Type? page = null;
private void ChangePage(Type? newPage)
=> this.page = newPage;
}

ASP.NET Core creating custom input tag helper

I have a form that has multiple fields, that each can take multiple inputs (based on this question add multiple tags to form in net core 3.1 mvc)
Currently it looks like this
<input type="text" class="multiple-input form-control text-right" id="teams-input" container-name="teams-container">
<div class="row justify-content-end" input-name="teams-hidden">
<div id="teams-container" class="col-md-12 multiple-input-container"></div>
</div>
<input asp-for="Teams" type="hidden" id="teams-hidden" />
Problem is, I use similar things multiple times across multiple forms, so I want to make custom tag helper for it.
Currently this tag helper looks like this
[HtmlTargetElement("input", Attributes = "[type = multi-input]")]
public class MultipleInputsTagHelper : TagHelper
{
public ModelExpression For { get; set; }
public string Name { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.Content.SetHtmlContent(#$"<input type='text' class='multiple-input form-control text-right' id='teams-input' container-name='{Name}-container'>
<div class='row justify-content-end' input-name='{Name}-hidden'>
<div id='{Name}-container' class='col-md-12 multiple-input-container'></div>
</div>
<input asp-for='{For}' type='hidden' id='{Name}-hidden' />");
}
}
And used in form like this
<input multi-input asp-for="Teams" asp-name="teams"/>
But my tag helper is not applying for some reason. What could be the problem, and how to correctly make custom input tag helpers?
You don't specify the attribute names mapped with the corresponding properties, like this:
[HtmlTargetElement("input", Attributes = "multi-input")]
public class MultipleInputsTagHelper : TagHelper
{
[HtmlAttributeName("asp-for")]
public ModelExpression For { get; set; }
[HtmlAttributeName("asp-name")]
public string Name { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.Content.SetHtmlContent(#$"<input type='text' class='multiple-input form-control text-right' id='teams-input' container-name='{Name}-container'>
<div class='row justify-content-end' input-name='{Name}-hidden'>
<div id='{Name}-container' class='col-md-12 multiple-input-container'></div>
</div>
<input asp-for='{For}' type='hidden' id='{Name}-hidden' />");
}
}
We use the HtmlAttributeNameAttribute to specify the mapped attribute used in the original razor code.

Blazor set value inside other component

I'm building my page and I was wondering if I can make my life easier and put some simple custom Input boxes inside method and them pass reference to my values to them
<div class="col-12 row">
<label class="col-2">#Caption</label>
<InputNumber class="form-control col-3" #bind-Value="#Value"/>
</div>
<br />
#code {
[Parameter]
public string Caption { get; set; }
[Parameter]
public int Value { get; set; }
}
And then use it like
<CustomInputNumber Caption="Price" Value="#Product.Price" />
It is possible to set value like that? Or pass object as reference? Thanks for help!
The way I would go about this is inheriting from inputbase and basically building your own input. Chrissainty has an excellent blog post about, which I think is much clearer then me citing half of what he already explains in that post.
https://chrissainty.com/building-custom-input-components-for-blazor-using-inputbase/
If however you really want to wrap the already existing inputcomponent, you could do it like this:
The component
<div class="col-12 row">
<label class="col-2">#Caption</label>
<InputNumber class="form-control col-3" Value="Value" TValue="int" ValueChanged="HandleValueChanged" ValueExpression="(()=>Value)" />
</div>
<br />
#code{
[Parameter]
public string Caption { get; set; }
[Parameter]
public int Value { get; set; }
[Parameter]
public EventCallback<int> ValueChanged { get; set; }
public void HandleValueChanged(int newValue)
{
ValueChanged.InvokeAsync(newValue);
}
}
Usage like:
<ExampleComponent #bind-Value="ExampleValue"/>
Here you basically override the existing events that exist on a default inputfield. When the default component notices a change, you call your own valuechanged event to pass the event to it's parent.
Though again, I think the first solution is much cleaner.

Best approach in dealing with data inputs from dynamic sub forms in .NET MVC app

I'm relatively new to .NET MVC and have been struggling with an web app I'm working on. In sum, this app consists of a form, the main part which inputs the customer's name, and then there's a button to add a child. When this button is clicked, a sub form appears via a partial view. I'm not sure what to do with the data yet when the save button on the sub form is clicked yet (but the sub form will collapse, accordion style on the HTML side), but the gist is that a customer can add one or many child sub forms to input their child(ren)'s names and ages.
What is the best way to handle the inputs on these forms? I spoke w/ some coworkers and they seemed unsure. One suggested that I first save the 'parent' form to the data base and then within each sub-form (child), when the save button is click, send those onto the database as well, using the parent Id to connect them. Someone else suggested using Vue for this. What would YOU suggest in terms of model-binding or sending the data back to the controller?
First the view model for the form looks like this:
namespace ModelBindingObjects.ViewModel
{
public class ParentVM
{
public string Name { get; set; }
public List<ChildVM> Children { get; set; }
}
public class ChildVM
{
public string Name { get; set; }
public int Age { get; set; }
}
}
For the db side, the models look like this:
public partial class Parent
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
}
public partial class Child
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public int ParentId { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
public int Age { get; set; }
The view looks like this
#model ModelBindingObjects.ViewModel.ParentVM
<h3>A Sample Form</h3>
#using (Html.BeginForm())
{
<div class="container">
<div class="row">
<div class="form-row">
<div class="form-group col-md-6">
#Html.LabelFor(x => x.Name, new { #class = "required" })
#Html.TextBoxFor(x => x.Name, new { #class = "form-control" })
</div>
<div class="col-md-6">
</div>
</div>
</div>
<div class="row">
<div class="form-group col-md-6">
<div class="addChildren">
<h5 class="topspacing-med btmspacing-med">Your Child(ren) Details</h5>
<div class="displayChildForm">
#*#Html.Partial("_AddChild", new ViewDataDictionary()
{
TemplateInfo = new TemplateInfo()
{ HtmlFieldPrefix = "Test" }
})*#
</div>
<button type="button" class="btn btn-outline-secondary btn-sm btn-block btmspacing-med" id="addChild">Add New Child</button>
</div>
</div>
<div class="form-group col-md-6"></div>
</div>
</div>
}
#section scripts {
<script type="text/javascript">
var hideButtons = function () {
$("#addChild").hide();
};
$("#saveChild").click(function () {
console.log("testing");
$(".panel-body").hide();
});
$("#addChild").click(function () {
$.ajax({
url: '#Url.Action("RenderAddChildForm", "Home")',
dataType: "html",
type: 'GET',
success: function (result) {
$('.displayChildForm').append(result);
hideButtons();
},
error: function (xhr, ajaxOptions, thrownError) {
console.log(xhr.status);
console.log(thrownError);
},
});
});
</script>
Then the partial view ...
#model ModelBindingObjects.ViewModel.ChildVM
<div class="panel panel-primary">
<div class="panel-heading"><h5>Add A Child</h5></div>
<div class="panel-body">
<div class="form-group row">
<div class="col-md-12">
<input type="hidden" name="products.Index" value="test" />
#*#Html.LabelFor(x => x.Name)
#Html.TextBoxFor(x => x.Name, new { #class = "form-control" })*#
<input type="text" name="[test].Name" value="Delaney" />
</div>
</div>
<div class="form-group row">
<div class="col-md-12">
<input type="text" name="[test].Age" value="12" />
</div>
</div>
<div class="btn-group float-right mt-2" role="group">
<button class="btn btn-primary btn-sm btn-block" type="submit" id="saveChild">Save</button>
</div>
</div>
</div>
and at great last, the good old controller:
namespace ModelBindingObjects.Controllers
{
public class HomeController : Controller
{
private string PartialViewPath(string name)
{
return $"~/Views/Partials/{name}.cshtml";
}
// GET: Home
public ActionResult Index()
{
return View();
}
public ActionResult RenderAddChildForm()
{
return PartialView(PartialViewPath("_AddChild"));
}
}
}
You can just model bind to an ICollection property on the model
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-2.2#collections
an oldie but a goldie: https://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
You might find some libraries of mine helpful for this kind of thing also.
https://github.com/mcintyre321/FormFactory
https://github.com/mcintyre321/AspNetCoreJTokenModelBinder
It depends on the complexity of the page flow, how familiar you're with Vue, and if there is a separate edit page or you want to support edit/delete within the same page. If all it does is save children, then you can easily do that with AJAX requests without introducing Vue.
One approach is to separate the two operations, so the user can't add a child unless they have added and saved the parent's data. On parent form save success, return the saved parentId, and disable the button. For each subform, when the user clicks "save child", send {parentId, childVm}.
Another approach is to have only one save button for the entire page i.e. each child doesn't get their own save button. The displayChildForm div acts as a collection of inputs with a single "add child" button that creates more children partial views and appends them to itself. When the user clicks the page's save button, you send {parentVm, childVmCollection[]}.
You can mix both approaches, so you end up with a "save parent" button, and a single "save/edit children" button within the displayChildForm div. On clicking save children button, you send {parentId, childVmCollection[]}.

Atata – How to check hidden checkbox within table?

I am using Atata Framework and working on the following scenario—There's a table with checkbox within TD element. I want to be able to invoke Click() method on the checkbox, but couldn't get it work correctly.
The truncated HTML is as following:
<table data-v-c4547572="" class="invGrid">
<tr data-v-c4547572="" row-id="3ed5bcf4-473d-43ae-991a-ffe36d5e0a53" class="row-index-0">
<td data-v-c4547572="" class="column-index-0 checkbox-col">
<input data-v-c4547572="" type="checkbox" element-id="3ed5bcf4-473d-43ae-991a-ffe36d5e0a53" class="">
<label data-v-c4547572="" for="3ed5bcf4-473d-43ae-991a-ffe36d5e0a53"></label>
</td>
<td data-v-c4547572="" class="column-index-1">
<span data-v-c4547572="" class="val-name">Some text</span>
<span data-v-c4547572="" class="arrow pull-right dsc"></span>
</td>
</tr>
</tbody>
</table>
The code I'm using is:
// The page class:
[FindByCss(".invGrid")]
public Table<GroupsRow, Page> Inventory { get; set; }
// The row class:
public class GroupsRow : TableRow<Page>
{
[FindByIndex(0)]
public CheckBox<Page> CheckBox { get; set; }
[FindByCss(".val-name")]
public Text<Page> Text { get; set; }
}
As an additional note, invoking Exists() on the checkbox yields false:
inv.CheckBox.Exists(); // false
Any idea how to make the checkbox to be operational?
I can guess that your check box is actually hidden, and <label> is used as a wrapper for custom rendering. As almost all controls in Atata are looking for visible elements by default, you can specify Visibility:
[FindByIndex(0, Visibility = Visibility.Any)]
public CheckBox<Page> CheckBox { get; private set; }
It should find the check box. But if click on it will not work (as it can be hidden), you can add a property for label and click it:
[FindFirst]
public Label<Page> CheckBoxLabel { get; private set; }

Categories

Resources