Input Masking with Vue

A presentation at VueConf US in March 2019 in Tampa, FL, USA by Divya

Slide 1

Slide 1

Input Masking with Vue @shortdiv

Slide 2

Slide 2

Divya Sasidharan Developer Advocate

Slide 3

Slide 3

0.00

Slide 4

Slide 4

0.01

Slide 5

Slide 5

0.12

Slide 6

Slide 6

1.23

Slide 7

Slide 7

12.34

Slide 8

Slide 8

123.45

Slide 9

Slide 9

1,234.56

Slide 10

Slide 10

123.45

Slide 11

Slide 11

12.34

Slide 12

Slide 12

1.23

Slide 13

Slide 13

0.12

Slide 14

Slide 14

0.01

Slide 15

Slide 15

0.00

Slide 16

Slide 16

$0

Slide 17

Slide 17

$ 1

Slide 18

Slide 18

$ 12

Slide 19

Slide 19

$ 123

Slide 20

Slide 20

$ 1,234

Slide 21

Slide 21

$ 1,234 5

Slide 22

Slide 22

$ 1,234 56

Slide 23

Slide 23

$ 1,234 5

Slide 24

Slide 24

$ 1,234

Slide 25

Slide 25

$ 123

Slide 26

Slide 26

$ 12

Slide 27

Slide 27

$ 1

Slide 28

Slide 28

$0

Slide 29

Slide 29

Why use this pattern? I don’t have any UX research to cite, but anecdotally, I like it when inputs that expect data in a specific format use an input mask. — Chris Coyier

Slide 30

Slide 30

Input Events The Role of Reactivity Reusable component patterns

Slide 31

Slide 31

0.00

Slide 32

Slide 32

Requirements Allow only numbers Add format for dollars and cents

Slide 33

Slide 33

Input Events

Slide 34

Slide 34

Patterns Capturing Key Events 0.00

Slide 35

Slide 35

Patterns Capturing Key Events

Slide 36

Slide 36

Patterns Capturing Key Events 1

Slide 37

Slide 37

Patterns Capturing Key Events 1 oninput

Slide 38

Slide 38

Patterns Capturing Key Events 1 @input

Slide 39

Slide 39

<input type=”text” id=“currency” placeholder=”0.00” @input=”formatCashMoney” :value=”formattedCashMoney” />

Slide 40

Slide 40

<input type=”text” id=“currency” placeholder=”0.00” @input=”formatCashMoney” :value=”formattedCashMoney” :value=“formattedCashMoney” />

Slide 41

Slide 41

<input type=”text” id=“currency” placeholder=”0.00” /> v-model=”formattedCashMoney”

Slide 42

Slide 42

<template> <input type=”text” id=”currency” v-model=”formattedCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } } } </script>

Slide 43

Slide 43

<template> <input type=”text” id=”currency” v-model=”formattedCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, } </script>

Slide 44

Slide 44

<template> <input type=”text” id=”currency” v-model=”formattedCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } } } </script>

Slide 45

Slide 45

<template> <input type=”text” id=”currency” v-model=”formattedCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } } } </script>

Slide 46

Slide 46

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @input=”formatCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney() { //format cashMoney } } } </script>

Slide 47

Slide 47

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @input=“formatCashMoney” @input=”formatCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney() { //format cashMoney } } } </script>

Slide 48

Slide 48

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @input=“formatCashMoney” @input=”formatCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney() { //format cashMoney } } } </script>

Slide 49

Slide 49

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @input=“formatCashMoney” @input=”formatCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { cashMoney: null formattedCashMoney: null } }, methods: { formatCashMoney() { //format cashMoney } } } </script>

Slide 50

Slide 50

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @input=”formatCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney() { //format cashMoney } } } </script>

Slide 51

Slide 51

Requirements Allow only numbers Add format for dollars and cents

Slide 52

Slide 52

<template> <input type=”text” id=”currency” :value=”formattedCashMoney” @input=”formatCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney() { //format cashMoney } } } </script>

Slide 53

Slide 53

<template> <input type=”text” id=”currency” :value=”formattedCashMoney” @input=”formatCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney() { //format cashMoney } } } </script>

Slide 54

Slide 54

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @input=”formatCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney(e) { var key = e.key } } } </script>

Slide 55

Slide 55

<template> <input type=”text” id=”currency” :value=”formattedCashMoney” @input=”formatCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney() {{ formatCashMoney(e) var key = e.key if (!/^\d+/g.test(key)) { e.preventDefault() } } } } </script>

Slide 56

Slide 56

Working Demo

Slide 57

Slide 57

keydown > keypress > input > keyup

Slide 58

Slide 58

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @input=”formatCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney(e) { var key = e.key if (!/^\d+/g.test(key)) { e.preventDefault() } } } } </script>

Slide 59

Slide 59

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @input=”formatCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney(e) { var key = e.key if (!/^\d+/g.test(key)) { e.preventDefault() } } } } </script>

Slide 60

Slide 60

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @keydown=”formatCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney(e) { var key = e.key if (!/^\d+/g.test(key)) { e.preventDefault() } } } } </script>

Slide 61

Slide 61

Working Demo

Slide 62

Slide 62

Requirements Allow only numbers Add format for dollars and cents

Slide 63

Slide 63

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @keydown=”formatCashMoney” /> </template> <script> export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney(e) { var key = e.key if (!/^\d+/g.test(key)) { e.preventDefault() } } } } </script>

Slide 64

Slide 64

<template> <input type=”text” id=”currency” :value=”formattedCashMoney” @keydown=”formatCashMoney” /> </template> <script> import { wearMask } from “./utils” export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney(e) { … } } this.formattedCashMoney = wearMask(e.target.value) } </script>

Slide 65

Slide 65

function wearMask (str) { let num = decimalize(str) num = separateByThousandths(num, “,”) } function decimalize(numstr) { if (numstr.length > 1) { let dp = numstr.substring(numstr.length-2, numstr.length) let denom = numstr.substring(0, numstr.length - 2) if (denom.substring(0,1) === “0”) { denom = denom.substring(1, denom.length) } return ${denom}.${dp} } else { return 0.0${numstr} } } function separateByThousandths(str, delimiter) { return str.replace(/\B(?=(\d{3})+(?!\d))/g, ${delimiter}); }

Slide 66

Slide 66

function wearMask (str) { let num = decimalize(str) num = separateByThousandths(num, “,”) } function decimalize(numstr) decimalize(numstr) {{ if (numstr.length >> 1) 1) {{ let dp = numstr.substring(numstr.length-2, numstr.substring(numstr.length-2, numstr.length) numstr.length) let denom = numstr.substring(0, numstr.substring(0, numstr.length numstr.length — 2) 2) if (denom.substring(0,1) (denom.substring(0,1) === === “0”) “0”) {{ denom = denom.substring(1, denom.substring(1, denom.length) denom.length) } return ${denom}.${dp}${denom}.${dp} } else { return 0.0${numstr}0.0${numstr} } } function separateByThousandths(str, delimiter) { return str.replace(/\B(?=(\d{3})+(?!\d))/g, ${delimiter}); }

Slide 67

Slide 67

function wearMask (str) { let num = decimalize(str) num = separateByThousandths(num, “,”) } function decimalize(numstr) decimalize(numstr) {{ if (numstr.length >> 1) 1) {{ let dp = numstr.substring(numstr.length-2, numstr.substring(numstr.length-2, numstr.length) numstr.length) let denom = numstr.substring(0, numstr.substring(0, numstr.length numstr.length — 2) 2) if (denom.substring(0,1) (denom.substring(0,1) === === “0”) “0”) {{ denom = denom.substring(1, denom.substring(1, denom.length) denom.length) } return ${denom}.${dp}${denom}.${dp} } else { return 0.0${numstr}0.0${numstr} } } function separateByThousandths(str, delimiter) { return str.replace(/\B(?=(\d{3})+(?!\d))/g, ${delimiter}); }

Slide 68

Slide 68

function wearMask (str) { let num = decimalize(str) num = separateByThousandths(num, “,”) } function decimalize(numstr) decimalize(numstr) {{ if (numstr.length >> 1) 1) {{ let dp = numstr.substring(numstr.length-2, numstr.substring(numstr.length-2, numstr.length) numstr.length) let denom = numstr.substring(0, numstr.substring(0, numstr.length numstr.length — 2) 2) if (denom.substring(0,1) (denom.substring(0,1) === === “0”) “0”) {{ denom = denom.substring(1, denom.substring(1, denom.length) denom.length) } return ${denom}.${dp}${denom}.${dp} } else { return 0.0${numstr}0.0${numstr} } } function separateByThousandths(str, delimiter) { return str.replace(/\B(?=(\d{3})+(?!\d))/g, ${delimiter}); }

Slide 69

Slide 69

function wearMask (str) { let num = decimalize(str) num = separateByThousandths(num, “,”) } function decimalize(numstr) decimalize(numstr) {{ if (numstr.length >> 1) 1) {{ let dp = numstr.substring(numstr.length-2, numstr.substring(numstr.length-2, numstr.length) numstr.length) let denom = numstr.substring(0, numstr.substring(0, numstr.length numstr.length — 2) 2) if (denom.substring(0,1) (denom.substring(0,1) === === “0”) “0”) {{ denom = denom.substring(1, denom.substring(1, denom.length) denom.length) } return ${denom}.${dp}${denom}.${dp} } else { return 0.0${numstr}0.0${numstr} } } function separateByThousandths(str, delimiter) { return str.replace(/\B(?=(\d{3})+(?!\d))/g, ${delimiter}); }

Slide 70

Slide 70

function wearMask (str) { let num = decimalize(str) num = separateByThousandths(num, “,”) } function decimalize(numstr) { if (numstr.length > 1) { let dp = numstr.substring(numstr.length-2, numstr.length) let denom = numstr.substring(0, numstr.length - 2) if (denom.substring(0,1) === “0”) { denom = denom.substring(1, denom.length) } return ${denom}.${dp} } else { return 0.0${numstr} } } function separateByThousandths(str, delimiter) { return str.replace(/\B(?=(\d{3})+(?!\d))/g, ${delimiter}); }

Slide 71

Slide 71

function wearMask (str) { let num = decimalize(str) num = separateByThousandths(num, “,”) } function decimalize(numstr) { if (numstr.length > 1) { let dp = numstr.substring(numstr.length-2, numstr.length) let denom = numstr.substring(0, numstr.length - 2) if (denom.substring(0,1) === “0”) { denom = denom.substring(1, denom.length) } return ${denom}.${dp} } else { return 0.0${numstr} } } function separateByThousandths(str, delimiter) { return str.replace(/\B(?=(\d{3})+(?!\d))/g, ${delimiter}); }

Slide 72

Slide 72

thousandths 123456 cents

Slide 73

Slide 73

thousandths 1,234.56 cents

Slide 74

Slide 74

Working Demo

Slide 75

Slide 75

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @keydown=”formatCashMoney” /> </template> <script> import { wearMask } from “./utils” export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney() { … } } this.formattedCashMoney = wearMask(e.target.value) } </script>

Slide 76

Slide 76

keydown > keypress > input > keyup

Slide 77

Slide 77

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @keydown=”formatCashMoney” /> </template> <script> import { wearMask } from “./utils” export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney(e) { … } } this.formattedCashMoney = wearMask(e.target.value) } </script>

Slide 78

Slide 78

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @keydown=“formatCashMoney” @keydown=”formatCashMoney” /> </template> <script> import { wearMask } from “./utils” export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { formatCashMoney(e) { … } } this.formattedCashMoney = wearMask(e.target.value) } </script>

Slide 79

Slide 79

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @keydown=“validateInput” @keydown=”validateInput” @input=“formatInput” @input=”formatInput” /> </template> <script> import { wearMask } from “./utils” export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { validateInput(e) { … } formatInput(e) { this.formattedCashMoney = wearMask(e.target.value) } } } </script>

Slide 80

Slide 80

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @keydown=“validateInput” @keydown=”validateInput” @input=“formatInput” @input=”formatInput” /> </template> <script> import { wearMask } from “./utils” export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { validateInput(e) { … } formatInput(e) { this.formattedCashMoney = wearMask(e.target.value) } } }c } </script>

Slide 81

Slide 81

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @keydown=”validateInput” @input=”formatInput” /> </template> <script> import { wearMask } from “./utils” export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { validateInput(e) { … } formatInput(e) { this.formattedCashMoney = wearMask(e.target.value) } } } </script>

Slide 82

Slide 82

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @keydown=“validateInput” @keydown=”validateInput” @input=“formatInput” @input=”formatInput” /> </template> <script> import { wearMask, removeMask } from “./utils” export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { validateInput(e) { … } formatInput(e) { let unformatted = removeMask(e.target.value) this.formattedCashMoney = wearMask(e.target.value) } } } </script>

Slide 83

Slide 83

function wearMask (str) { let num = decimalize(str) num = separateByThousandths(num, “,”) } function decimalize(numstr) { if (numstr.length > 1) { let dp = numstr.substring(numstr.length-2, numstr.length) let denom = numstr.substring(0, numstr.length - 2) if (denom.substring(0,1) === “0”) { denom = denom.substring(1, denom.length) } return ${denom}.${dp} } else { return 0.0${numstr} } } function separateByThousandths(str, delimiter) { return str.replace(/\B(?=(\d{3})+(?!\d))/g, ${delimiter}); } function removeMask(str) { return numstr.replace(/\D+/g, “”); }

Slide 84

Slide 84

function wearMask (str) { let num = decimalize(str) num = separateByThousandths(num, “,”) } function decimalize(numstr) { if (numstr.length > 1) { let dp = numstr.substring(numstr.length-2, numstr.length) let denom = numstr.substring(0, numstr.length - 2) if (denom.substring(0,1) === “0”) { denom = denom.substring(1, denom.length) } return ${denom}.${dp} } else { return 0.0${numstr} } } function separateByThousandths(str, delimiter) { return str.replace(/\B(?=(\d{3})+(?!\d))/g, ${delimiter}); } function removeMask(str) { return numstr.replace(/\D+/g, “”); }

Slide 85

Slide 85

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @keydown=”validateInput” @input=”formatInput” /> </template> <script> import { wearMask, removeMask } from “./utils” export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { validateInput(e) { … } formatInput(e) { let unformatted = removeMask(e.target.value) this.formattedCashMoney = wearMask(e.target.value) } } } </script>

Slide 86

Slide 86

Requirements Allow only numbers Allowformat for delete/backspace Add for dollars and cents

Slide 87

Slide 87

The Role of Reactivity

Slide 88

Slide 88

0.00

Slide 89

Slide 89

Reactivity Methods methods: { onInput() {}, onKeyPress() {}, onKeyDown() {} … }

Slide 90

Slide 90

<input type=”text” id=”currency” :value=”formattedCashMoney” @input=”formatInput” @keydown=”validateInput” />

Slide 91

Slide 91

<input type=”text” id=”currency” :value=”formattedCashMoney” @input=”formatInput” @keydown=”validateInput” />

Slide 92

Slide 92

Reactivity Computed Property computed: { currentMask() { // format // } }

Slide 93

Slide 93

<input type=”text” id=”currency” :value=”formattedCashMoney” @input=”formatInput” @keydown=”validateInput” />

Slide 94

Slide 94

<template> <input type=”text” id=”currency” :value=”formattedCashMoney” @keydown=”validateInput” @input=”formatInput” /> </template> <script> import { wearMask, removeMask } from “./utils” export default { name: “v-dinero”, data () { return { formattedCashMoney: null } }, methods: { validateInput(e) { … } formatInput(e) { let unformattedVal = removeMask(e.target.value) this.formattedCashMoney = wearMask(unformattedVal) } } } </script>

Slide 95

Slide 95

<template> <input type=”text” id=”currency” :value=”formattedCashMoney” @keydown=”validateInput” @input=”updateInput” /> </template> <script> import { wearMask, removeMask } from “./utils” export default { name: “v-dinero”, data () { return { unformatted: null } }, methods: { validateInput(e) { … } updateInput(e) { this.unformatted = removeMask(e.target.value) } }, computed: { formattedCashMoney() { return wearMask(this.unformatted) } } } </script>

Slide 96

Slide 96

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @keydown=”validateInput” @input=”formatInput” /> </template> <script> import { wearMask, removeMask } from “./utils” export default { name: “v-dinero”, data () { return { unformatted: null } }, methods: { validateInput(e) { … } updateInput(e) { this.unformatted = removeMask(e.target.value) } }, computed: { formattedCashMoney() { return wearMask(this.unformatted) } } } </script>

Slide 97

Slide 97

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @keydown=“validateInput” @input=“formatInput” /> </template> <script> import { wearMask, removeMask } from “./utils” export default { name: “v-dinero”, data () { return { unformatted: null } }, methods: { validateInput(e) { … } updateInput(e) { this.unformatted = removeMask(e.target.value) } }, computed: { formattedCashMoney() { return wearMask(this.unformatted) } } } </script>

Slide 98

Slide 98

<template> <input type=”text” id=“currency” :value=”formattedCashMoney” @keydown=“validateInput” @input=“updateInput” @input=”updateInput” /> </template> <script> import { wearMask, removeMask } from “./utils” export default { name: “v-dinero”, data () { return { unformatted: null } }, methods: { validateInput(e) { … } updateInput(e) { this.unformatted = removeMask(e.target.value) } }, computed: { formattedCashMoney: {{ formattedCashMoney() return wearMask(this.unformatted) } } } </script>

Slide 99

Slide 99

Working Demo

Slide 100

Slide 100

Reactivity Computed Property computed: { currentMask() { get(){ … } set(){ … } } }

Slide 101

Slide 101

<template> <input type=”text” id=”currency” :value=”formattedCashMoney” @input=”updateInput” @keydown=”validateInput” /> </template> <script> import { wearMask, removeMask } from “./utils” export default { name: “v-dinero”, data () { return { unformatted: null } }, methods: { validateInput(e) { … } updateInput(e) { this.unformatted = removeMask(e.target.value) } }, computed: { formattedCashMoney() { return wearMask(this.unformatted) } } } </script>

Slide 102

Slide 102

<template> <input type=”text” id=”currency” v-model=”formattedCashMoney” @keydown=”validateInput” /> </template> <script> import { wearMask, removeMask } from “./utils” export default { name: “v-dinero”, data () { return { formatted: null } }, methods: { validateInput(e) { … } }, computed: { formattedCashMoney() { get() { … }, set() { … } } } } </script>

Slide 103

Slide 103

<template> <input type=”text” id=”currency” v-model=”formattedCashMoney” @keydown=”validateInput” /> </template> <script> import { wearMask, removeMask } from “./utils” export default { name: “v-dinero”, data () { return { formatted: null } }, methods: { validateInput(e) { … } }, computed: { formattedCashMoney() { get() { return this.formatted }, set() { … } } } } </script>

Slide 104

Slide 104

<template> <input type=”text” id=”currency” v-model=”formattedCashMoney” @keydown=”validateInput” /> </template> <script> import { wearMask, removeMask } from “./utils” export default { name: “v-dinero”, data () { return { formatted: null } }, methods: { validateInput(e) { … } }, computed: { formattedCashMoney() { get() { return this.formatted }, set(newVal) { let val = removeMask(newVal) this.formatted = wearMask(this.unformatted) } } } } </script>

Slide 105

Slide 105

Working Demo

Slide 106

Slide 106

Reusable Components

Slide 107

Slide 107

0.00

Slide 108

Slide 108

(###)-###-####

Slide 109

Slide 109

MM/DD/YYYY

Slide 110

Slide 110

<VDinero />

Slide 111

Slide 111

Component Pattern Renderless Components export default { render(h) { return this.$scopedSlots.default({ … }) } }

Slide 112

Slide 112

<v-mask> //some children nodes// </v-mask>

Slide 113

Slide 113

<v-mask v-slot:default=”{ … }” > //some children nodes// </v-mask>

Slide 114

Slide 114

<v-mask v-slot:default=”{ formattedValue, input, keydown }” > <label> <input type=”text” :value=”formattedValue” @input=”input” @keydown=”keydown” /> <label> </v-mask>

Slide 115

Slide 115

<v-mask v-slot:default=“{ v-slot:default=”{ formattedValue, input, keydown }” }” > <label> <input type=”text” :value=“formattedValue” :value=”formattedValue” @input=“input” @input=”input” @keydown=”keydown” @keydown=“keydown” /> <label> </v-mask>

Slide 116

Slide 116

<v-mask v-slot:default=”{ formattedValue, input, keydown }” > <label> <input type=”text” :value=”formattedValue” @input=”input” @keydown=”keydown” /> <label> </v-mask>

Slide 117

Slide 117

<v-mask v-slot:default=“{ v-slot:default=”{ formattedValue, listeners }” }” > <label> <input type=”text” :value=”formattedValue” v-on=”listeners” /> <label> </v-mask>

Slide 118

Slide 118

<v-mask v-slot:default=”{ formattedValue, listeners }” > <label> <input type=”text” :value=”formattedValue” v-on=”listeners” /> <label> </v-mask>

Slide 119

Slide 119

export default { name: “v-mask”, data() { return { formatted: “0.00” }; }, render() { return this.$scopedSlots.default({ formattedValue: this.formatted, listeners: { keydown: e => { … }, input: e => { … } } }) } }

Slide 120

Slide 120

export default { name: “v-mask”, data() { return { formatted: “0.00” }; }, render() { return this.$scopedSlots.default({ formattedValue: this.formatted, listeners: { keydown: e => { … }, input: e => { … } } }) } }

Slide 121

Slide 121

export default { name: “v-mask”, “v-mask”, data() { return { formatted: “0.00” “0.00” }; }, render() { return this.$scopedSlots.default({ formattedValue: this.formatted, listeners: { keydown: e => { … }, input: e => { … } } }) } }

Slide 122

Slide 122

export default { name: “v-mask”, “v-mask”, data() { return { formatted: “0.00” }; }, render() { return this.$scopedSlots.default({ formattedValue: this.formatted, listeners: { keydown: e => { … }, input: e => { … } } }) } }

Slide 123

Slide 123

<input v-mask=”’(###)###-####’” />

Slide 124

Slide 124

<input v-mask=”’##/##/####’” />

Slide 125

Slide 125

Component Pattern Directives directives: { mask: { bind(){ … } inserted(){ … } update(){ … } componentUpdated(){ … } unbind(){ … } } }

Slide 126

Slide 126

Slide 127

Slide 127

<template> <input type=”text” id=”currency” v-mask=”’(###)###-####’” > </template> <script> export default { name: “v-dinero”, directives: { mask: { bind() {…} componentUpdated() {…} } }, } </script>

Slide 128

Slide 128

<template> <input type=”text” id=”currency” v-mask=”’(###)###-####’” > </template> <script> export default { name: “v-dinero”, directives: { mask: { bind() {…} componentUpdated() {…} } }, } </script>

Slide 129

Slide 129

<template> <input type=”text” id=”currency” v-mask=”’(###)###-####’” > </template> <script> import { wearMask, removeMask } from “./utils” export default { name: “v-dinero”, directives: { mask: { bind(el, { value }) { const mask = value el.value = wearMask(el.target.value, mask); }, componentUpdated() {…} } }, } </script>

Slide 130

Slide 130

<template> <input type=”text” id=”currency” v-mask=”’(###)###-####’” > </template> <script> import { wearMask, removeMask } from “./utils” export default { name: “v-dinero”, directives: { mask: { bind(el, { value }) { const mask = value el.value = wearMask(el.target.value, mask); }, componentUpdated componentUpdated(el, { value }) { const mask = value let unformatted = removeMask(el.target.value) el.value = wearMask(unformatted, mask); } } }, } </script>

Slide 131

Slide 131

<template> <input type=”text” id=”currency” v-mask=”’(###)###-####’” v-model=”currentMask” > </template> <script> import { wearMask, removeMask } from “./utils” export default { name: “v-dinero data() { return { currentMask: null } }, directives: { mask: { bind(el, { value }) { const mask = value el.value = wearMask(el.target.value, mask); }, componentUpdated(el, { value }) { const mask = value let unformatted = removeMask(el.target.value) el.value = wearMask(unformatted, mask); } } }, } </script>

Slide 132

Slide 132

<template> <input type=”text” id=”currency” v-mask=”’(###)###-####’” v-model=”currentMask” > </template> <script> import { wearMask, removeMask } from “./utils” export default { name: “v-dinero data() { return { currentMask: null } }, directives: { mask: { bind(el, { value }) { const mask = value el.value = wearMask(el.target.value, mask); }, componentUpdated(el, { value }) { const mask = value let unformatted = removeMask(el.target.value) el.value = wearMask(unformatted, mask); } } }, } </script>

Slide 133

Slide 133

Component Pattern Hooks ?????????????????????

Slide 134

Slide 134

Component Pattern “Hooks” ?????????????????????

Slide 135

Slide 135

Component Pattern “Hooks” Compositional Functions ?????????????????????

Slide 136

Slide 136

Mixins can’t consume and use state from one to another, but Hooks can. This means that if we need to chain encapsulated logic, it’s now possible with Hooks. — Evan You Why use this pattern?

Slide 137

Slide 137

Slide 138

Slide 138

<input type=”text” v-model=”formatted” @keydown=”validateInput” >

Slide 139

Slide 139

<input type=”text” :value=”formatted.get()” @input=“formatted.set(e)” @keydown=“validateInput” >

Slide 140

Slide 140

import { useData, useComputed } from “vue-hooks”; export function computeMask() { function wearMask(numstr) { //format code// } const data = useData({ unformatted: null }); } const computed = useComputed(() => { return { formatted: { get() { return data.unformatted; }, set(e) { data.unformatted = wearMask(e.target.value); } } } } return { computed };

Slide 141

Slide 141

import { useData, useComputed } from “vue-hooks”; export function computeMask() { function wearMask(numstr) { //format code// } const data = useData({ unformatted: null }); } const computed = useComputed(() => { return { formatted: { get (){{ get() return data.unformatted; }, set(e) { data.unformatted = wearMask(e.target.value); } } } } return { computed };

Slide 142

Slide 142

import { useData, useComputed } from “vue-hooks”; export function computeMask() { function wearMask(numstr) { //format code// } const data = useData({ unformatted: null }); } const computed = useComputed(() => { return { formatted: { get (){{ get() return data.unformatted; }, set(e) { data.unformatted = wearMask(e.target.value); } } } } return { computed };

Slide 143

Slide 143

import { useData, useComputed } from “vue-hooks”; export function computeMask() { function wearMask(numstr) { //format code// } const data = useData({ unformatted: null }); } const computed = useComputed(() => { return { formatted: { get() { return data.unformatted; }, set(e) { data.unformatted = wearMask(e.target.value); } } } } return { computed };

Slide 144

Slide 144

import { useData, useComputed } from “vue-hooks”; export function computeMask() { function wearMask(numstr) { //format code// } const data = useData({ unformatted: null }); } const computed = useComputed(() => { return { formatted: { get() { return data.unformatted; }, set(e) { data.unformatted = wearMask(e.target.value); } } } } return { computed };

Slide 145

Slide 145

<template> <input type=”text” :value=”formatted.get()” @input=”formatted.set(e)” /> </template> <script> import { computeMask } from “../hooks/computeMask” export default { name: “VDinero”, hooks() { const { computed } = computeMask(); const { formatted } = computed } return { formatted }; } </script>

Slide 146

Slide 146

<template> <input type=“text” :value=”formatted.get()” :value=“formatted.get()” @input=”formatted.set(e)” > /> </template> <script> import { computeMask } from “../hooks/computeMask” “../hooks/computeMask” export default { name: “VDinero”, hooks() { const { computed } = computeMask(); const { formatted } = computed } return { formatted }; } </script>

Slide 147

Slide 147

<template> <input type=“text” :value=”formatted.get()” :value=“formatted.get()” @input=”formatted.set(e)” /> </template> <script> import { computeMask } from “../hooks/computeMask” export default { name: “VDinero”, hooks() { const { computed } = computeMask(); const { formatted } = computed } return { formatted }; } </script>

Slide 148

Slide 148

<template> <input type=”text” :value=”formatted.get()” @input=”formatted.set(e)” /> </template> <script> import { computeMask } from “../hooks/computeMask” export default { name: “VDinero”, hooks() { const { computed } = computeMask(); const { formatted } = computed } return { formatted }; } </script>

Slide 149

Slide 149

Why use this pattern? Maybe don’t use that… — Vue people I chatted with

Slide 150

Slide 150

Slide 151

Slide 151

import { value, computed } from ‘@vue/observer’ export function useComputeMask() { const unformatted = value(0) const formatted = computed( () => unformatted.value, val => { let unformattedVal = removeMask(val) unformatted.value = wearMask(unformattedVal) } ) return { formatted } }

Slide 152

Slide 152

import { value, computed } from ‘@vue/observer’ ‘@vue/observer’ export function useComputeMask() { const unformatted = value(0) const formatted = computed( () => unformatted.value, val => { let unformattedVal = removeMask(val) unformatted.value = wearMask(unformattedVal) } ) return { formatted } }

Slide 153

Slide 153

import { value, computed } from ‘@vue/observer’ ‘@vue/observer’ export function useComputeMask() { const unformatted = value(0) const formatted = computed( () => unformatted.value, val => { let unformattedVal = removeMask(val) unformatted.value = wearMask(unformattedVal) } ) return { formatted } }

Slide 154

Slide 154

import { value, computed } from ‘@vue/observer’ ‘@vue/observer’ export function useComputeMask() { const unformatted = value(0) const formatted = computed( () => unformatted.value, val => { let unformattedVal = removeMask(val) unformatted.value = wearMask(unformattedVal) } ) return { formatted } }

Slide 155

Slide 155

import { value, computed } from ‘@vue/observer’ ‘@vue/observer’ export function useComputeMask() { const unformatted = value(0) const formatted = computed( () => unformatted.value, val => { let unformattedVal = removeMask(val) unformatted.value = wearMask(unformattedVal) } ) } return { formatted }

Slide 156

Slide 156

import { value, computed } from ‘@vue/observer’ ‘@vue/observer’ export function useComputeMask() { const unformatted = value(0) const formatted = computed( () => unformatted.value, val => { let unformattedVal = removeMask(val) unformatted.value = wearMask(unformattedVal) } ) } return { formatted }

Slide 157

Slide 157

import { value, computed } from ‘@vue/observer’ export function useComputeMask() { const unformatted = value(0) const formatted = computed( () => unformatted.value, val => { let unformattedVal = removeMask(val) unformatted.value = wearMask(unformattedVal) } ) return { formatted } }

Slide 158

Slide 158

<template> <input type=”text” v-model=”formatted” /> </template> <script> import { useComputemask } from “../cp” export default { name: “VDinero”, data() { const { formatted } = useComputemask(); } return { formatted }; } </script>

Slide 159

Slide 159

Slide 160

Slide 160

Input Events The Role of Reactivity Reusable component patterns

Slide 161

Slide 161

Methods, Computeds, Directives, Renderless Components, Hooks Compositional Functions, Oh my!

Slide 162

Slide 162

🤔

Slide 163

Slide 163

Usability User Experience Developer Ergonomics

Slide 164

Slide 164

Thanks! @shortdiv