jQuery

Convert exponential numbers to decimal form in Javascript

As almost every developers knows, Javascript has notorious problems when working with really big or really small decimal numbers. The reason is floating point. Purpose of this article is not to explain why it behaves the way it does, there are much better resources dedicated just for that. You can read the explanation here or here. There are also various libraries dedicated to working with decimals and precise calculations, such as big.js.

To ilustrate the problem, see the short video below. The input shows the text value, and on the right there is raw value as represented in Javascript. The issue it can cause, is that when Angular (or any other framework for that sake) re-renders the value, it uses the raw value. So the 0.00000001 is replaced with 1e-8. Not very user friendly.

So as a workaround, I’ve written simple utility function. It takes a number and returns correctly formatted string. Be aware that because how Javascript handles decimal numbers, you are not able to represent small fractions in their decimal form. Therefore it has to be represented as a string or some kind of object.

/**
 * Checks if number is in exponential format (eg: 1e-8 for 0.00000001).
 * If it does not, original number is returned.
 * If it does it converts it to string representation of that number
 * which forces it to format 0.00000001
 */
export function convertExponentialToDecimal(exponentialNumber: number): number|string {
  // sanity check - is it exponential number
  const str = exponentialNumber.toString();
  if (str.indexOf('e') !== -1) {
    const exponent = parseInt(str.split('-')[1], 10);
    // Unfortunately I can not return 1e-8 as 0.00000001, because even if I call parseFloat() on it,
    // it will still return the exponential representation
    // So I have to use .toFixed()
    const result = exponentialNumber.toFixed(exponent);
    return result;
  } else {
    return exponentialNumber;
  }

If it helped you solve your problem, let me know in comments below

Angular – .map is not a function

I’ve encountered really weird error. After I’ve removed clearly unused code, our Angular app stopped working. It did however pass lint tests and also the production build passed without problems. The error occurred only during runtime. The error said something like “map is not a function at blablabla” and on the first glance it was totally unrelated to the code where it occurred.

After long digging and googling I found the root cause. Somewhere in our app we used import ‘rxjs/RX’, which imported the whole RxJS library which is large. It has about 50+ operators. And because it was included in the bundle accidentally, the app magically worked. We never had to worry about manually importing the RxJs operators. That is until we fixed the unused and unrelated import, hehe.

Conclusion

So the conclusion is, never import rxjs/RX because it will increase your bundle code size. But include only the specific operators which you really use. You can do it in the main module (usually called the app.module.ts) or specifically in those files, where they are actually used.

 

totally unrelated image :)

My import statement in app.module.ts now looks like this

import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/bufferWhen';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/delay';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/publish';
import 'rxjs/add/operator/publishLast';
import 'rxjs/add/operator/take';
import 'rxjs/add/operator/takeWhile';
import 'rxjs/add/operator/timeout';
import 'rxjs/add/operator/withLatestFrom';

Which is much better than this

// never ever!
import 'rxjs/Rx';

Print document from cross-origin iframe

When you need to show content from some other site, such as Google Docs, you can easily embed it inside an iframe. Google Docs even generates it for you. But what if you need to print that document via some button click? You can not do that, because the cross-origin policy does not allow it.

You have basically two options.

A) Create some proxy function on your server. But that is only available when you actually have a server and not just some hosted WordPress.

B) Create proxy iframe, which you are actually allowed to print, because there is no cross-origin involved. Then load the desired iframe inside the proxy iframe. Attach onload=”print()” on the target iframe, so the print starts automatically once it is loaded.

Working solution is below, have a nice day 🙂 Let me know, if it helped you.

Comments and suggestions for improvements are welcome. Hints: The iframe is not destroyed after print() is invoked, which might lead to memory leaks. Second issue is that you might need to calculate height of the iframe content, so that it is correctly printed. But if you can safely guess it, then you are fine.

  /**
   * Load iframe from cross-origin via proxy iframe
   * and then invokes the print dialog.
   * It is not possible to call window.print() on the target iframe directly
   * because of cross-origin policy.
   * 
   * Downside is that the iframe stays loaded. 
   */
  function printIframe(url) {
    var proxyIframe = document.createElement('iframe');
    var body = document.getElementsByTagName('body')[0];
    body.appendChild(proxyIframe);
    proxyIframe.style.width = '100%';
    proxyIframe.style.height = '100%';
    proxyIframe.style.display = 'none';

    var contentWindow = proxyIframe.contentWindow;
    contentWindow.document.open();
    // Set dimensions according to your needs.
    // You may need to calculate the dynamically after the content has loaded
    contentWindow.document.write('<iframe src="' + url + '" onload="print();" width="1000" height="1800" frameborder="0" marginheight="0" marginwidth="0">');
    contentWindow.document.close();
  }