Using method in javascript - javascript

Supouse I have the next method to insert a value in a cell of a table:
var objeto = {
row: function(rowId){
return {
cell: function(cellClass){
return {
set: function(val){
document.querySelector(rowId+' '+cellClass).innerHTML = val;
}
}
//Or just return the value
}
}
}
};
I know to insert the value, I need use:
objeto.row('#rowid').cell('.cellclass').set('hello');
But If I want to get the cell value using:
var data = objeto.row('#rowid').cell('.cellclass');
What's the method I need to use?

You can use the following structure:
var objeto2 = {
row: function(rowId){
return {
cell: function(cellClass){
return {
value: function(val){
if (val) {
document.querySelector(rowId + ' ' + cellClass).innerHTML = val;
} else {
return document.querySelector(rowId + ' ' + cellClass).innerHTML;
}
}
}
}
}
}
};
objeto.row('#rowid').cell('.cellclass').value('hello'); // set value
objeto.row('#rowid').cell('.cellclass').value(); // get value

The problem is return type of any function is fixed. That means if it returns object always object will be return from that function. What you want to do is you try to benefit from polymorpholism but its cannot be done in such way.
var objeto = {
row: function(rowId){
return {
cell: function(cellClass,newValue){
this.set = function (newValue){
if(typeof newValue != "undefined"){
document.querySelector(rowId+' '+cellClass).innerHTML = val;
}
}
this.set(newValue);
return document.querySelector(rowId+' '+cellClass).innerHTML;
}
//Or just return the value
}
}
}
};
So you can use
var currentValue = objeto.row('#rowid').cell('.cellclass','hello');
or
var currentValue = objeto.row('#rowid').cell('.cellclass');

Related

Method chaining with Javascript - Plus Minus Question

I got a code challenge that create a plus-minus functions using method chaining. i have created the code as follows but it eventually failed when it comes to the output rendering
like plus(3).minus(2).value() and minus(3).minus(3).value() kind of method invoking
code as follows
function plus(valuez)
{
this.valuez = this.valuez+ valuez;
function value{
return valuez
}
plus.minus = minus;
plus.value = value;
plus.plus = this;
return this;
}
function minus(valuez)
{
this.valuez = this.v+ valuez;
function value(){
return valuez
}
minus.plus = plus;
minus.minus = this
minus.value = value;
return this;
}
expected output is
1 and 6 but I only get the printed last number entered. how can I resolve this?
class Box {
constructor(v) { this._value = v }
plus(v) { this._value += v; return this; }
minus(v) { this._value -= v; return this; }
value() { return this._value; }
}
function plus(v) { return new Box(v) }
function minus(v) { return new Box(-v) }
console.log("plus(3).minus(2).value()", plus(3).minus(2).value());
console.log("minus(3).minus(3).value()", minus(3).minus(3).value());
function plus (x) { return { _value: x, plus(y){ return plus(this._value+y) }, minus(y){ return plus(this._value-y) }, value(){ return this._value } } }
function minus(x) { return plus(-x) }
console.log("plus(3).minus(2).value()", plus(3).minus(2).value());
console.log("minus(3).minus(3).value()", minus(3).minus(3).value());
Using closure
function plus (x) { return { plus(y){ return plus(x+y) }, minus(y){ return plus(x-y) }, value(){ return x } } }
function minus(x) { return plus(-x) }
console.log("plus(3).minus(2).value()", plus(3).minus(2).value());
console.log("minus(3).minus(3).value()", minus(3).minus(3).value());
Without a constructor or class-sugar you can return an object using closed over values from the addition/subtraction method. Something like:
const { plus, val, reset } = PM();
console.log(plus().plus(3).minus(4).plus(25).plus(4).trace());
console.log(reset().minus(2).plus(33).val());
function PM() {
let values = [];
let traced = [];
const reset = () => {
values = [];
traced = [];
return ret;
};
const add = value => {
const calc = +(values[values.length-1] || 0) + value;
traced.push(`${values[values.length-1] || 0}${
!value || value >= 0 ? "+" : ""}${value || 0}=${calc || 0}`);
values.push(calc || 0);
return ret;
};
const ret = {
plus: value => add(value),
minus: value => add(-value),
val: () => values,
trace: () => traced,
reset,
};
return ret;
}
Or combine this with an embedded constructor

How to replace style property with proxy

I have code like this in my unit tests for jQuery Terminal:
// https://github.com/tmpvar/jsdom/issues/135
Object.defineProperties(window.HTMLElement.prototype, {
offsetLeft: {
get: function() { return parseFloat(window.getComputedStyle(this).marginLeft) || 0; }
},
offsetTop: {
get: function() { return parseFloat(window.getComputedStyle(this).marginTop) || 0; }
},
offsetHeight: {
get: function() { return parseFloat(window.getComputedStyle(this).height) || 0; }
},
offsetWidth: {
get: function() { return parseFloat(window.getComputedStyle(this).width) || 0; }
},
// this will test if setting 1ch change value to 1ch which don't work in jsdom used by jest
style: {
get: function() {
if (this.__style) {
return this.__style;
}
var self = this;
var attr = {};
function set_style_attr() {
var str = Object.keys(attr).map((key) => `${key}: ${attr[key]}`).join(';') + ';';
self.setAttribute('style', str);
}
var mapping = {
backgroundClip: 'background-clip',
className: 'class'
};
var reversed_mapping = {};
Object.keys(mapping).forEach(key => {
reversed_mapping[mapping[key]] = key;
});
return this.__style = new Proxy({}, {
set: function(target, name, value) {
name = mapping[name] || name;
if (!value) {
delete target[name];
delete attr[name];
} else {
attr[name] = target[name] = value;
}
set_style_attr();
return true;
},
get: function(target, name) {
if (name === 'setProperty') {
return function(name, value) {
attr[name] = target[name] = value;
set_style_attr();
};
} else {
return target[name];
}
},
deleteProperty: function(target, name) {
name = reversed_mapping[name] || name;
delete target[name];
delete attr[name];
set_style_attr();
}
});
}
}
});
It works for 1ch attribute in my tests that look like this:
it('should handle wider characters without formatting', function() {
var input = 'ターミナルウィンドウは黒[[;;]です]';
var string = $.terminal.format(input, {char_width: 7});
expect(string).toEqual('<span style="width: 24ch"><span style="widt'+
'h: 24ch">ターミナルウィンドウは黒</span></span'+
'><span style="width: 4ch" data-text="です">'+
'<span style="width: 4ch">です</span></span>');
});
If I don't use my Proxy I got width in pixels, because I have code like this to check if ch is supported in my code:
var agent = window.navigator.userAgent;
var is_IE = /MSIE|Trident/.test(agent) || /rv:11.0/i.test(agent);
var is_IEMobile = /IEMobile/.test(agent);
// -------------------------------------------------------------------------
var is_ch_unit_supported = (function() {
if (is_IE && !is_IEMobile) {
return false;
}
var div = document.createElement('div');
div.style.width = '1ch';
return div.style.width === '1ch';
})();
The problem is when I want to check the style property to get some value, like this test:
it('should find inside formatting', function() {
term.less(big_text.concat(['[[;red;]foo bar baz]']));
search('bar');
var spans = term.find('[data-index="0"] > div:first-child span');
['foo ', 'bar', ' baz'].forEach(function(string, i) {
expect(a0(spans.eq(i).text())).toEqual(string);
});
[true, false, true].forEach(function(check, i) {
console.log(spans.get(i).style.getPropertyValue('color'));
expect([i, !!spans.get(i).attr('style').match(/color:\s*red/)]).toEqual([i, check]);
});
});
I've tried:
spans.get(i).style.getPropertyValue('color')
This return error that's not a function and
spans.get(i).attr('style')
is undefined. This also don't work
spans.get(i).getAttribute('style')
which should be the same and the one before.
Is there a way to have ch unit support check work but in same way getting values from style attribute as well?
I'm using jest framework that use jsDom I running my tests from Node.
I've tried to create getPropertyValue function in get trap for the proxy but I don't know how to get original function so I can call it.
So to sum up, I need solution in jsDOM that allow to set width to 1ch and get that value back (so my code don't change), and that should work when creating new HTMLElement in DOM and get it's value out of, It don't need to be style object with props it can be style property as string. Alternative solution is to test if ch unit is supported that will work in jsDOM.
I've solved the issue by temporarily disabling getter when accessing properties:
(function() {
var style_descriptor = Object.getOwnPropertyDescriptor(window.HTMLElement.prototype, 'style');
Object.defineProperties(window.HTMLElement.prototype, {
offsetLeft: {
get: function() { return parseFloat(window.getComputedStyle(this).marginLeft) || 0; }
},
offsetTop: {
get: function() { return parseFloat(window.getComputedStyle(this).marginTop) || 0; }
},
offsetHeight: {
get: function() { return parseFloat(window.getComputedStyle(this).height) || 0; }
},
offsetWidth: {
get: function() { return parseFloat(window.getComputedStyle(this).width) || 0; }
},
// this will test if setting 1ch change value to 1ch which don't work in jsdom used by jest
style: {
get: function getter() {
if (this.__style) {
return this.__style;
}
var self = this;
var attr = {};
function set_style_attr() {
var str = Object.keys(attr).map((key) => `${key}: ${attr[key]}`).join(';') + ';';
self.setAttribute('style', str);
}
var mapping = {
backgroundClip: 'background-clip',
className: 'class'
};
var reversed_mapping = {};
Object.keys(mapping).forEach(key => {
reversed_mapping[mapping[key]] = key;
});
function disable(fn) {
// temporary disable proxy
Object.defineProperty(window.HTMLElement.prototype, "style", style_descriptor);
var ret = fn();
Object.defineProperty(window.HTMLElement.prototype, "style", {
get: getter
});
return ret;
}
return this.__style = new Proxy({}, {
set: function(target, name, value) {
name = mapping[name] || name;
if (!value) {
delete target[name];
delete attr[name];
} else {
attr[name] = target[name] = value;
}
set_style_attr();
disable(function() {
self.style[name] = name;
});
return true;
},
get: function(target, name) {
if (name === 'setProperty') {
return function(name, value) {
attr[name] = target[name] = value;
set_style_attr();
};
} else if (target[name]) {
return target[name];
} else {
return disable(function() {
return self.style[name];
});
}
},
deleteProperty: function(target, name) {
name = reversed_mapping[name] || name;
delete target[name];
delete attr[name];
disable(function() {
delete self.style[name];
});
set_style_attr();
}
});
}
}
});
})();

"Undefined" in the end of the accumulator.value()

I ran into the problem of the value misplacement in the constructor function method this.result. I do not understand why I'm get the result of the end of the function - undefined...
Tell me please, what is forgotten to include in the function :(
function Accumulator(startingValue) {
this.startingValue = startingValue;
this.read = function() {
this.a = +prompt('Your digit: ', '');
};
this.value = function() {
this.value += this.a;
};
this.result = function() {
return this.value + this.startingValue;
}
}
var accumulator = new Accumulator(1); // starting value 1
accumulator.read(); // sum prompt with current value
accumulator.read(); // sum current prompt with previous prompt and current value
console.log( accumulator.result() ); // display sum result
If .value is supposed to be an integer, don't define it as a function :-)
I think you should drop .value(), .startingValue and .a and just use .value everywhere. Put the summation directly into the read method. Simplify to:
function Accumulator(startingValue) {
this.value = startingValue;
this.read = function() {
// the temporary variable might be unnecessary but I left it in for verbosity
const a = +prompt('Your digit: ', '');
this.value += a;
};
this.result = function() {
return this.value;
};
}
var accumulator = new Accumulator(1); // starting value 1
accumulator.read(); // add prompt to current value
accumulator.read(); // add another prompt to current value
console.log( accumulator.result() ); // display sum by calling result() method
You might also want to define the methods on the prototype:
function Accumulator(startingValue) {
this.value = startingValue;
}
Accumulator.prototype.read = function() {
this.value += +prompt('Your digit: ', '');
};
Accumulator.prototype.result = function() {
return this.value;
};
and even use modern class syntax, as #ArtificialBug suggested:
class Accumulator {
constructor(startingValue) {
this.value = startingValue;
}
read() {
this.value += parseInt(prompt('Your digit: ', ''), 10);
}
result() {
return this.value;
}
}
There are two problems
this.value = function() {
this.value += this.a; //this.value is a function
};
and
console.log( accumulator.value ); // accumulator value is a function which needs to be invoked
Make it
function Accumulator(startingValue) {
this.startingValue = startingValue;
this.read = function() {
this.a = (this.a || this.startingValue ) + +prompt('Your digit: ', '');//initialize and add the prompted value
};
this.value = function() {
return this.a; //simply return the value
};
this.result = function() {
return this.a + this.startingValue; //this.a instead of this.value
}
}
var accumulator = new Accumulator(1);
accumulator.read();
accumulator.read();
console.log( accumulator.value() ); // invoke the method

How to chain in javascript yet get value in between

ok weird title, I know. However the question is simple. In a class I want to be able to do these two things:
invoice.getAmount(); // returns 1000
and
invoice.getAmount().asCurrency(); // returns $1000.00
I can do either, just don't know how to get both to work.
What I have for now for the second idea:
getAmount() {
this._temp = this.amount;
return this;
}
asCurrency(){
if(this._temp){
return "$" + this._temp + ".00";
}
}
That's an ugly copy of what I really have but the concept is represented...
Any idea?
Thanks
Trick is to use the valueOf() method.
class Invoice {
constructor(value) {
this.value = value;
}
getAmount() {
return {
valueOf: _ => this.value,
asCurrency: _ => '$' + this.value
}
}
}
const i = new Invoice(150);
console.log(i.getAmount() + 10); // 160
console.log(i.getAmount().asCurrency()); // '$150'
You can use Number.prototype.toLocaleString():
function getAmount() {
return 1000;
}
var result = getAmount().toLocaleString('en-EN', {style: 'currency', currency: 'USD'});
console.log(result);
You can override a few built-ins (toString() and valueOf()) on the Invoice.prototype like so:
function Invoice(amount) {
this.amount = amount;
}
Invoice.prototype.toString =
Invoice.prototype.valueOf = function valueOf() {
return this.value;
};
Invoice.prototype.getAmount = function getAmount() {
this.value = this.amount;
return this;
};
Invoice.prototype.asCurrency = function asCurrency() {
this.value = '$' + this.value.toFixed(2);
return this;
};
var invoice = new Invoice(1000);
console.log(Number(invoice.getAmount()));
console.log(String(invoice.getAmount().asCurrency()));
// or more subtly
console.log(invoice.getAmount() + 0);
console.log(invoice.getAmount().asCurrency() + '');
Or using ES6 class:
class Invoice {
constructor(amount) {
this.amount = amount;
}
toString() {
return this.value;
}
valueOf() {
return this.value;
}
getAmount() {
this.value = this.amount;
return this;
}
asCurrency() {
this.value = '$' + this.value.toFixed(2);
return this;
}
}
var invoice = new Invoice(1000);
console.log(Number(invoice.getAmount()));
console.log(String(invoice.getAmount().asCurrency()));
// or more subtly
console.log(invoice.getAmount() + 0);
console.log(invoice.getAmount().asCurrency() + '');
Remember that getAmount is returning a number.
Thus, there is no way to chain asCurrency on the return value of getAmount (without using valueOf) unless asCurrency exists on the Number prototype.
If you wanted to keep all this composed in your class without modifying the Number prototype, you need to either use valueOf (best solution), or make your return value of getAmount your class instance so that you can chain it with asCurrency.

Protractor- Retrieve value of global array in a function

I am trying to push array content in a global defined empty array & then retrieve its contents inside another function.
Below is the code which i tried:
describe('My Test', function() {
var arrayf3=[];
var indexf3='not found';
it('Test starts', function() {
browser.ignoreSynchronization = true;
browser.get('https://www.w3schools.com/angular/');
var elm = element(by.id('leftmenuinner')).all(By.css('[target="_top"]'));
elm.count().then(function(count) {
Methods.pushToArray(0, count, elm);
})
var texttocheck='Data Binding';
Methods.getIndex(0, arrayf3.length, arrayf3, texttocheck);
console.log('Text content of global array is ' + arrayf3);
console.log('index of the array number having texttofind is ' + indexf3);
})
var Methods = {
getIndex :function (i, max, array, texttocheck) {
if (i < max) {
console.log('text[' + i + '].indexOf = ' + array[i].indexOf(texttocheck))
if (array[i].indexOf(texttocheck) > 0) {
indexf3 = i;
} else {
Methods.getIndex(i + 1, max, array, texttocheck);
}
}
},
pushToArray :function (i, max, elm) {
if (i < max) {
elm.get(i).getText().then(function(tmpText) {
console.log("The array "+tmpText);
arrayf3.push(tmpText);
})
Methods.pushToArray(i + 1, max, elm);
}
},
}
});
Problem is i am getting null values for below placeholder values:
Text content of global array is
index of the array number having texttofind is
I want the array value copied in this global empty array to be used & displayed in the same it block function 'Test starts'
Protractor's element.all inherently knows how to getText() on each of the elements and return the values as an array.
it('Test starts', function() {
browser.ignoreSynchronization = true;
browser.get('https://www.w3schools.com/angular/');
var getIndexOfElementByPartialText = function(inputText) {
return element(by.id('leftmenuinner')).all(by.css('[target="_top"]')).getText().then(function(values) {
var indexNumber;
values.forEach(function(value, index) {
if (new RegExp(inputText).test(value)) {
if (indexNumber === undefined) {
indexNumber = index;
} else {
throw new Error('multiple elements match the input text');
}
}
});
if (indexNumber === undefined) {
throw new Error('no elements match the input text');
} else {
return indexNumber;
}
});
});
expect(getIndexOfElementByPartialText('thing1').toBe(1);
expect(getIndexOfElementByPartialText('thing2').toBe(2);
});
Edited the answer to provide it in terms of a re-usable function.

Categories

Resources