How to correctly format currency with Knockoutjs? - javascript

I need to format number as localized currency with Knockoutjs. But I have issues with it. I created a jsfiddle for it. If somebody could be so nice, and check it (not fix it), please write it to me, what should I fix in it.
Example is here:
ko.extenders.formattedMoney = function (target, arg) {
var result = ko.computed({
read: function () {
var targetValue = target().toString().replace(/[^0-9-.]/g, '');
return parseInt(targetValue).toLocaleString('hu-HU') + " HUF";
},
write: target
});
result.raw = target;
return result;
};
function MyViewModel() {
var self = this;
self.minValue = ko.observable(20000);
self.curValue = ko.observable(40000).extend({ formattedMoney: 0 });
};
ko.applyBindings(new MyViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div>
MinValue: <input type="text" data-bind="value: minValue" />
</div>
<div>
CurValue: <input type="text" data-bind="value: curValue" />
</div>
Thank you very much!

Related

Sending only the updated object from ko.observableArray

How can I send only the updated model from an observable Array instead of sending the entire array?
var student = function (){
this.studentId=0;
this.firstName=ko.obserable();
this.lastName=ko.obserable();
}
var course= function (){
this.courseId=0;
this.students=ko.obserableArray([]);
this.Name=ko.obserable();
}
Now I want to get only that particular student from course whose info is updated. Assuming that when we add a new class we can dynamically add new students to it on the go. Supposing that you have to validate the previous student before adding a new one. When I get that particular student I want to send that student info back to the server.
Thanks.
If I understood your task right, you could use "arrayChange" event type to get exact changed (added/removed) items:
sourceArray = ko.observableArray();
sourceArray.subscribe(function (changes) {
changes.forEach(function(arrayChange) {
if(arrayChange.status === 'added') {
// some code on add
} else if(arrayChange.status === 'deleted') {
// some code on delete
}
});
}, null, "arrayChange");
If you want to get list of students which have been modified, you can provide a flag to identify if an object has been modified in student object. Use .subscribe to modify that flag whenever a value is updated. Then use ko.computed or ko.pureComputed to get that list.
Also it supposes to be observable.
var student = function (id, firstName, lastName) {
var self = this;
self.hasChanged = ko.observable(false);
var modified = function(){
self.hasChanged(true);
};
self.studentId = ko.observable(id);
self.firstName = ko.observable(firstName);
self.firstName.subscribe(modified);
self.lastName = ko.observable(lastName);
self.lastName.subscribe(modified);
}
var course= function (){
var self = this;
self.courseId = 0;
self.students = ko.observableArray([new student(1, "Cristiano", "Ronaldo"), new student(2, "Lionel", "Messi")]);
self.modifiedStudent = ko.computed(function(){
return ko.utils.arrayFilter(self.students(), function(student) {
return student.hasChanged();
});
}, self);
self.Name = ko.observable("Programming 101");
}
$(document).ready(function () {
var myViewModel = new course();
ko.applyBindings(myViewModel);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
List of all students:
<div data-bind="foreach: students">
<div>
<span data-bind="text: studentId"></span>
<input type="text" data-bind="value: firstName" />
<input type="text" data-bind="value: lastName" />
</div>
</div>
<br/>
List of students which has been modified:
<div data-bind="foreach: modifiedStudent">
<div>
<span data-bind="text: studentId"></span>
<input type="text" data-bind="value: firstName" readonly />
<input type="text" data-bind="value: lastName" readonly />
</div>
</div>

Create observable returning other observables as a single map

I have an own binding for numeric inputs made in knockoutJS which accepts only numbers.
To make big numbers I declare various instances of number in a NumberField like:
var NumberField = function () {
var self = this;
self.maskFormat = "0";
self.firstNumber = ko.observable("");
self.secondNumber = ko.observable("");
self.thirdNumber = ko.observable("");
};
And
<input id="0" maxlength="1" type="tel" data-bind="numeric: firstNumber">
<input id="1" maxlength="1" type="tel" data-bind="numeric: secondNumber">
<input id="2" maxlength="1" type="tel" data-bind="numeric: thirdNumber">
This is working like a charm, but when I made submission, system is expecting a map with numbers. I achieved it IMHO in an ugly way:
Added to NumberField this attribute:
this.cleanNumber = ko.pureComputed(function () {
return this.firstNumber().toString() + this.secondNumber().toString() + this.thirdNumber().toString();
}, this);
And in the code, when I need to use it I must do this:
let unwrapNumbers = this.numbers().cleanNumber().split("").map(function (item){
return Number(item);
});
This is working, but... I'm pretty sure there is an easier and more straight way.... Any suggestions?
I think it could help to split the computed in to two parts:
Getting the numbers you want to include in order
Creating a string based on the ordered values
Often it makes sense to split a computed in to several pure computeds that have a single clear data processing responsibility.
var NumberField = function () {
var self = this;
self.firstNumber = ko.observable(1);
self.secondNumber = ko.observable(2);
self.thirdNumber = ko.observable(3);
self.orderedNumbers = ko.pureComputed(function() {
return [self.firstNumber,
self.secondNumber,
self.thirdNumber].map(ko.unwrap);
});
self.cleanedNumber = ko.pureComputed(function() {
return self.orderedNumbers().join("");
});
};
var nf = new NumberField();
// If you want the numbers:
console.log(nf.orderedNumbers());
// If you want the string
console.log(nf.cleanedNumber());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
Now, I'm not sure what your requirements are, but you can take it one step further and use an observableArray as the base data format:
var NumberField = function () {
var self = this;
self.numbers = ko.observableArray(
[ko.observable(0), ko.observable(1), ko.observable(2)]);
self.add = function() {
self.numbers.push(ko.observable(self.numbers().length));
}
self.unwrappedNumbers = ko.pureComputed(function() {
return self.numbers().map(ko.unwrap);
});
self.cleanedNumber = ko.pureComputed(function() {
return self.unwrappedNumbers().join("");
});
};
ko.applyBindings(new NumberField());
label { display: block }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="foreach: numbers">
<label>
<span data-bind="text: 'Number ' + $index()"></span>
<input type="number" data-bind="textInput: $parent.numbers()[$index()]">
</label>
</div>
<button data-bind="click: add">add</button>
<pre>
Unwrapped:<code data-bind="text: unwrappedNumbers"></code>
Cleaned:<code data-bind="text: cleanedNumber"></code>
</pre>

2 states roundable numeric text box with knockoutjs

I want to have an html numeric text box with 2 states, when focused, it has to show all decimal places, and when focus is lost, only show 2 decimals. I've almost achieved it.
HTML:
<input data-bind="attr: { 'data-numericvalue': valueToRound}" class="numerictextbox"
type="number"/>
Javascript:
var viewModel = {
valueToRound: ko.observable(7.4267),
};
//NUMERIC TEXTBOX BEHAVIOUR
$('.numerictextbox').focusout(function () {
$(this).attr("data-numericvalue", this.value); //this line does not update the viewModel
this.value = parseFloat($(this).attr("data-numericvalue")).toFixed(2);
});
$('.numerictextbox').focusin(function () {
if ($(this).attr("data-numericvalue") !== undefined) this.value = $(this).attr("data-numericvalue");
});
ko.applyBindings(viewModel);
Jsfiddle: https://jsfiddle.net/7zzt3Lbf/64/
But my problem is that when focusout occurs, it doesn't update bound property, viewModel in this case. This is a simplified version of my code, so I want it to be generic for a lot of properties in my real scenario.
You're mixing in too much jQuery :)
Knockout has event bindings and a hasFocus binding to deal with UI input.
In the example below I've made a viewmodel that has a hidden realValue observable which stores the unmodified input. The displayValue limits this number to a 2 digit number when showDigits is false.
I've used hasFocus to track whether we want to show the whole number: it's linked to showDigits.
var ViewModel = function() {
this.showDigits = ko.observable(true);
var realValue = ko.observable(6.32324261);
this.displayValue = ko.computed({
read: function() {
return this.showDigits()
? realValue()
: parseFloat(realValue()).toFixed(2);
},
write: realValue
}, this);
};
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input data-bind="value: displayValue, hasFocus: showDigits" type="number"/>
Edit: After comment that a computed is too much extra code: here's how to wrap the computed logic in a reusable extender:
ko.extenders.digitInput = function(target, option) {
var realValue = target,
showRealValue = ko.observable(false),
displayValue = ko.computed({
read: function() {
return showRealValue()
? realValue()
: parseFloat(realValue()).toFixed(2);
},
write: realValue
}, this);
displayValue.showRealValue = showRealValue;
return displayValue;
};
var ViewModel = function() {
this.value1 = ko.observable(6.452345).extend({ digitInput: true });
this.value2 = ko.observable(4.145).extend({ digitInput: true });
};
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input data-bind="value: value1, hasFocus: value1.showRealValue" type="number"/>
<input data-bind="value: value2, hasFocus: value2.showRealValue" type="number"/>

Knockout : find out which observable triggerred computed

I have an object with multiple observables. Is there a way in a computed to know which observable changes, therefore which observable fired the computed?
Thank you in advance
Matthew
Without details of exactly what you are trying to achieve, I'll post this in the hope it might help.
A simple way to track changes is to use the .subscribe method on an observable you want to track. Each time the observable gets updated, this method will fire.
self.myValue = ko.observable('initial value');
self.myValue.subscribe(function (item) {
alert('myValue has changed to: ' + item);
});
The item passed in to the subscribe function is optional, so you can use the new value if required.
Here's a simple example of it in use with a computed:
Sample JSFiddle
JS:
var viewModel = function () {
var self = this;
self.firstName = ko.observable('Mod');
self.lastName = ko.observable('dinu');
self.valueChanged = ko.observable('');
self.fullName = ko.computed(function () {
var val = '';
if (self.valueChanged() !== '') {
val = ' (' + self.valueChanged() + ' Changed)';
}
return self.firstName() + ' ' + self.lastName() + val;
});
self.firstName.subscribe(function () {
self.valueChanged('First Name');
});
self.lastName.subscribe(function () {
self.valueChanged('Last Name');
});
};
ko.applyBindings(new viewModel());
HTML:
<div>
<label for="fname">First Name:</label>
<input id="fname" data-bind="value: firstName" />
</div>
<div>
<label for="lname">Last Name:</label>
<input id="lname" data-bind="value: lastName" />
</div>
<hr />
<div>Hello <span data-bind="text: fullName"></span></div>
<hr />
<div>Value Changed: <span data-bind="text: valueChanged"></span></div>

Iterating through array using jquery returns only last object

I am new to jquery and i think this is just a basic problem. `
<input type="text" name="text1" value=""></input>
<input type="text" name="text2" value=""></input>
<input type="text" name="text3" value=""></input>
<input type="text" name="text4" value=""></input>
<input type="text" name="text5" value=""></input>
<input type="submit" value="submit"></input>
<pre id="result">
</pre>
</form>`
This is my html form and i am using following jquery function to produce json object
$.fn.serializeObject = function()
{
var o = {};
var d={};
var a = this.serializeArray();
$.each(a, function(i,n) {
o['name'] = n['name'];
o['content'] =(n['value']);
});
return o;
};
$(function() {
$('form').submit(function() {
$('#result').text(JSON.stringify($('form').serializeObject()));
return false;
});
});
on runnig the above html i am getting the output {"name":"text5","content":"sdsd"}
just the final text field. i know am wrong somewhere . can someone help me to fix it. thanks in advance
That's because you are overwriting object's properties and the last values win, you can use an array and it's push method.
$.fn.serializeObject = function () {
var o = [];
var a = this.serializeArray();
$.each(a, function (i, n) {
o.push({
name: n['name'],
content: n['value']
})
});
return o;
};
http://jsfiddle.net/kxM3e/
Using jQuery map method:
$.fn.serializeObject = function () {
return this.find('input, textarea, select').map(function(){
return { name: this.name, content: this.value };
}).get();
};

Categories

Resources