Skip to content

Instantly share code, notes, and snippets.

@dfoderberg
Forked from davismj/app.html
Created November 29, 2017 22:36
Show Gist options
  • Save dfoderberg/123eefbced46144b10269d68aebac7c3 to your computer and use it in GitHub Desktop.
Save dfoderberg/123eefbced46144b10269d68aebac7c3 to your computer and use it in GitHub Desktop.
bug
<template>
<h1>Davis' Height</h1>
<p>
For the inches input, we do the following:<br />
<ol>
<li>First, we remove the inches value converter. The canonical value is inches, so we don't need to convert
the value.</li>
<li>Next, we add a number value converter. Seems trivial, but this ensures that our value always comes back
from the view as a number. We could add this to the decimals value converter, except it might not be clear
how that would behave. Would it return a number of max precision, or of precision n?</li>
<li>Next, we add the decimals value converter to truncate.</li>
<li>Finally, we add the debounce binding behavior, which delays computing a change. This is to prevent
the decimals binding behavior from truncating user input. Without it, if a user tried to type 75.54 the
decimal value converter would fire immediately after typing 75.5 and output 75.50. If the user deleted
the trailing 0 the value converter would fire again and add it back.</li>
</ol>
</p>
<!-- Note: Firefox automatically truncates inputs. -->
<div><input type="number" step="0.1" value.bind="davisHeight | number | decimals: 3 & debounce: 4000" /></div>
<p>
For the mm input, we do the following:<br />
<ol>
<li>First, we use the toMm value converter since the canonical value is inches.</li>
<li>Next, we add the decimals value converter to truncate.</li>
</ol>
Note that we don't add the debounce behavior since decimal values of mm are (probably) not needed.
</p>
<div><input type="number" value.bind="davisHeight | toMm | decimals: 0" /></div>
<p>Note that in all of this, the value stored in our view model is still as precise as JavaScript allows.</p>
<div>${davisHeight | decimals: 3} (${davisHeight})</div>
</template>
// https://gist.run/?id=c431b3c017bb4c4c8612403b451bd7fb LINK
import { observable } from 'aurelia-framework';
const MM_COEFF = 25.4;
export class App {
@observable davisHeight = 75;
davisHeightChanged(newVal, oldVal) {
console.log(newVal, oldVal);
}
}
// "Inches" was bad naming on my part. It's not clear if it is from inches or to inches.
// Let's call it "toInches" to make it clear it translates a mm value in the view model
// to inches in the view, and likewise for mm.
// We want each value converter to do just one thing. In this case, having it try to
// handle precision as well was confusing to Aurelia because it was fundamentally
// changing the value, which then needed to be translated back again, resulting in
// another fundamental translation. Let's keep these value converters limited to just
// converting units.
export class ToInchesValueConverter {
toView(mm) {
return mm / MM_COEFF;
}
fromView(inches) {
return parseFloat(inches) * MM_COEFF;
}
}
export class ToMmValueConverter {
toView(inches) {
return inches * MM_COEFF;
}
fromView(mm) {
return parseFloat(mm) / MM_COEFF;
}
}
// Instead, we delegate the task of truncating to a separate value converter that runs
// after thte initial conversion. Think of it as saying "convert from inches to mm, and
// then truncate." This is less confusing for Aurelia because now it knows that this is
// just a translation for the view, and not a fundamental translation of the value that
// needs to be pushed back from the view.
export class DecimalsValueConverter {
toView(val, decimals) {
return val.toFixed(decimals);
}
// Do we want it to truncate the value when returning from the view? This seems like
// just a transformation to make the output (in the view) easier to read, so I've left
// it out.
// fromView(val, decimals) {
// return parseFloat(val).toFixed(decimals);
// }
}
export class NumberValueConverter {
fromView(val) {
return parseFloat(val);
}
}
<!doctype html>
<html>
<head>
<title>Aurelia</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body aurelia-app>
<h1>Loading...</h1>
<script src="https://jdanyow.github.io/rjs-bundle/node_modules/requirejs/require.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/config.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/aurelia.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/babel.js"></script>
<script>
require(['aurelia-bootstrapper']);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment