Angular 5 : how to dynamically bind events on button from a list - angular5

<div id="home" class="tab-pane fade in" style="text-align:left;opacity: 1;" >
<button *ngFor="let tool of toolArray" class="m-btn btn btn-secondary" type="button" (click)="{{tool.ToolMethod}}()" placement="bottom"
ngbTooltip="{{tool.Tooltip}}">
<img src={{tool.ToolImgPath}} alt="" width="24" height="24"/>
</button>
</div>
Lets say , I have some tools managed by admin , if the user logs in , he can use that tools for editing .
let toolArray = [
{ToolCategory: "analysis"
ToolId: 96
ToolImgPath: "images/zoom-selection.png"
ToolMethod: "zoomToClickedFeature"
ToolName: "an_zoomto_selected"
Tooltip: "Zoom To Selected Feature"}
]
if I add this in the html using ngFor , its getting error like
Got interpolation ({{}}) where expression was expected

If the method is a member of the controller, you should use this like: (click)="this[tool.ToolMethod]()".
Here's a working example:
https://stackblitz.com/edit/angular-q2l54d?file=src%2Fapp%2Fapp.component.html

Remove the curly brackets from (click)="{{tool.ToolMethod}}()" to (click)="tool.ToolMethod()" and refactor your code to something like this, to get the reference to the corresponding method right:
public zoomToClickedFeature() {
console.log("do something");
}
public toolArray = [
{
ToolCategory: "analysis",
ToolId: 96,
ToolImgPath: "images/zoom-selection.png",
ToolMethod: this.zoomToClickedFeature,
ToolName: "an_zoomto_selected",
Tooltip: "Zoom To Selected Feature"
}
];
Array and function must be class properties of the component.

Related

Not able to click on a button, when isExisting is true; isDisplayedInViewport true, but waitForDisplayed timed out for the element in WebdriverIO

The script fails with this error.
Error: element (".WONDERESC") still not displayed after 30000ms
Tried different combinations for Xpath, (relative, fixed, text()} and CSS selectors, but the button not clicked. The sign-in button div in the code block:
<div>
<div class="WONDERBSC" role="form">
<div>
<div class="WONDERJ1B" data-automation-id="userName">
<div class="TOM-Label WONDERP1B"
title="Username">Username</div>
<input type="text"
class="TOM-TextBox WONDERM1B" aria-label="Username">
<button type="button" class="TOM-Button WONDERN1B"/>
</div>
</div>
<div>
<div class="WONDERJ1B"
data-automation-id="password">
<div class="TOM-Label WONDERP1B" title="Password">Password</div>
<input type="password" class="TOM-PasswordTextBox WONDERM1B" aria-label="Password">
<button type="button" class="TOM-Button WONDERN1B"/>
</div>
</div>
<button type="button"
class="WONDERESC"
data-automation-id="goButton">Sign In</button>
</div>
</div>
Kindly suggest the workarounds - the other conditions are also meeting- visibility-true, display-block, opacity not zero.
Thanks,
Tan
If element is not displayed then you have to make it visible
Case 1
The element takes some time to be visible automatically
In this case, you can use webdriver wait
WebDriverWait wait = new WebDriverWait(driver,Duration.ofSeconds(10));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("xxxx"));
Case 2
Element is visible after some interaction like mouse movement or click or type
Then you have to perform that action with selenium. For mouse movements you can use action classes
As it was told in the comments, you are using invalid selectors.
Assuming you have the latest WebdriverIO v6, the following works like a charm
// open page
browser.url('https://impl.workday.com/wday/authgwy/accenture_dpt2/login.htmld?redirect=n')
const form = $('[data-automation-id="authPanel"] [role="form"]')
// make sure form is visible
expect(form).toBeVisible()
// define form data
const formData = [{
id: 'userName', value: 'test'
}, {
id: 'password', value: 'password'
}]
// fill form
formData.forEach(f => {
form.$(`[data-automation-id="${f.id}"] input`).setValue(f.value)
})
// submit form
form.$('button[data-automation-id="goButton"]').click()
// make sure error message exist
const errorMessage = $('[data-automation-id="alertMessage"]')
expect(errorMessage).toBeVisible()
expect(errorMessage).toHaveTextContaining('Invalid user name or password')

vue.js basic function not working as wanted

I have create a set of pairs of divs, that use the v-for method to get data from a set of dummy objects in an array. The goal is for each pair of divs when I click on the visible div it opens the corresponding relevant div. At the moment my function which I have attached as a property of a method object only opens the invisible div of the first pair of divs even if I click on the 3rd visible div it still displays the invisible div. i am using the vue framework.
I have attached pictures of my code then the actual code.
[The div I am trying to open is session-on-click details atm it is opening only that for the first index][1]
<div class = "rowuserlog" id="log-container" v-for="session in sessions" :key="session.id"
>
<div id="log-container-user-row-1">
<div id="profile-log-title" #click="logToggler()"> {{session.name}} </div>
<div id="profile-log-date"> {{session.date}}</div>
<div id="user-log-metric-container">
<div id ="user-log-hr" class="log-metric">{{session.hr}}</div>
<div id ="user-log-time" class="log-metric"> {{session.time}}</div>
<div id ="user-log-meters" class="log-metric"> {{session.distance}} </div>
<div id="session-onclick-details">
<div id="log-comments">
{{session.comments}}
</div>
<div id="log-edit-buttons">
<button id="log-save" class="log-edit-button"> Save </button>
<button id="log-delete" class="log-edit-button"> Delete </button>
<button id="log-cancel" class="log-edit-button"> Cancel </button>
<button id="log-edit" class="log-edit-button"> Edit </button>
</div>
</div>
```
My method
The toggler is the method I want:Here is the code
methods:{
logToggler () {
const extraInfoLog = document.getElementById("session-onclick-details");
return extraInfoLog.style.display="block";
}
]
[2]
[1]: https://i.stack.imgur.com/ddrYo.png
[2]: https://i.stack.imgur.com/evriF.png
data() {
return {
sessions:[
{ name:'test',
value:false,
id:1
},
{ name:'test1',
value:false,
id:2
},
{ name:'test2',
value:false,
id:3
}
]
}
}
methods:{
logToggler(_index) {
for(let index=0;index<sessions.length;index++) {
if(index == _index)
sessions[index].value = true
else
sessions[index].value = false
}
}
}
Template
<div class = "rowuserlog" id="log-container" v-for="(session,index) in sessions" :key="session.id"
>
<div id="profile-log-title" #click="logToggler(index)" v-if="session.value"> {{session.name}} </div>

How to pass a dynamicy changed model to Partial view?

I have a list of "workbooks" displayed in a table. Each workbook has a "Share" button next to the workbook's title. When the user clicks on the share button a modal dialog is shown containing a form.
The form allows the user to enter a list of the recipient's emails separated by a comma which is validated on the client-side.
As the dialog is located in a partial view _ShareView.cshtml that allows me to pass a modal WorkbookShareModel that has some fields like WorkbookId and Title. The goal here is to pass the details of each workbook when the user presses the share button (i.e. construct a modal and pass it to the already rendered model).
I am not sure how to pass a model to an already rendered view?
The solution have to be done on the client (i.e. dont involve actions on the server that return the partial view provided the parameters are passed). I want to avoid unnesessary calls to the server - we have all the data on the client regarding a workbook and I need to do a POST when the user types in list of emails.
This is my index.cshtml:
#section BodyFill
{
<div id="shareFormContainer">
#{ await Html.RenderPartialAsync("_ShareView", new WorkbookShareModel());}
</div>
<div class="landing-container">
<div class="workbook-container">
<table class="table">
<tbody>
#foreach (var workbook in Model.Workbooks)
{
string trClassName, linkText;
if (workbook.Metadata.SharedBy == null)
{
trClassName = "saved-workbooks";
linkText = workbook.Name;
} else {
trClassName = "shared-with-me";
linkText = string.Format(
BaseLanguage.SharedWithMeWorkbook,
workbook.Name,
workbook.Metadata.SharedBy,
workbook.Metadata.SharedDate.ToShortDateString()
);
}
<tr class="#trClassName">
<td>#Html.ActionLink(linkText, "Open", "OpenAnalytics", new { id = Model.Id, workbook = workbook.Name })</td>
<td class="last-modified-date" title="Last Modified Date">#workbook.ModifiedDate.ToShortDateString()</td>
<td class="share">
<button title="Share" class="share-button" onclick='showSharingView("#workbook.Name", "#workbook.Id", "#Model.Id")'> </button>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
#section Scripts
{
<!--Load JQuery 'unobtrusive' validation -->
#await Html.PartialAsync("_ValidationScriptsPartial")
<script type="text/javascript">
// hide the modal as soon as the page loads
$('#shareFormModal').modal("hide");
function showSharingView(title, workbookId, id) {
$('#shareFormModal').modal("show");
// how to pass a WorkbookShareModel to my partial view from here?
}
function hideDialog() {
var form = $("#partialform");
// only hide the dialog if the form is valid
if (form.valid()) {
activateShareButtons();
$('#shareFormModal').modal("hide");
}
}
// Helper method that validates list of emails
function IsEmailValid(emailList, element, parameters) {
var SPLIT_REGEXP = /[,;\s]\s*/;
var EMAIL_REGEXP =
/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+##[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/i;
var emails = emailList.split(SPLIT_REGEXP);
for (var i = emails.length; i--;) {
if (!EMAIL_REGEXP.test(emails[i].trim())) {
return false;
}
}
return true;
}
</script>
}
That is my dialog:
#using DNAAnalysisCore.Resources
#model DNAAnalysisCore.Models.WorkbookShareModel
#* Partial view that contains the 'Share Workbook dialog' modal *#
<!-- Modal -->
<div onclick="activateShareButtons()" class="modal fade" id="shareFormModal" role="dialog">
<div class="modal-dialog modal-md">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Share Workbook - #Model.Title</h4>
</div>
#using (Html.BeginForm("ShareWorkbook", "Home", FormMethod.Post, new { #id = "partialform" }))
{
<div class="modal-body">
<label>#BaseLanguage.Share_workbook_Instruction_text</label>
<div class="form-group">
<textarea class="form-control" asp-for="Emails" rows="4" cols="50" placeholder="#BaseLanguage.ShareDialogPlaceholder"></textarea>
<span asp-validation-for="Emails" class="text-danger"></span>
</div>
<input asp-for="Title" />
<input asp-for="Id" />
<input asp-for="WorkbookId"/>
</div>
<div class="modal-footer">
<button onclick="hideDialog()" type="submit" class="btn btn-primary">Share</button>
<button onclick="activateShareButtons()" id="btnCancelDialog" type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>
</div>
There are two solutions to solve your problem :
Option 1 :
Since you have got the parameters(title, workbookId, id) , you can call server side function using AJAX to render the partial view , then replace the DIV contained in the partial view with the updated contents in the callback function of AJAX .
You can click here for code sample .
Option 2 :
Directly update related input/area using Jquery . For example , the input tag helper :
<input asp-for="<Expression Name>">
generates the id and name HTML attributes for the expression name specified in the asp-for attribute. So you can set the value using Jquery like :
$("#Title").val("Title")
Please click here for Tag Helpers in forms in ASP.NET Core
With Option 2 , you need to clear the Emails area firstly after user click the share button ; With Option 1 , you don't need to care that since the HTML will replace entirely .

Aurelia repeater: model.bind is not working for radio buttons

I am creating a set of radio buttons in Aurelia with the code like this:
<div repeat.for="option of options">
<input type="radio" id="${option}_id" name="radio_options" model.bind="option" checked.bind="optionValue"/>
<label for="${option}_id" id="${option}_label">${option}</label>
</div>
However, doing it this way I discovered that model.bind is not working - the optionValue in corresponding class is not populated when radio button is checked. Similarly when some value is assigned to optionValue in the class, the appropriate radio button is not checked. I found this happening only with repeater. Options are numbers in my case. Could you please help me to find out what may be wrong here?
The first problem is that model.bind should be used when working with objects. Since you're working with a primitive type, you should use value.bind instead.
The second problem is that input values are always strings, so when setting an initial value, it must be a string. For example:
Html:
<template>
<div repeat.for="option of options">
<input type="radio" id="${option}_id" name="radio_options" value.bind="option" checked.bind="optionValue"/>
<label for="${option}_id" id="${option}_label">${option}</label>
</div>
<p>Selected Option: ${optionValue} </p>
</template>
JS:
export class App {
options = [ 1, 2, 3, 4 ]
optionValue = '3';
}
If you really want to use int in your view-model, you can create a ValueConverter to convert the value to int when passing it to/from the view. For instance:
export class AsIntValueConverter {
fromView(value) {
return Number.parseInt(value);
}
toView(value) {
return value.toString();
}
}
Usage:
<input type="radio" id="${option}_id" name="radio_options" value.bind="option" checked.bind="optionValue | asInt"/>
Running Example https://gist.run/?id=1465151dd5d1afdb7fc7556e17baec35

GEB find first visible element

I have got some hidden buttons on the page. When I click on text input, one of these buttons shows on the page. Before:
<div field='login'>
<input type="text">
<button class="submit" style="display: none">Save</button>
</div>
<div field='name'>
<input type="text">
<button class="submit" style="display: none">Save</button>
</div>
After click on second input:
<div field='login'>
<input type="text">
<button class="submit" style="display: none">Save</button>
</div>
<div field='name'>
<input type="text">
<button class="submit">Save</button>
</div>
So I try to interact with second button by next selectors in my test:
static content = {
submitButton { $("button.submit") }
}
but I have next error:
isorg.openqa.selenium.ElementNotVisibleException: Element is not currently visible and so may not be interacted with
If I write:
static content = {
submitButton { $("button.submit", 1) }
}
it works, but I need to work with one first visible button on the page. What is wrong?
Unfortunately there is no css selector to find visible elements but you can use displayed property of Navigator and its findAll() method to find the button that is visible:
static content = {
submitButton { $("button.submit").findAll { it.displayed } }
}