not getting parseInt to work on knockout.js (NaN error) - javascript

I'm trying to do some calculations through knockout.js but I'm getting a NaN error.
This is the code:
function AppViewModel() {
aanvoerSl25mm = ko.observable();
lengtePrijs125mm = 1;
totaalAanvoer = parseInt(aanvoerSl25mm*lengtePrijs125mm);
}
ko.applyBindings(new AppViewModel());
totaalAanvoer gives me NaN as output.
Here is a fiddle:
http://jsfiddle.net/FullContCoder/3TUsp/2/
Any help would be fantastic. Thanks!

There are a few problems:
As attila said, aanvoerSl25mm is a function (observables are functions), so you need to get the value out using aanvoerSl25mm()
You haven't provided aanvoerSl25mm with a value, so its current value is undefined. You should create it with a value such as aanvoerSl25mm = ko.observable(0);
You are only calculating the value once (immediately). What you want to do is to make a computed function (see the documentation here). This will mean the computed value will change automatically when the observable changes. For example:
totaalAanvoer = ko.computed(function() {
return parseInt(aanvoerSl25mm()*lengtePrijs125mm);
});

Related

Vue JS - Loop through a computed property to display a list of years

I currently have the following code that lists a list of years. I feel that all this code may be very unnecessary and perhaps a computed property for validYears, would help me make this code more optimal and get rid of the unnecessary watchers. My issue is converting this to a computed property as I'm failing to grasp the correct logic to achieve this. I'd appreciate if someone can offer an example of how I can set a computed property for valid years and still return the same result.
onBeforeMount(calculateDateRange)
watch(() => props.earliestDate, (newValue, prevValue) => {
calculateDateRange();
});
// If there is a new value passed from the parent, the dropdown should display that new value.
watch(() => props.latestDate, (newValue, prevValue) => {
calculateDateRange()
});
const validYears = ref([])
function calculateDateRange () {
for(let year = props.latestDate; year >= props.earliestDate; year--){
validYears.value.push(year)
}
}
I didn't provide the rest of the code not to clutter the question, but as one can see in this component I have a set of props that determine the values in my for loop.
You could optimize it as follows :
const validYears = computed(()=>{
let _years=[]
for(let year = props.latestDate; year >= props.earliestDate; year--){
_years.push(year)
}
return _years;
})

I'm trying to use jquery to create a div containing columns but I can't get my array to format correctly

I have an array that contains dates. and for some reason I can't get it to show on my screen I've been debugging for a few days now and I've tracked it down to a single line, but the line has worked before and I can't figure out what the issue might be.
The array looks like this:
var selectItems =
[ "05-26-2017", "06-02-2017", "06-09-2017",
"06-16-2017", "06-23-2017", "06-30-2017", "07-07-2017", "07-14-2017",
"07-21-2017", "07-28-2017"...];
It's passed as an argument from another function, but that's how it's showing in console.log().
I might be going about this the wrong way, maybe even a lot further around then I need to but this is what I've come up with:
1. function setTHead(selectItems) {
2 var formatString;
3. for (var x = 0; x < 12; x++) {
4. formatString = selectItems[x].replace(/[^0-9/-]/g, "").toString();
5. console.log(selectItems);
6. $('#datTab').append("<div id='col" + x + "' class='column'>'" + formatString + "'</div>");
7. }
8. }
the array up top is what's showing from the console.log 5 lines down.
the sixth line is what is seeming to give me issues. Nothing is put on the page at all.
I'm getting a console error saying:
jQuery.Deferred exception: selectItems is undefined setTHead#http://localhost/mySite/script.js:136:9
startUp2#http://localhost/mySite/script.js:146:5
#http://localhost/mySite/table.php:19:9
mightThrow#http://localhost/mySite/lib/jquery.js:3586:52
resolve/</process<#http://localhost/mySite/lib/jquery.js:3654:49
setTimeout handler*resolve/<#http://localhost/mySite/lib/jquery.js:3692:37
fire#http://localhost/mySite/lib/jquery.js:3320:30
fireWith#http://localhost/mySite/lib/jquery.js:3450:29
fire#http://localhost/mySite/lib/jquery.js:3458:21
fire#http://localhost/mySite/lib/jquery.js:3320:30
fireWith#http://localhost/mySite/lib/jquery.js:3450:29
ready#http://localhost/mySite/lib/jquery.js:3923:13
completed#http://localhost/mySite/lib/jquery.js:3933:9
EventListener.handleEvent*#http://localhost/mySite/lib/jquery.js:3949:9
#http://localhost/mySite/lib/jquery.js:39:9
#http://localhost/mySite/lib/jquery.js:17:3
undefined
followed by:
TypeError: selectItems is undefined
and thats pointing to line 6.
if anyone has any advice I would be very much appreciative. Thank you in advance.
EDIT: A little more code:
function startTblView(defSel) {
if (defSel === true) {
setCookie('defSel', true, 7);
} else{
setCookie('defSel', false, 7);
}
saveSelected();
window.open('table.php', '_self');
defSel = getCookie('defSel');
if (defSel) {
selectItems = getDefDates();
}else {
selectItems = reGetSelected();
}
setTHead(selectItems);
}
defSel, is a boolean passed from my last page stating whether I'm doing a default view or a custom view, the custom view is passed from saveSelected();
saveSelected is a function for just saving the selected global value as a cookie so I can pull it out on the next page.
getDefDates pulls the default values for the array
reGetSelected, gets the selected array from the cookie.
I apologize for wonky naming conventions. I'm the only one working on this site and I'm just making sure the names don't overlap.
You can do this :
HTML code
<div id="datTab"></div>
JS code
var selectItems =
[ "05-26-2017", "06-02-2017", "06-09-2017",
"06-16-2017", "06-23-2017", "06-30-2017", "07-07-2017", "07-14-2017",
"07-21-2017", "07-28-2017"];
function setTHead(selectItems) {
var formatString;
$.each( selectItems, function( index, value ){
formatString = value.replace(/[^0-9/-]/g, "").toString();
$('#datTab').append("<div id='col" + index + "' class='column'>'" + value + "'</div>");
});
};
You can use $.each, its better than 'for' with javascript.
The .each() method is designed to make DOM looping constructs concise
and less error-prone. When called it iterates over the DOM elements
that are part of the jQuery object. Each time the callback runs, it is
passed the current loop iteration, beginning from 0. More importantly,
the callback is fired in the context of the current DOM element, so
the keyword this refers to the element.
I did a JsFiddle
Here.

knockoutJS activate/deactivate value input

few weeks ago I've started to work with typescript and knockoutJS, I have a specific problem and yet I have solution for it, it's so ugly I can't stand that, but can't get anything better from it, there's too much code to be pasted, but i'll try to describe my problem the best I can:
I have two view models that communicate with the same data model. Let's say that model is an array of Simple Objects called Numbers. Every Number has following properties: Value, isMinValueEnabled, minValue, isMaxValueEnabled, maxValue, isStepEnabled, stepValue, valueFormat. valueFormat might be numeric or percentage (so that value, min, max and step are multiplied by 100). I can activate minimum, maximum and step values and deactivate them. Then save data to the model and do exactly the same (with some restrictions) in another viewModel.
The problem is with those optional parameters and percentage values, because when I'm reading data I firstly check if Number is percentage or not and if every property is Enabled. Then I eventually multiply value by 100 if it is set. I have to do the same operation when I'm saving data, that is check every number for format and is*Enabled and eventually divide by 100. With 3-4 properties there is no problem, but now I have to write few more optional properties that depend's on the format and enabled/disabled state and I'm getting into troubles with ton's of if's statements, I myself can't even read that. Is there some better patter that can be used in this situation?
EDIT
Ok, so things look like this: I have a series of numbers, they can look like:
100, 2 000, 34 000.21, 2.1k, 2.11M, 22% but those are only display values whereas real values should stand like this for the example given: 100, 2000, 34000.21, 2100, 2110000, 0.22. The user can edit the value to anything else, like, let's say has 22% in input and then edit this into 1k. I shall convert 1k to original value which is 1000 and check if minimumValue and maximumValue for that number are set. If they are, I will check, and let's say maxValue is 800, then user input can no longer be 1k, but 0.8k instead because he can not get out of maximumValue. MinimumValue, MaximumValue, StepValue and so on are properties of every single Number. I was playing with ko.pureComputed, but I need to abstract it somehow:
var f = ko.computed(<KnockoutComputedDefine<number>>{
read: ...
write: ...
});
What I have now is totally ugly and looks like this:
export class Variable {
[...]
public inputType: KnockoutObservable<VariableInputType>;
public typeAndFormat: KnockoutObservable<DataTypeFormat>;
public isMinEnabled: KnockoutObservable<boolean>;
public minValue: KnockoutObservable<number>;
public isMaxEnabled: KnockoutObservable<boolean>;
public maxValue: KnockoutObservable<number>;
public isStepEnabled: KnockoutObservable<boolean>;
public stepValue: KnockoutObservable<number>;
public value: KnockoutObservable<number>;
[...]
constructor(...) {
[...]
this.inputType = ko.observable(VariableInputType.Input);
this.typeAndFormat = ko.observable(variable.typeAndFormat || DataTypeFormat.Number);
if (variable.minValue !== null) {
this.isMinEnabled = ko.observable(true);
this.minValue = ko.observable(variable.minValue);
} else {
this.isMinEnabled = ko.observable(false);
this.minValue = ko.observable(null);
}
if (variable.maxValue !== null) {
this.isMaxEnabled = ko.observable(true);
this.maxValue = ko.observable(variable.maxValue);
} else {
this.isMaxEnabled = ko.observable(false);
this.maxValue = ko.observable(null);
}
if (variable.step !== null) {
this.isStepEnabled = ko.observable(true);
this.stepValue = ko.observable(variable.step);
} else {
this.isStepEnabled = ko.observable(false);
this.stepValue = ko.observable(null);
}
if (variable.defaultValue !== null) {
this.value = ko.observable(variable.defaultValue);
} else {
this.value = ko.observable(0);
}
if (this.typeAndFormat() === DataTypeFormat.NumberPercentage) {
this.value(this.value() * 100);
if (this.isMinEnabled()) this.minValue(this.minValue() * 100);
if (this.isMaxEnabled()) this.maxValue(this.maxValue() * 100);
if (this.isStepEnabled()) this.stepValue(this.stepValue() * 100);
}
[...]
this.isMinEnabled.subscribe((v) => { if (v !== true) this.minValue(null) }, this);
this.isMaxEnabled.subscribe((v) => { if (v !== true) this.maxValue(null) }, this);
this.isStepEnabled.subscribe((v) => { if (v !== true) this.stepValue(null)}, this);
[...]
}
public getModifiedVariable() {
[...]
this.originalData.typeAndFormat = this.typeAndFormat();
this.originalData.minValue = this.minValue();
this.originalData.maxValue = this.maxValue();
this.originalData.step = this.stepValue();
this.originalData.defaultValue = this.value();
[...]
if (this.typeAndFormat() === DataTypeFormat.NumberPercentage) {
this.originalData.defaultValue = this.originalData.defaultValue / 100;
if (this.isMinEnabled()) this.originalData.minValue = this.originalData.minValue / 100;
if (this.isMaxEnabled()) this.originalData.maxValue = this.originalData.maxValue / 100;
if (this.isStepEnabled()) this.originalData.step = this.originalData.step / 100;
}
[...]
return this.originalData;
};
[...]
}
The second viewmodel that has even more validation and restrictions looks even worse... I don't really know how I could abstract that so that it would be readable for me and for others.
There are two different problems
you need a custom visualization, that needs formatting, and a custom data input, that needs parsing
you need to add some business logic (validation of values)
The first question can be solved by using an extender. With this technique your observable must store the actual value, not the formatted value. You can use it to add a child observable, which could be called formattedValue. This must be a writable computed observable, which two functions:
read: format the underlying actual value, and return it formatted, so that the user has a beautiful view of the value
write: parse the value received from the user input, and store the result in the underlying actual value
You can find examples of extenders like theses ones: Three Useful Knockout Extenders. The extenders can recevie parameters, so that they can be configured individually (in your case you can set percentage, steps, and so on). Another big example of this technique is the ko.valdiation library.
If you use this technique, in the HTML you need to bind the child observable, instead of the underlying observable with the real value, i.e.:
<input type="text" data-bind="value: vm.someValue.formattedValue"/>
As explained, the formattedValue is a new child observable which formats/parses the value.
The second question can also be solved with writable computed observables. You can add the validation logic in the write method, so that any time the value is modified, it's validated, and rejected or corrected, depending on what you want to do. The computed observable can access other values from the view model, so its implementation should be easy. Of course, the validation logic must access the observables with the actual values. I.e it can completely ignore if the observable is extended or not.
The great advantage of this implementation is that you can implement an test each required functionality independently:
implement and test the parsing/formatting extenders, the format/parse in the extenders
implementa and test the the business logic in the writable computed observables
Once implemented an tested, start using them together.

Knockout updating decimal places from observable

I have an observable item and I am trying to apply user driven formatting on that observable. I have an integer input box where a user can select how many decimal places, 0-6. I am trying to update the observable to apply decimal places based on the selection in that input box.
I first tried a computed value, which did not work. Returned an error that 'toFixed' was not a function.
this.formattedResult = ko.computed(function () {
var newValue = self.decimalValue();
var precision = self.decimalPlaces();
return newValue.toFixed(precision);
});
I then tried adding a binding handler, which results in 'uncaught object' on the 'toFixed'
ko.bindingHandlers.numericText = {
update: function (element, valueAccessor, allBindingsAccessor) {
var value = new String(ko.utils.unwrapObservable(valueAccessor()).toString());
var precision = ko.utils.unwrapObservable(allBindingsAccessor().precision);
var formattedValue = value.toFixed(precision);
ko.bindingHandlers.text.update(element, function () { return formattedValue; });
},
defaultPrecision: 1
};
Then I tried extending numeric with a numericText, which also results in a 'uncaught object' error on the 'toFixed'.
ko.extenders.numeric = function (target, precision) {
if (precision() != null) {
var precisionValue = target();
console.log(precisionValue);
var precisionDecimal = precisionValue.toFixed(2);
console.log(precisionDecimal);
var result = ko.dependentObservable({
read: function () {
return target().toFixed(precision());
},
write: target
});
result.raw = target;
return result;
}
else
return target;
};
The binding on the HTML elements were all updated to take these changes into account but they did not work. I also tried just simply adding 'toFixed' to the data binding in the HTML and that also did not work.
I found these similar questions but the solutions are not working for me.
Format knockout observable with commas and/or decimal places within html data binding, NOT in viewmodel
Formatting rules for numbers in KnockoutJS
Adding decimal formatting to Knockout number data bindings
This is a very frustrating problem as I thought it would be really simple to add decimal places in a computed function. What am I doing wrong?
I'm not sure if it matters but I am using knockout 3.1
Edit:
I also tried just simply adding 'toFixed' to the data-binding but that also results in an uncaught object.
<p data-bind="text: decimalValue().toFixed(2)"></p>
I didn't end up solving my problem in the way that I had anticipated but I modified the computed column to force the decimal to a float and then added the decimals to that float value.
The final function looks like this:
this.formattedResult = ko.computed(function () {
var newValue = parseFloat(self.decimalValue());
var precision = self.decimalPlaces();
return newValue.toFixed(precision);
});
I don't know why this works but the other does not? Maybe when I assign the value to newValue I'm just assigning an observable to it and the observable doesn't know how to handle toFixed()? Doesn't explain all of the other problems but at least this is fixed for me.
I recommend using the following JavaScript Number constructor which will return NaN if the number is invalid.
this.formattedResult = ko.computed(function () {
// call the number constructor. newValue is now a number or NaN
//we choose a default value as well.
var newValue = isNaN(self.decimalValue()) ? 0 :
Number(self.decimalValue());
// call the number constructor again
var precision = isNaN(self.decimalPlaces()) ? 0 : Number(self.decimalPlaces());
return newValue.toFixed(precision);
});

Javascript - Overriding property (not methods) inside an Object

Let us explain the question with an example. I have a text box. The textbox (every textbox) has a property called 'value'. I want to over ride that textbox.value and comeup with and
new thing. When the text in textbox is 'ranjan' then the textbox.VALUE property returns 'ranjan'. Now I want to thus overwrite this so that when you type textbox.VALUE you get a different thing say for example, RaNjAn or say, Mr. Ranjan or whatever.
We can over ride methods using Object.PROTOTYPE property. But how can we do it for non-function objects inside object for example the 'value' property in this case.
If i need to make the question more clear, please mention.
Regards - Ranjan.
You can define custom properties for your element using Object.defineProperty
If you have a case where you need to get the value of an element as Mr. <value> for example, then this approach will be useful. Overriding standard properties may not be such a good idea.
Demo: http://jsfiddle.net/zvCGw/2/
Code:
var foo = document.getElementById('foo');
Object.defineProperty(foo, "xvalue", {
get: function() {
return 'Mr. ' + foo.value;
},
set: function(_newValue) {
foo.value = _newValue;
}
});
foo.xvalue = 'Hello';
alert(foo.xvalue);
What you are trying to do is called type augmentation. In javscript there are types of things, such as the object type, array type, etc.
You can use the prototype to augment these built in types, for example, adding a new method that can be called on any object that is of the type array:
Array.prototype.myNewMethod = function() {
//the method logic
}
Then you can call your method on any array:
[0,1,2].myNewMethod();
There is no INPUT type in JavaScript, DOM elements are classed as Objects. But you could jerry-rig something together that kind of does what you need, like this
Object.prototype.changeValue = function(el) {
if (el.tagName === "INPUT") {
return "Mr " + el.value;
}
}
var testEl = document.getElementById("test");
document.write(testEl.changeValue(testEl))
Used in conjunction with this textbox:
<input id="test" value="Dan" />
You would then get the output 'Mr Dan'
However, this is not great, it's just to illustrate the point and is just something to get you started...
I made a fiddle so you can play around with it
You can redeclare value but it will do no good ;)
This example would do that if test is a textbox
var input = document.getElementById("test");
Object.defineProperty(input, "value", {
get : function () {
return "'" + this["value"] + "'";
},
set : function (val) {
this["value"] = val;
}
});
input.value = "Hello World";
alert(input.value);
Unfortunately, "this.value" will reference the getter causing infinite recursion.
Once redefined, the original value will no longer exist so you will have crippled the element object.
At least as far as I have been able to test.
If the property you're trying to override can also be represented by an HTML attribute (e.g. an input's value), then you can use getAttribute and setAttribute.
Object.defineProperty(myInputElement, 'value', {
get: function () {
return myInputElement.getAttribute('value');
},
set: function (value) {
myInputElement.setAttribute('value', value);
}
});
Note, however, that this override itself cannot be overridden without re-implementing it.

Categories

Resources