Protractor find parent element - selenium

<label class="radio inline check">
<input class="input ng-new ng-valid" name="BookType" required="" type="radio">
<!---->
<!---->
Fiction
</label>
<label class="radio inline check">
<input class="input ng-new ng-valid" name="BookType" required="" type="radio">
<!---->
<!---->
NonFiction
</label>
<label class="radio inline check">
<input class="input ng-new ng-valid" name="BookTypeReal" required="" type="radio">
<!---->
<!---->
Fiction
</label>
<label class="radio inline check">
<input class="input ng-new ng-valid" name="BookTypeReal" required="" type="radio">
<!---->
<!---->
Fantasy
</label>
http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.filter
If I use
element.all(locator).filter(filterFn)
the text returned is empty.
How can I go to parent element <label> to get the text?
label[class="radio inline check"]
returns 60 elements where more than one getText will return same text so it won't be unique.
input[name="BookType"]
returns two elements where each element is unique.

I used the following approach using TypeScript:
import {by, ElementFinder} from 'protractor';
export interface ElementDescriptor {
tag?: string;
cssClass?: string;
}
async function matches(element: ElementFinder, parentDescription: ElementDescriptor): Promise<boolean> {
const promises = [];
if (parentDescription.tag) {
promises.push(element.getTagName());
}
if (parentDescription.cssClass) {
promises.push(element.getAttribute('class'));
}
let i = 0;
const results: string[] = await Promise.all(promises);
return (!parentDescription.tag || results[i++] === parentDescription.tag)
&& (!parentDescription.cssClass || results[i++].split(' ').includes(parentDescription.cssClass));
}
export async function findParent(element: ElementFinder, parentDescription: ElementDescriptor): Promise<ElementFinder> {
let parent: ElementFinder;
do {
parent = element.element(by.xpath('..'));
} while ((await parent.getTagName()) !== 'html' && !(await matches(parent, parentDescription)));
return parent;
}

To click on the <input> tag whose <label> has text as Fiction and <input> tag has name as BookType you can use the following xpath :
"//label[#class='radio inline check' and contains(normalize-space(), 'Fiction')]/input[#class='input ng-new ng-valid' and #name='BookType']"

You have two options to do it:
To get the parent element, as you mentioned. For this you can use xpath's ancestor for it like this:
.//*[#name='BookType']/ancestor::label
To get the text as the sibling of the element with #name='BookType':
.//*[#name='BookType']/following-sibling::text()[normalize-space()]

In Xpath if you want parent of input you can use //input[name="BookType"]/..

i have framed this based on available details over the net. Please give a try.
element.all(by.css('.radio inline check')).filter(function(elem){
return elem.element(by.xpath("//*[text()[contains(.,'Fiction')]]")).getText()
}).then(function(filteredElements){
filteredElements.first().element(by.css('input[name=BookType]')).click();
});
Note: You may have some format issue in this.

Related

Dynamic input value related to another values

I would like to make the value ****here****of the input id="inputWorkload" dynamic and related to the value of inputDuration (newTask.duration * 2 )
How to do it with Vue js?
<div class="col-sm-2">
<div class="form-group">
<label for="inputDuration">Duration (H)</label>
<input class="form-control" id="inputDuration" min="4" step="4" type="number" v-model="newTask.duration">
</div>
</div>
<div class="col-sm-2">
<div class="form-group">
<label for="inputWorkload">Workload</label>
<input disabled class="form-control" id="inputWorkload" value="****here****">
</div>
</div>
Add an on change event to the duration:
<input class="form-control" id="inputDuration" min="4" step="4" type="number" v-model="newTask.duration" onchange="myFunction()">
Then write some JavaScript which gets the value of the duration on change and updates the workload value based on that.
<script>
function myFunction() {
var inputDurationValue = document.getElementById("inputDuration").value;
document.getElementById("inputWorkload") = inputDurationValue;
}
</script>
With a watcher. Something like this.
data: {
//define your #inputWorkload variable , let's say "workload"
},
watch: {
newTask: {
handler(val){
this.workload = val.duration * 2;
},
deep: true
},
}
// in your template
<input disabled class="form-control" id="inputWorkload" :value="workload">

Add invalid class to form-group using VueValidate to bootstrap CSS

How to add invalid class to form-group if the validation fails on input. By default VueValidate adds to the input.
<div class="form-group">
<label for="mobile" v-bind:class="{'text-danger': errors.has('mobile')}">Mobile</label>
<input type="text" v-validate="validation.mobile" class="form-control" v-model="user.mobile" name="mobile" id="mobile" />
<span class="invalid-feedback">{{ errors.first('mobile') }}</span>
</div>
Currently i am using v-bind:class="{'text-danger': errors.has('mobile')}" on the label and i get red colored label on field error.
If i could add invalid to form-group, it would be better to control with css. Below is my VueValidate Settings
Vue.use(VeeValidate, {
aria: true,
classes: true,
classNames: {
invalid: 'is-invalid'
}
});
You can bind a computed function to check errors and return the div's classes
{
computed: {
formGroupClass: function () {
if (this.error.has('mobile') ){
return ['form-group', 'invalid']
}
return ['form-group']
}
}
}
<div :class="formGroupClass">
<label for="mobile" v-bind:class="{'text-danger': errors.has('mobile')}">Mobile</label>
<input type="text" v-validate="validation.mobile" class="form-control" v-model="user.mobile" name="mobile" id="mobile" />
<span class="invalid-feedback">{{ errors.first('mobile') }}</span>
</div>

Angular2: How to enable save button if any model value changed on edit page

I am new for angular 2. I have a page where we can edit details of customer profile. How to enable save button if any property of has been changed. I know it is possible in angular1 by using $watch.
It is simple. dirty check your form if you are using #angular/forms.
create form
export class HeroDetailComponent4 {
heroForm: FormGroup;
states = states;
constructor(private fb: FormBuilder) {
this.createForm();
}
createForm() {
this.heroForm = this.fb.group({
name: ['', Validators.required ],
street: '',
city: '',
state: '',
zip: '',
power: '',
sidekick: ''
});
}
}
HTML:
<h2>Hero Detail</h2>
<h3><i>A FormGroup with multiple FormControls</i></h3>
<form [formGroup]="heroForm" novalidate>
<button (click)="submit()" [disabled]="!heroForm.dirty" type="button">Submit</button>
<div class="form-group">
<label class="center-block">Name:
<input class="form-control" formControlName="name">
</label>
</div>
<div class="form-group">
<label class="center-block">Street:
<input class="form-control" formControlName="street">
</label>
</div>
<div class="form-group">
<label class="center-block">City:
<input class="form-control" formControlName="city">
</label>
</div>
<div class="form-group">
<label class="center-block">State:
<select class="form-control" formControlName="state">
<option *ngFor="let state of states" [value]="state">{{state}}</option>
</select>
</label>
</div>
<div class="form-group">
<label class="center-block">Zip Code:
<input class="form-control" formControlName="zip">
</label>
</div>
<div class="form-group radio">
<h4>Super power:</h4>
<label class="center-block"><input type="radio" formControlName="power" value="flight">Flight</label>
<label class="center-block"><input type="radio" formControlName="power" value="x-ray vision">X-ray vision</label>
<label class="center-block"><input type="radio" formControlName="power" value="strength">Strength</label>
</div>
<div class="checkbox">
<label class="center-block">
<input type="checkbox" formControlName="sidekick">I have a sidekick.
</label>
</div>
</form>
use heroForm.dirty to check whether form data is changed. it will set to true if any control inside heroForm has been changed.
<button (click)="submit()" [disabled]="!heroForm.dirty" type="button">Submit</button>
Refer angular docs for more info
you can use form control validation for it.
some thing like this in html template:
<form fxLayout="column" [formGroup]="form">
<mat-form-field class="mb-1">
<input matInput [(ngModel)]="userProfileChangeModel.firstName" placeholder="نام"
[formControl]="form1.controls['fname']">
<small *ngIf="form1.controls['fname'].hasError('required') && form1.controls['fname'].touched"
class="mat-text-warn">لطفا نام را وارد نمایید.
</small>
<small *ngIf="form1.controls['fname'].hasError('minlength') && form1.controls['fname'].touched"
class="mat-text-warn">نام باید حداقل 2 کاراکتر باشد.
</small>
<small *ngIf="form1.controls['fname'].hasError('pattern') && form1.controls['fname'].touched"
class="mat-text-warn">لطفا از حروف فارسی استفاده نمائید.
</small>
</mat-form-field>
<mat-card-actions>
<button mat-raised-button (click)="editUser()" color="primary" [disabled]="!form1.valid" type="submit">
ذخیره
</button>
</mat-card-actions>
</form>
and like this in ts file:
this.form = this.bf.group({
fname: [null, Validators.compose([
Validators.required,
Validators.minLength(2),
Validators.maxLength(20),
Validators.pattern('^[\u0600-\u06FF, \u0590-\u05FF]*$')])],
});
if:
[disabled]="!form1.valid"
is valid save button will be active
bast regards.
You can use disabled option like below :
<button [disabled]="isInvalid()" type="button" (click) = "searchClick()" class="button is-info">
<span class="icon is-small">
<i class="fa fa-search" aria-hidden="true"></i>
</span>
<span>Search</span>
</button>
you can create isInvalid() in your ts file and check if that property is empty or not and return that boolean value
and for hide button on a state you can use *ngIf in line directive.
This worked for me, pls try.
In your html,
<input type="text" [ngModel]="name" (ngModelChange)="changeVal()" >
<input type="text" [ngModel]="address" (ngModelChange)="changeVal()" >
<input type="text" [ngModel]="postcode" (ngModelChange)="changeVal()" >
<button [disabled]="noChangeYet" (click)="clicked()" >
<span>SUBMIT</span>
</button>
In your component
export class customer implements OnInit {
name: string;
address: string;
postcode: string;
noChangeYet:boolean = true;
constructor() {}
changeVal(){ // triggers on change of any field(s)
this.noChangeYet = false;
}
clicked(){
// your function data after click (if any)
}
}
Hope this is what you need.
Finally I resolved this issue.
import { Component, Input, Output, OnInit, AfterViewInit, EventEmitter, ViewChild } from '#angular/core';
#Component({
selector: 'subscribe-modification',
templateUrl: './subscribe.component.html'
})
export class SampleModifyComponent implements OnInit, AfterViewInit {
disableSaveSampleButton: boolean = true;
#ViewChild('sampleModifyForm') sampleForm;
ngAfterViewInit() {
setTimeout(() => {
this.sampleForm.control.valueChanges.subscribe(values => this.enableSaveSampleButton());
}, 1000);
}
enableSaveSampleButton() {
console.log('change');
this.disableSaveSampleButton = false;
}
}
HTML
<button id="btnSave" class="btn btn-primary" (click)="save()" />

Vue Validator only after change / blur / submit

I'm using Vue for the first time, with Vue Validator. Here is an example of my code:
<label for="first_name">First name:
<span v-if="$validation1.first_name.required" class="invalid">Enter your first name.</span>
<input id="first_name" placeholder="e.g. Christopher" class="" v-validate:first_name="['required']" v-model="first_name" name="first_name" type="text">
</label>
The only issue at the moment is that when I land on the page with my form, the whole thing is covered in errors. Is there a way I can suppress the errors and only show them on input blur / form submit?
Argh, the Google-able word isn't about blur, or on submit – its about timing and initial:
http://vuejs.github.io/vue-validator/en/timing.html
<input id="first_name" initial="off" placeholder="e.g. Christopher" class="" v-validate:first_name="['required']" v-model="first_name" name="first_name" type="text">
you need to add .dirty or .touched to your validation
<label for="first_name">First name:
<span v-if="$validation1.first_name.required && $validation1.first_name.touched" class="invalid">Enter your first name.</span>
<input id="first_name" placeholder="e.g. Christopher" class="" v-validate:first_name="['required']" v-model="first_name" name="first_name" type="text">
</label>
I was dealing with a similar problem. I had to have an initialized variable for the input name: "" but I also wanted to have a required attribute in element.
So I add required when the event onblur occurs.
<input name="name" type="number" v-model="name" #blur="addRequired" />
const app = Vue.createApp({
data() {
return {
name: ""
}
},
methods:{
addRequired: function(event){
event.target.setAttribute("required", true);
}
}
});

vuejs set a radio button checked if statement is true

I am trying to make a radio button checked using vuejs v-for only if my if-statement is true. Is there a way to use vuejs' v-if/v-else for this type of problem?
in php and html I can achieve this by doing the following:
<input type="radio" <? if(portal.id == currentPortalId) ? 'checked="checked"' : ''?>>
Below is what I have so far using vuejs:
<div v-for="portal in portals">
<input type="radio" id="{{portal.id}}" name="portalSelect"
v-bind:value="{id: portal.id, name: portal.name}"
v-model="newPortalSelect"
v-on:change="showSellers"
v-if="{{portal.id == currentPortalId}}"
checked="checked">
<label for="{{portal.id}}">{{portal.name}}</label>
</div>
I know the v-if statement here is for checking whether to show or hide the input.
Any help would be very much appreciated.
You could bind the checked attribute like this:
<div v-for="portal in portals">
<input type="radio"
id="{{portal.id}}"
name="portalSelect"
v-bind:value="{id: portal.id, name: portal.name}"
v-model="newPortalSelect"
v-on:change="showSellers"
:checked="portal.id == currentPortalId">
<label for="{{portal.id}}">{{portal.name}}</label>
</div>
Simple example: https://jsfiddle.net/b4k6tpj9/
Maybe someone finds this approach helpful:
In template I assign each radio button a value:
<input type="radio" value="1" v-model.number="someProperty">
<input type="radio" value="2" v-model.number="someProperty">
Then in the component I set the value, i.e:
data: function () {
return {
someProperty: 2
}
}
And in this case vue will select the second radio button.
You can follow below option if you can adjust with your logic:
<div class="combination-quantity">
<input type="radio" value="Lost"
v-model="missing_status">
<label for="lost">Lost</label>
<br>
<input type="radio" value="Return Supplier" v-model="missing_status">
<label for="return_supplier">Return Supplier</label>
</div>
Value for missing_status could be "Lost" or "Return Supplier" and based on the value radio option will be get selected automatically.
Below is an example of keeping track of the selected radiobutton, by
applying a value binding to the object (:value="portal") and
applying a v-model binding to the currently selected object (v-model="currentPortal").
The radiobutton will be checked automatically by Vue, when the two match (no :checked binding necessary!).
Vue 3 with composition API
Vue.createApp({
setup() {
const portals = [{
id: 1,
name: "Portal 1"
}, {
id: 2,
name: "Portal 2"
}];
const currentPortal = portals[1];
return {
portals,
currentPortal
}
}
}).mount("#app");
<script src="https://unpkg.com/vue#next"></script>
<div id="app">
<template v-for="portal in portals">
<input
type="radio"
:id="portal.id"
name="portalSelect"
:value="portal"
v-model="currentPortal">
<label :for="portal.id">{{portal.name}}</label>
</template>
</div>
I would like to point out a few options when dealing with radios and vue.js. In general if you need to dynamically bind an attribute value you can use the shorthand binding syntax to bind to and calculate that value. You can bind to data, a computed value or a method and a combination of all three.
new Vue({
el: '#demo',
data() {
return {
checkedData: false,
checkedGroupVModel: "radioVModel3", //some defaul
toggleChecked: false,
recalculateComputed: null
};
},
computed: {
amIChecked() {
let isEven = false;
if (this.recalculateComputed) {
let timeMills = new Date().getMilliseconds();
isEven = timeMills % 2 === 0;
}
return isEven;
}
},
methods: {
onToggle() {
this.toggleChecked = !this.toggleChecked;
return this.toggleChecked;
},
mutateComputedDependentData() {
this.recalculateComputed = {};
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="demo">
<div>
<div>
<span>Simple Radio Group - Only one checked at a time. Bound to data.checkedData</span><br>
<label>Radio 1 - inverse of checkedData = {{!checkedData}}
<input type="radio" name="group1" value="radio1" :checked="!checkedData">
</label><br>
<label>Radio 2 - checkedData = {{checkedData}}
<input type="radio" name="group1" value="radio2" :checked="checkedData">
</label><br>
<span>Understanding checked attribute: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-checked</span>
</div>
<br>
<div>
<span>Simple Radio - Checked bouned to semi-random computed object</span><br>
<label>Radio 1: {{amIChecked}}
<input type="radio" :checked="amIChecked">
</label>
<label>Recalculate Computed Value
<button type="button" #click="mutateComputedDependentData">Click Me Several Times</button>
</label>
</div>
<br>
<div>
<span>Simple Radio Group - v-model bound value = {{checkedGroupVModel}}</span><br>
<label>Simple Radio 1:
<input type="radio" name="vModelGroup" value="radioVModel1" v-model="checkedGroupVModel">
</label><br>
<label>Simple Radio 2:
<input type="radio" name="vModelGroup" value="radioVModel2" v-model="checkedGroupVModel">
</label><br>
<label>Simple Radio 3:
<input type="radio" name="vModelGroup" value="radioVModel3" v-model="checkedGroupVModel">
</label>
</div>
<br>
<div>
<span>Simpe Radio - click handler to toggle data bound to :checked to toggle selection</span><br>
<label>Toggle Radio = {{toggleChecked}}
<input type="radio" :checked="toggleChecked" #click='onToggle()'>
</label>
</div>
</div>
</div>