1

The requirement is to format the amounts displayed on all the pages. This is my code for custom binding.

(function () {
    function refresh(element, valueAccessor) {
        var val = ko.utils.unwrapObservable(valueAccessor());
        $(element).text(getCultureSpecificAmount(val));
    }
    ko.bindingHandlers.currency = {
        init: refresh,
        update: refresh
    }
})();

And this is the method which formats the amounts (not so relevant but still posting)

function getCultureSpecificAmount(number) {
var result = 0;
var regex = /[+-]?\d+(?:\.\d+)?/g;
var tempNumber = number;

if (match = regex.exec(number.toString())) {
    tempNumber = match[0];
}

result = (parseFloat(tempNumber)).toLocaleString(culture, { maximumFractionDigits: currencyDecimalDigits, minimumFractionDigits: 0 });
return (number.toString()).replace(tempNumber, result);

}

This is from the cshtml to show how I am binding it

 <span style="font-weight:bold" data-bind="currency:PurchaseOrderValue"></span>

The getCultureSpecificAmount method is written in a common js. Currently I am writing the code for custom binding on each js. If I move this code to the common.js then it stops working. Writing this code on every page makes the code look really ugly. Is there a way to define custom binding globally and use it across all pages. This is my project on knockout so I am completely clueless.

eranjali08
  • 77
  • 10
  • Can you post your html to show how you are binding against the custom `bindingHandler`? – gkb Nov 28 '18 at 09:17
  • @gkb Updated the post with the html binding – eranjali08 Nov 28 '18 at 09:37
  • which console errors you get when you move the first snippet to common.js? – john Smith Nov 28 '18 at 09:42
  • @johnSmith Don't get any errors but it doesn't works also. I tried putting debugger and console.log and realized that it is not even getting called. – eranjali08 Nov 28 '18 at 09:44
  • what if you ommit the `(function () { `.. and `})();` part and make sure its after your getCultureSpecific function? – john Smith Nov 28 '18 at 09:47
  • @johnSmith could you please post the code. It is all very confusing for me. – eranjali08 Nov 28 '18 at 09:58
  • @johnSmith This doesn't works function refresh(element, valueAccessor) { var val = ko.utils.unwrapObservable(valueAccessor()); $(element).text(getCultureSpecificAmount(val)); } ko.bindingHandlers.currency = { init: refresh, update: refresh } – eranjali08 Nov 28 '18 at 10:00
  • what if you type into console `ko.bindingHandlers.currency` ? if its undefined you probably overwrite it or try to register it before knockout was loaded – john Smith Nov 28 '18 at 10:02
  • @johnSmith could you please tell me how to register/overwrite it? – eranjali08 Nov 28 '18 at 10:48
  • @johnSmith `ko.bindingHandlers.currency` is undefined – eranjali08 Nov 28 '18 at 11:24
  • @eranjali08 Are you using any framework to load the dependencies/modules? If yes then which one? – gkb Nov 29 '18 at 06:03
  • knockout.js with MVC – eranjali08 Nov 29 '18 at 06:10
  • so I guess you could move your logic into a different file, but would need to include the reference to that js file in the .cshtml file like explained here - https://stackoverflow.com/questions/24763493/how-to-include-js-files-in-the-view-asp-net-mvc-4 best luck :-) – gkb Nov 29 '18 at 06:16
  • The js is already registered in bundles. I just feel I am missing something here. How will the common.js know what is `ko.bindingHandlers` – eranjali08 Nov 29 '18 at 06:18
  • Figured it... It was the order of the js in bundle. The common.js needs to appear first. Thank you! – eranjali08 Nov 29 '18 at 08:33

1 Answers1

0

Here is something that works. One of the issues I found was that the if(match = regex.exec(...)) needed to move outside of the if(...) statement, but other than that, the below code is the essentially the same, so you weren't far off in getting it working.

function getCultureSpecificAmount(number) {
  var result = 0;
  var regex = /[+-]?\d+(?:\.\d+)?/g;
  var tempNumber = number;

  var match = regex.exec(number.toString());
  if (match != null) {
    tempNumber = match[0];
  }
  var culture = "en-AU";
  var currencyDecimalDigits = 2;
  result = (parseFloat(tempNumber)).toLocaleString(culture, {
    maximumFractionDigits: currencyDecimalDigits,
    minimumFractionDigits: 0
  });
  return (number.toString()).replace(tempNumber, result);
}

function refresh(element, valueAccessor) {
  var val = ko.utils.unwrapObservable(valueAccessor());
  $(element).text(getCultureSpecificAmount(val));
}
ko.bindingHandlers.currency = {
  init: refresh,
  update: refresh
}

var vm = {
  PurchaseOrderValue: ko.observable(3596.94985)
};

ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<label>Purchase Order Total</label>
<span style="font-weight:bold" data-bind="currency:PurchaseOrderValue"></span>
<br/>
<label>Edit Purchase Order Total</label>
<input  data-bind="textInput: PurchaseOrderValue" />
Nathan Fisher
  • 7,961
  • 3
  • 47
  • 68
  • Here everything seems to be on the same page. This doesn't works if the model is on another page/js – eranjali08 Nov 29 '18 at 05:14
  • what do you mean by another page/js? because as long as knockout and the bindingHandler/s are loaded in the correct order whether this is as part of the original HTML document from the server or added afterwards by a module loader like requirejs then this should work. I need more context as to the problem you are having. – Nathan Fisher Nov 29 '18 at 05:56
  • So there are multiple pages/views in the application. All of them have some amount fields to be displayed. Now the amount needs to be formatted while binding. We cannot alter the actual variable value and have to keep it as raw number as they are used for calculations. So I created custom binding to format the amounts. Now I am currently writing this custom binder on all the views to make the bindings work. This is leading to a lot of repetitive code. I wanted to know if there is a way where we can define the custom binder in a common place and use it across application. – eranjali08 Nov 29 '18 at 06:13
  • Ok, yes, you can put it in its own js file and load it once per server page request. just make sure that it loads after knockout and before any binding to that handler happens. – Nathan Fisher Nov 29 '18 at 06:21
  • if you open up the debug console on your browser, are there any errors? – Nathan Fisher Nov 29 '18 at 06:25
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/184448/discussion-between-nathan-fisher-and-eranjali08). – Nathan Fisher Nov 29 '18 at 06:26
  • 1
    Figured it... It was the order of the js in bundle – eranjali08 Nov 29 '18 at 08:32