Global helper works when called in HTML, not in JS - javascript

I have the following global helper:
Template.registerHelper('results',function(){
var valuationId = this._id;
var valuation = Valuations.findOne({_id: valuationId});
var targetId = this.targetId;
var targetTicker = Companies.findOne({_id:targetId}).ticker;
var targetData = CompaniesData.findOne({ticker: targetTicker});
return {
peFy1: targetData.epsFy1 * valuation.PriceEarningsFy1,
peFy2: targetData.epsFy2 * valuation.priceEarningsFy2
}
});
When I call this helper through HTML, like so, it works fine:
<div>
{{results.peFy1}}
</div>
I am not able to display the value when calling the helper through Javascript, per this answer.
<div>
{{peFy1}}
</div>
Template.ValuationResults.helpers({
peFy1: function() {
return UI._globalHelpers.results().peFy1;
}
});
I've tried writing this in a couple other ways but none work:
return UI._globalHelpers['results']().peFy1;
return Template._globalHelpers.results().peFy1;
For what it's worth, UI._globalHelpers gives an error in Webstorm as being unresolved variables.
I thought the problem might be that I am not passing any parameters to the function, but it works fine through HTML so shouldn't be necessary. Also, adding console.log(this._id) and console.log(this.targetId) within the test helper both return correct results, so those are valid.
CORRECT CODE USING ANSWER BELOW:
getResults = function(valuationId,targetId){
var valuation = Valuations.findOne({_id: valuationId});
var targetTicker = Companies.findOne({_id:targetId}).ticker;
var targetData = CompaniesData.findOne({ticker: targetTicker});
return {
peFy1: targetData.epsFy1 * valuation.priceEarningsFy1,
peFy2: targetData.epsFy2 * valuation.priceEarningsFy2
}
};
Template.registerHelper('results',function(){
return getResults();
});
Template.Valuation.helpers({
peFy1: function() {
var valuationId = this._id;
var targetId = this.targetId;
return getResults(valuationId,targetId).peFy1;
},
peFy1: function() {
var valuationId = this._id;
var targetId = this.targetId;
return getResults(valuationId,targetId).peFy1;
}
});

Your use of UI._globalHelpers.results().peFy1 looks correct syntactically.
However as an alternative option that sidesteps the Meteor (UI._globalHelpers) api, create a standard JavaScript function like this:
getResults = function(){
var valuationId = this._id;
var valuation = Valuations.findOne({_id: valuationId});
var targetId = this.targetId;
var targetTicker = Companies.findOne({_id:targetId}).ticker;
console.log('targetId: ' + targetId);
console.log(Companies.findOne({_id:targetId})),
var targetData = CompaniesData.findOne({ticker: targetTicker});
return {
peFy1: targetData.epsFy1 * valuation.PriceEarningsFy1,
peFy2: targetData.epsFy2 * valuation.priceEarningsFy2
}
};
Template.registerHelper('results',function(){
return getResults();
});
Use results helper in templates and getResults function in JavaScript.

Related

Invalid Locator error

I tried to rewrite my tests in Page Object style but something goes wrong.
I use Class Tab and this is a part of my code:
var World = require('../support/world.js');
const isAllAjaxRequests = require('../scripts/util').isAllAjaxRequests;
const isElementLocatedAndVisible = require('../scripts/util').isElementLocatedAndVisible;
module.exports.Tab = class Tab {
constructor(data) {
this.name = "Base";
this.locators = {
'nextStepIsLocked': {xpath: '//md-tab-item[#aria-selected="true"]//div[#class="cc-status red"]'},
'isActiveTab': {xpath: '//md-tab-item[#aria-selected="true"]//span[text()="'+ data + '"]'}
}
}
waitForElement(bySelector) {
var driver = World.getDriver();
var self = this;
//var bySelector = self.locators[bySelector];
return driver.wait(isAllAjaxRequests(driver), waitTimeOut).then(() => {
//console.log(bySelector)
return driver.wait(isElementLocatedAndVisible(bySelector), waitTimeOut);
});
}
tabIsOpen(tabName) {
var driver = World.getDriver();
var self = this;
var bySelector = By.xpath('//md-tab-item[#aria-selected="true"]//span[text()="'+ tabName + '"]');
return self.waitForElement(bySelector);
}
}
Code in util:
exports.isElementLocatedAndVisible = function isElementLocatedAndVisible(driver, bySelector) {
return new Condition('element is located and visible', function(driver) {
console.log(bySelector)
return driver.findElements(bySelector).then((arr) => {
if (arr.length > 0) {
return arr[0].isDisplayed();
}
else {
return false;
}
});
});
};
I tried to use is in my test:
this.Then(/^Tab "([^"]*)" is open$/, function (tabName) {
this.createTab(tabName);
//var bySelector = tab.getLocator(isActiveTab);
return tab.tabIsOpen(tabName);
});
But I recieved an Invalid Locator error.
Via debug print I see thah I miss bySelector value when code go to exports.isElementLocatedAndVisible function. This is undefiened.
What I did wrong?
I suspect it is just missing of a parameter causing the issue.
In the following line:
return driver.wait(isElementLocatedAndVisible(bySelector), waitTimeOut);
add driver object as first argument and then bySelector, as follows:
return driver.wait(isElementLocatedAndVisible(driver, bySelector), waitTimeOut);
function is defined as follows:
function isElementLocatedAndVisible(driver, bySelector)
so, expecting driver object along with bySelector

Protractor - Waiting for async task to complete before proceeding with tests

Issue:
I have a helper library that reads data from an excel and returns an array of object. This library is an async function and I am unable to set a global array with the result of this function.
I was planning to read the global array as input source for my protractor tests.
Is there any way how this can be done? Any help would be appreciated.
Helper method:
'use strict';
var exceljs = require('exceljs');
var GetTestData = function(){
var colName = [];
var array = [];
this.getExcelData = function(page){
var workbook = new exceljs.Workbook();
return workbook.xlsx.readFile('./TestData/testLogin.xlsx').then(function(){
var worksheet = workbook.getWorksheet(page);
var headerRow = worksheet.getRow(1);
headerRow.eachCell(function(cell){
var colObj = {};
colObj = {header: cell.value, key: cell.value.toLowerCase()};
colName.push(colObj);
});
worksheet.columns = colName;
var iter = 0;
var rowObj = {};
worksheet.eachRow(function(row) {
if (iter != 0){
if(row.getCell('execute').value == 'Yes'){
headerRow.eachCell(function(cell){
rowObj[cell.value] = row.getCell(cell.value.toLowerCase()).value;
});
}
array.push(rowObj);
rowObj = {};
}
iter++;
});
return array;
});
};
};
module.exports = function(){
return new GetTestData();
};
I want to use the array returned from this function as a data source and loop my tests in it.
'use strict';
var array;
var TestData = require('../../Libraries/ExcelFn.js');
describe('Login Page', function(){
beforeAll(function(){
testData.getExcelData('Login').done(function(result){
array = result;
console.log(array); //The array returns fine here
});
});
array.forEach(function(data){
it('should test for each set of test data', function(){
//Login Tests here
});
});
});
Result: No specs found
Is there anyway how I can set the result from the helper method (possibly in a parameter in beforeLaunch()) and use that to parameterize my tests?

javascript array.filter()'s function is not working

when i'm trying to search on the md-contact-chips i'm having an error showing that createFilterFor is not a function. But i did create it as a function as show below. may i know is there any way to resolve this problem ?
basically i'm trying to clone this demo but instead of writing the contacts name manually i called existing data from my database to replace the contacts name by using http call which is show below Account.getTastes() however, after i got the data display in the md-contact-chip like the pictures below, the querySearch is not working anymore.
html
<md-contact-chips required ng-model="tags" md-require-match md-contacts="querySearch($query)" md-contact-name="name" md-contact-image="image" filter-selected="true" placeholder="Thai, chinese, cheap, cafe and etc">
</md-contact-chips>
Controller.js
$scope.querySearch = querySearch;
$scope.filterSelected = true;
$scope.allContacts = loadContacts();
$scope.allContacts.then(function(contacts){
$scope.tags = [contacts[0],contacts[1]]
})
/**
* Search for contacts.
*/
function querySearch (query) {
var results = query ?
$scope.allContacts.filter(createFilterFor(query)) : [];
return results;
}
/**
* Create filter function for a query string
*/
function createFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(contact) {
return (contact._lowername.indexOf(lowercaseQuery) != -1);;
};
}
function loadContacts() {
var contacts;
return Account.getTastes()
.then(function(res) {
contacts = res.data[0].tastes;
return contacts.map(function (c, index) {
var colors = ["1abc9c", "16a085", "f1c40f", "f39c12", "2ecc71", "27ae60", "e67e22","d35400","3498db","2980b9","e74c3c","c0392b","9b59b6","8e44ad","34495e","2c3e50"];
var color = colors[Math.floor(Math.random() * colors.length)];
var cParts = c.split(' ');
var contact = {
name: c,
image: "http://dummyimage.com/50x50/"+color+"/f7f7f7.png&text="+c.charAt(0)
};
contact._lowername = contact.name.toLowerCase();
return contact;
});
})
.catch(function(error) {
console.log("Error:"+error)
});
}
I have solved this problem
the reason i'm keep getting this error is because my $scope.allContacts is a promise instead of a array so the .filter will return the error saying createFilterFor is not a function.
$scope.allTastes.then(function(tastes){
$scope.tags = [tastes[0],tastes[1]]
$scope.allTags = tastes; // create a new $scope to get all tags from the promise
})
/**
* Search for contacts.
*/
function querySearch (query) {
console.log($scope.allTastes) // return a promise
$scope.allTastes= $scope.allTags; // pass it to $scope.allTastes, now it has all the array
var results = query ?
$scope.allTastes.filter(createFilterFor(query)) : [];
return results;
}
thanks for all the help !

jQuery TypeError:function not found error

I want to get some properties of one of my div via following code
(function ($) {
function StickyNotes() {
this.getProperties = function (note) {
var properties = {};
properties['top'] = note.position().top;
properties['from_center'] = this.calcFromCenter(note.position().left);
properties['width'] = note.find(".resize").width();
properties['height'] = note.find(".resize").height();
return properties;
}
this.saveBoardAndNotes = function (board_id, board_name) {
var noteList = new Array();
$(".optimal-sticky-notes-sticker-note").each(function(){
// Replace plain urls with links and improve html
var note = $(this);
content = note.find(".textarea").html();
noteID = note.attr("id");
properties = JSON.stringify(this.getProperties(note));
});
}
}
var StickyNotes = new StickyNotes();
jQuery(document).ready(function (e) {
$('#sticky-notes-add-board').click(function (e) {
if(confirm('Do you want to save previous board?')) {
var board_id = $('.optimal-sticky-notes-board:last').attr('id');
var board_name = $('.optimal-sticky-notes-board:last').text();
StickyNotes.saveBoardAndNotes(board_id, board_name);
}
})
});})(jQuery);
But I get following error..
TypeError: this.getProperties is not a function
I filtered all data like content and noteID. They are showing. But problem with this.getProperties. How can i solve the problem. Thanks in advance.
Inside each loop this points to current optimal-sticky-notes-sticker-note div. One of the several ways to reference correct scope is to use variable pointing to outer this:
this.saveBoardAndNotes = function (board_id, board_name) {
var noteList = new Array();
var self = this;
$(".optimal-sticky-notes-sticker-note").each(function(){
// Replace plain urls with links and improve html
var note = $(this);
content = note.find(".textarea").html();
noteID = note.attr("id");
properties = JSON.stringify(self.getProperties(note));
});
}

Reference issue with Javascript

I have an array of objects cached on client side using JS array.
var scannerDictionary = new Array(); //Holds all scanners unmodified
var modifiedScannerDictionary = new Array(); //Holds all scanners with modified values
The properties of each object is set/changed using GUI and updated in the object. Each object contains list of InputParameters (array of Parameter class containing Name, Value and other members).
Please have a look on GUI.
Below is the code i used to render the controls -
function renderControls(scannerId) {
var currentScanner = modifiedScannerDictionary[scannerId];
//Render Input Parameters
$("#tblInputCriteria").find('tr:gt(5)').remove();
for(var i=0;i<currentScanner.InputParameters.length;i++) {
var propType = currentScanner.InputParameters[i].DataType;
var inParName = currentScanner.InputParameters[i].Name;
switch(propType) {
case 0: //Number
var eRow1 = $("#tblInputCriteria").find('#emptyNumRow').clone();
$(eRow1).removeClass('hidden').attr('id', 'Row_'+currentScanner.InputParameters[i].Name);
$(eRow1).appendTo($('#tblInputCriteria'));
var prop1 = $(eRow1).find('#InNumPropName');
$(prop1).attr('id', 'InNumPropName_'+currentScanner.InputParameters[i].Name);
var propName1 = currentScanner.InputParameters[i].Name;
$(prop1).html(propName1);
var propVal1 = $(eRow1).find('#InNumPropValue');
$(propVal1).attr('id', 'InNumPropValue_'+currentScanner.InputParameters[i].Name);
$(propVal1).val(currentScanner.InputParameters[i].Value);
$(propVal1).blur(function () {
if(!ValidateNumber(this, propName1)) {
alert('Value should be numeric in ' + propName1);
setTimeout(function() {$(propVal1).focus();}, 100);
}else {
UpdateData(currentScanner.Id, propName1, $(propVal1).val());
}
});
break;
case 1: //String
var eRow2 = $("#tblInputCriteria").find('#emptyStrRow').clone();
$(eRow2).removeClass('hidden').attr('id', 'Row_'+currentScanner.InputParameters[i].Name);
$(eRow2).appendTo($('#tblInputCriteria'));
var prop2 = $(eRow2).find('#InStrPropName');
$(prop2).attr('id', 'InStrPropName_'+currentScanner.InputParameters[i].Name);
var propName2 = currentScanner.InputParameters[i].Name;
$(prop2).html(propName2);
var propVal2 = $(eRow2).find('#InStrPropValue');
$(propVal2).attr('id', 'InStrPropValue_'+currentScanner.InputParameters[i].Name);
$(propVal2).val(currentScanner.InputParameters[i].Value);
$(propVal2).blur(function () {
UpdateData(currentScanner.Id, propName2, $(propVal2).val());
});
break;
case 2: //Boolean
var eRow3 = $("#tblInputCriteria").find('#emptyBoolRow').clone();
$(eRow3).removeClass('hidden').attr('id', 'Row_'+currentScanner.InputParameters[i].Name);
$(eRow3).appendTo($('#tblInputCriteria'));
var prop3 = $(eRow3).find('#InBoolPropName');
$(prop3).attr('id', 'InBoolPropName_'+currentScanner.InputParameters[i].Name);
var propName3 = currentScanner.InputParameters[i].Name;
$(prop3).html(propName3);
var propVal3 = $(eRow3).find('#InBoolPropValue');
$(propVal3).attr('id', 'InBoolPropValue_'+currentScanner.InputParameters[i].Name);
$(propVal3).val(currentScanner.InputParameters[i].Value);
$(propVal3).blur(function () {
UpdateData(currentScanner.Id, propName3, $(propVal3).val());
});
break;
}
}
}
PROBLEM:
The problem here is of the variables inside switch working as reference variable. So the UpdateData() function gets the last Name for similar type properties. i.e. if fields are of Number type then only the last property is updated by UpdateData() method.
Can anybody help me out solve this issue. Thanks for sharing your time and wisdom.
Try something like the following. Its a tad overkill, but will bind the values of the variables to the closures.
var fnOnBlur = (function(thePropName, thePropVal) {
return function () {
if(!ValidateNumber(this, thePropName)) {
alert('Value should be numeric in ' + thePropName);
setTimeout(function() {$(thePropVal).focus();}, 100);
}else {
UpdateData(currentScanner.Id, thePropName, $(thePropVal).val());
}
};
})(propName1, propVal1);
$(propVal1).blur( fnOnBlur );
The link that Felik King supplied has much more detailed discussion.

Categories

Resources