I'm trying to use ids with my input box's within my login page but I get the following error with Protractor:
Failed: No element found using locator: By css selector, *[id="signin--username"])
Here is my log-in.js
var logIn = function() {
this.navigate = function() {
browser.get(browser.params.server);
};
this.usernameInputBox = element(by.id('signin--username'));
this.passwordInputBox = element(by.id('signin--password'));
this.dontHaveAnAccountButton = element(by.id('signin--no-account-question'));
this.logInButton = element(by.id('signin--log-in'));
this.Modal = element(by.css('.modal-dialog'));
this.ModalButton = element(by.xpath('//*[#id="app"]/div[3]/div/div/form/div[3]/button'));
};
module.exports = new logIn();
Snippet from log-in.html
<div class="form-group">
<div class="input-group input-group-lg">
<span class="input-group-addon">
<span class="glyphicon glyphicon-user"></span>
</span>
<input type="text"
id="signin--username"
class="form-control"
placeholder="{{'username' | translate}}"
ng-model="username"
name="username"
required
autofocus data-autofill
>
</div>
</div>
Protractor Test Javascript File:
module.exports = function() {
describe('Login Page', function() {
var loginPage = require('../pages/log-in.js');
var saveScreenshot = require('../screenshot.js');
beforeEach(function() {
loginPage.navigate();
})
it('should log in (with correct credentials)', function() {
browser.waitForAngular();
loginPage.usernameInputBox.sendKeys('service');
loginPage.passwordInputBox.sendKeys('s5rv1c5');
loginPage.logInButton.click();
browser.waitForAngular();
expect(browser.getCurrentUrl()).toContain(browser.params.server + '#/jobs/my_jobs');
})
});
};
Any help much appreciated! Thanks.
Looks like a timing issue. Improve navigate page object method to also wait for the page to load - wait for the username field to become present:
var logIn = function() {
this.usernameInputBox = element(by.id('signin--username'));
this.passwordInputBox = element(by.id('signin--password'));
this.dontHaveAnAccountButton = element(by.id('signin--no-account-question'));
// ...
this.navigate = function() {
browser.get(browser.params.server);
var EC = protractor.ExpectedConditions;
browser.wait(EC.presenceOf(this.usernameInputBox), 5000, "Username field is still not present");
};
};
You are not giving the unique selector correctly. That is why this error occurs.
Use element(by.model('username')).sendKeys('your user name');
I assume that you gave the html of login text box.
Hope this helps. :)
Related
I'm trying to make an autocomplete dropdown menu in vue and can't get the api response to render on the page. I'm making an api call on every keystroke in the input. You can see in the handleOnChange method that I'm trying to set the response to the results variable that is binding to the list above.
When I console log the results right after I make the api call AND set it to the data binding variable it logs as if everything is working fine. However, it does not render the actual data on the screen.
Here is my code
<template>
<div>
<input type="text" value="" v-model="address" placeholder="Start typing an address..." #input="methods.handleOnChange(address)"/>
<ul v-if="results.length !== 0">
<li v-for="result in results">
{{ result.streetLine || ""}}
</li>
</ul>
<p v-model="results">
{{results}}
</p>
</div>
</template>
<script>
const SmartyStreetsSDK = require("smartystreets-javascript-sdk");
const SmartyStreetsCore = SmartyStreetsSDK.core;
const Lookup = SmartyStreetsSDK.usAutocompletePro.Lookup;
const credentials = new SmartyStreetsCore.SharedCredentials([website-key]);
const clientBuilder = new SmartyStreetsCore.ClientBuilder(credentials).withLicenses(["us-autocomplete-pro-cloud"]).withBaseUrl("https://us-autocomplete-pro.api.smartystreets.me/lookup");
const client = clientBuilder.buildUsAutocompleteProClient();
export default {
name: 'Autocomplete',
data () {
return {
address: "",
results: [{streetLine: "testing"}],
methods: {
handleOnChange: function(address) {
//TODO: need to be able to access "results" from in here
console.log("this: ", this);
if (address) {
const lookup = new Lookup(address);
client.send(lookup).then((response) => {
console.log(response.result);
this.results = response.result;
console.log("databinding: ", this.results);
}).catch(console.log)
}
}
}
}
}
}
</script>
Vue.set()
As discussed in the comments, Vue.set was able to do it.
See documentation: https://v2.vuejs.org/v2/api/#Vue-set
Arguments are:
{Object | Array} target
{string | number} propertyName/index
{any} value
It replaces the value at target[propertyName/index] with value and forces reactivity on the value(s).
In your case it should be this instead of this.results = response.result;:
Vue.set(this, "results", response.result);
I am very new with vuejs and recently started to try to replace some old jquery code that I have and make it reactive with vuejs. The thing is I have a component that gets information from a nodejs server via socket.io asynchronously.
When I get the data and update my component's data I see the changes when I console log it but it does not change the DOM the way I want it to do.
What is the proper way to grab data asynchronously and use it inside a component? I post some parts of my code so you can see it. I will appreciate any advice you can give me. Thanks in advance!
Vue.component('chat', {
data() {
return {
chat: null,
commands: [],
chatOpened: false,
}
},
props: [
'io',
'messages',
'channels',
'connectChat',
'roomChat',
'user',
'userId',
'soundRoute',
],
methods: {
openChat() {
this.chatOpened = true;
},
closeChat() {
this.chatOpened = false;
},
},
created() {
this.chat = this.$io.connect(this.connectChat);
this.commands.push('clear');
let self = this;
$.each(this.channels, function(index, value) {
self.chat.emit('join', {room: index, user: self.user, userId: self.userId}, function(err, cb) {
if (!err) {
users = cb.users;
messages = cb.messages;
if (messages.length > 0) {
self.channels[index].loaded = true;
}
//some more code
}
});
});
console.log(this.channels);
},
template: `
<div>
<div id="container-chat-open-button" #click="openChat" :class="{hide : chatOpened}">
<div>+90</div>
<i class="fas fa-comment-alt"></i>
</div>
<div id="container-chat" class="chat__container" :class="{open : chatOpened}">
<div id="container-chat-close-button" #click="closeChat">
<span>
<div>
<i class="fas fa-comment-alt"></i>
#{{ messages.chat_lobby_icon_title }}
</div>
<i class="icon-arrowdown"></i>
</span>
</div>
<div id="alert-chat" class="chat__container-notifications animated flash"></div>
<div class="row">
<ul>
<li v-for="channel in channels" v-show="channel.loaded === true">Channel loaded</li>
</ul>
</div>
</div>
</div>
`
});
I would expect to see the list of channels with messsages but instead I don't see the list even thought I see my channels with the loaded attribute set to true (by default they all have this attribute set to false).
My guess is that it's this part that is not working as expected.
if (messages.length > 0) {
self.channels[index].loaded = true;
}
The reactive way of doing this is by setting the full object again.
Vue.set(self.channels, index, {
...self.channels[index],
loaded: true
}
EDIT 1:
this.channels.forEach((channel) => {
this.chat.emit('join', {room: index, user: self.user, userId: self.userId}, (err, cb) => {
if (!err) {
users = cb.users;
messages = cb.messages;
if (messages.length > 0) {
Vue.set(self.channels, index, {
...self.channels[index],
loaded: true
}
}
//some more code
}
});
})
You'll need to add support for the rest-spread-operator using babel.
I am trying to fire a function when something is typed into the input. For some reason, it is not working when I use a template and component. Can anyone help me figure out why this is happening? I am new to Angular by the way.
Component
When the button is clicked or when something is typed into the input, the ng-click and ng-change functions should fire but they are not.
(function(angular) {
'use strict';
angular
.module('app')
.component('coinSearch', {
controller: CoinSearchController,
controllarAs: 'coin',
templateUrl: 'src/coinSearch.html'
});
function CoinSearchController(CryptoService) {
var coin = this;
var list = [];
coin.jank="something weird";
coin.savedCoins = [];
coin.searchedCoin = '';
function getCrypto() { //pulls data from API
CryptoService
.retrieve()
.then(function(response) {
coin.list = response;
});
}
coin.click = function() {
console.log('HELLOOO');
};
coin.showSearch = function() {
console.log('hello');
return coin.searchedCoin === '';
};
getCrypto();
}
})(angular);
Template
Template for the component above. There are some console.logs for testing purposes.
<div class="container">
<form class="search-form">
<button ng-click="coin.click()">{{coin.jank}} </button> //testing
<input
type="text"
class="search"
placeholder="Search Crypto"
ng-model="coin.searchedCoin"
ng-change="coin.showSearch()">
<ul class="suggestions">
<li ng-hide="coin.showSearch()" ng-repeat="coins in coin.list |
filter:coin.searchedCoin">
<span>{{coins.name}} ({{coins.symbol}})</span>
<span>${{coins.price_usd}}</span>
<span><button ng-
click="coin.addToList(coins);">Add</button></span>
</li>
</ul>
</form>
<coin-list></coin-list>
</div>
If you look at your controllerAs statement, you have it spelled as controllarAs. That would explain why nothing is listening in the template when you use coin.
I'm trying to create a live search form which is calling a http request whenever there is user input. So far the live searching works well, but the http request results in a loop. This problem shows up when I'm assigning the catalog_items to this.items.
Vue.js
Vue.filter('searchFor', function (value, searchString) {
var result = [];
if(!searchString || searchString.length < 2){
return value;
}
searchString = searchString.trim().toLowerCase();
this.fetchData(searchString);
result = this.items;
return result;
})
new Vue({
el: '#searchform',
data: {
searchString: "",
items: []
},
methods: {
fetchData: function (name) {
this.$http.get('api_url' + name )
.then(function(response){
var data = response.data;
var catalog_items = data['catalog_items'];
this.items = catalog_items;
})
}
}
})
The html search input:
<input type="text" v-model="searchString" placeholder="Enter your search terms" />
<ul>
<li v-for="item in catalog_items | searchFor searchString">
<p>#{{item.name}}</p>
</li>
</ul>
Thanks in advance!
I am creating a wizard login form where the Mobile Number is first entered and
password is entered next.
Here am trying to focus the password input using
this.$$.passwordInput.focus()
however if am getting the error given below
Uncaught TypeError: Cannot read property 'focus' of undefined
The full code is below
index.html
<div id="login">
<div v-if="flow.mobile">
<form v-on="submit: checkmobile">
<p>
Mobile Number<br>
<input type="text" v-model="mobile_number" v-el="mobileNumber">
</p>
</form>
</div>
<div v-if="flow.password">
<form v-on="submit: checkpassword">
<p>
Password<br>
<input type="password" v-model="password" v-el="passwordInput">
</p>
</form>
</div>
script.js
var demo = new Vue({
el: '#login',
data: {
flow: {
mobile: true,
password: false
}
},
methods: {
checkmobile: function(e) {
e.preventDefault();
this.flow.mobile = false;
this.flow.password = true;
this.$$.passwordInput.focus();
},
checkpassword: function(e) {
e.preventDefault();
}
}
});
Your passwordInput is inside a v-if block, which only gets rendered when you set flow.password to true; However Vue uses asynchronous rendering, so the v-if block will not be rendered immediately. You can use Vue.nextTick to wait until it does:
this.flow.password = true;
var self = this;
Vue.nextTick(function () {
self.$$.passwordInput.focus();
});
Read the guide about async rendering for more details.
In Vue.js 2.x you can create your own directive to focus a field automatically:
Vue.directive('focus', {
inserted: function (el) {
el.focus();
},
update: function (el) {
Vue.nextTick(function() {
el.focus();
})
}
})
Then you can use v-focus attribute on inputs and other elements:
<input v-focus>
Working example: https://jsfiddle.net/LukaszWiktor/cap43pdn/
If you are using vuejs 2, you should read this:
https://v2.vuejs.org/v2/guide/migration.html#v-el-and-v-ref-replaced
--
In this case, in your template:
use ref="passwordInput" instead v-el="passwordInput"
and in your method:
this.$refs.passwordInput.focus()
I hope this help you!
Glad this worked for some of you.
I have no idea why, but after trying every conceivable variation of the accepted answer I could not get the $$.ref property when using v-repeat.
I could only access the newly created dom elements like so:
new Vue({
el: '#reporting_create',
data: {
recipients: {
0: {
fname: null,
lname: null,
email: null,
registration: false,
report: false
}
},
curRec:1
},
methods: {
addRecipient: function(){
event.preventDefault();
this.recipients.$add(
this.curRec,
{
fname: null,
lname: null,
email: null,
registration: false,
report: false
}
);
var num = this.curRec;
this.$nextTick(function () {
console.log(this._children[num].$$.rowrec);
newSwitches.find('.switch').bootstrapSwitch();
})
this.curRec++;
}
}})
html:
<template v-repeat="recipients">
<div class="row" v-el="rowrec">
<div>{{$key}}</div>
</div>
</template>
The addRecipients function was called outside the v-repeat so even the suggested answer here did couldn't help
Not sure if there is an issue with doing it this way but it works and I'm tired.
Vue.js 1 works a bit different.
Example:
<textarea v-el:model_message></textarea>
JS:
this.$els.model_message.focus();
If you are using Vue.js 2.0, you should do the following:
<input type="text" v-model="currentItem.name" ref="txtName">
So you can access this input by using the $refs object:
this.$refs.txtName.focus();
I hope it helps.
Vue v2's documentation uses focus as an example in writing custom directives. All of the needed code is supplied with the example, https://v2.vuejs.org/v2/guide/custom-directive.html. First you must register the directive. The link shows how to register it locally on the component.
// Register a global custom directive called `v-focus`
Vue.directive('focus', {
// When the bound element is inserted into the DOM...
inserted: function (el) {
// Focus the element
el.focus()
}
})
Having done this, you are now able to use v-focus on an element:
<input v-focus>
Like so.