A series of algorithms are encapsulated and made interchangeable with each other. The encapsulated algorithm is independent and cannot be changed externally
The data dictionary
In my understanding, data dictionaries naturally fit the concept of strategic patterns and are therefore classified as applications of strategic patterns
const dates = ['day'.'一'.'二'.'三'.'four'.'five'.'六']
console.log('Today is Sunday${dates[new Date().getDay()]}`)
const statusArr = ['save'.'edit'.'review'.'Approved'.'Audit failed'.'off']
statusArr[0] / / = > save
statusArr[3] //=> The audit passed
statusArr[6] / / = > shut down
// This is a simple example of enumeration state echo
const statusObj = {
'new':'new'.'edit':'edit'.'pass':'review'.'close':'off'
}
let code = 'close'
statusObj[code]
// Output in non-enumeration state The actual service may have non-enumeration types that can still be displayed in this way
// The following is a data dictionary simulating the I18N scenario
const DEFAULT = 'zh-CN' // Read by default
const dictionary = { // Internationalize the dictionary table
'zh-CN': {hello:'hello'.message:'message'.home:'home'
},
'en': {hello:'hello'.message:'message'.home:'home'}}// Install the new data dictionary method
function installNew(params,info){
Object.entries(info).forEach(([key,text]) = >{
// Convert the passed object to an array of key-value pairs
// params is the field to be followed
// The key here corresponds to the language
// Text corresponds to actual text
dictionary[key][params] = text
})
}
// Translation methods
function i18n(language,params){
const info = dictionary[language] || dictionary[DEFAULT]
// Fetch the corresponding language object of the dictionary according to the incoming language
return info[params] // Returns the corresponding argument of the language object
}
installNew('user', {// The user field needs to be installed
'zh-CN':'users'.en:'user'
})
i18n('zh-CN'.'user') / / = > user
i18n('en'.'user') // => user
i18n('zh-CN'.'home') / / = > home page
i18n('en'.'home') // => home
i18n('jp'.'home') / / = > home page
Copy the code
Automatic distribution
🌰 bus terminal will have different lines of buses between them the form of the line may be partially overlapped, the destination station may not be the same, so we choose a route from the bus must be in accordance with the existing route driving strategy mode of the behavior is similar to Qi
// Here we assume a business scenario and implement it using the policy pattern
// 1. Press a to output the description 'Hello'
// 2. Press b to pop up a timestamp
// 3. Press S to perform the a key logic and then perform the B key logic without increasing the policy execution times
// 4. Press ESC to output the number of times for executing different policies. The number of times for executing different policies is automatically added
const bus = {
run(e){ // The automatic distribution intersection is used as the bus terminal in the example
const {key} = e // Retrieves the key from the incoming keyboard event
const fn = this.methods[key] // From its own method
if(fn){
this.nums[key] ? this.nums[key]++ : this.nums[key] = 1 // Check if the policy has been executed. If not, set it to 1. If so, increment it by 1
fn.call(this,e) // Change the method's this pointer so that it can read the entire policy object}},nums: {},// => Save execution count data
methods: {a(){
console.log('hello')},b(){
alert(+new Date()},s(){
this.methods.a()
this.methods.b()
},
Escape(){
console.table(this.nums)
}
}
}
window.addEventListener('keydown'.e= >bus.run(e))
Copy the code
Use policy pattern to develop a table class for one-way data updates and arrow key movement
- Support shortcut keys up, down and left to move the focus
- Table data can be added/deleted/edited and synchronized to the data source in real time
<! DOCTYPEhtml>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<title></title>
<style>
table {
border-collapse: collapse;
}
th.td {
border: 1px solid #9c9c9c;
width: 200px;
}
input[type="text"] {
padding: 0px;
margin: 0px;
outline: 1px;
width: 100%;
border: 0px;
height: 30px;
}
input[type="text"]:focus {
box-shadow: 0px 0px 1px 2px #9f9f9f;
}
</style>
</head>
<body>
<input value="Add a row" type="button" e-click="addRow" />
<table>
<tbody></tbody>
</table>
</body>
<script>
function on(type, lister) { // Global listener encapsulation simplifies the amount of code
return window.addEventListener(type, lister)
}
function require(msg) {// Mandatory parameter verification
throw new Error(msg)
}
class MyTable {
_config = { // Default configuration
el: 'table'.cols: [].data: []
}
dom = null // An instance of the operation
constructor(config) {
Object.assign(this._config, config)// Copy the incoming configuration to itself
const { el } = this._config // Extract parameters
this.dom = document.querySelector(el) || require('Dom doesn't exist') // Don't get dom instantiation
this.renderHeader() // Render the header
this.load() // Render the content
this.addLiser() // monitor table
}
addLiser() {
on('click'.e= > {
// The element is clicked => extract the source dom => try to extract the e-click attribute configured on the DOM =
// Listen on the click event source dom if there is an e-click attribute automatically read its corresponding method name to execute
const { target } = e
const eName = target.attributes['e-click']
if (eName) {
this[eName.value](e)
}
})
on('input'.e= > {
// Element start input event => Try to extract the value of e-input => If the instance itself has a corresponding method => actively call method pass event => Extract event source input box custom index field => Extract the data object corresponding to the current row according to index and dynamically set the value of the corresponding property through field
// Listen on the click event source dom if there is an e-input attribute automatically read its own corresponding method name to execute
const { target } = e
const eName = target.attributes['e-input']
if (eName) {
this[eName.value](e)
}
})
on('keydown'.e= > {
// Listen for directional keys automatically distributed to the corresponding switch focus method inside the instance
const { key, target } = e
if (target.attributes.focus && typeof this[key] === 'function') {
const { index, field } = target.dataset
this[key](target, index * 1, field)
}
})
}
getNext(index, field) {
// Get the focus content of the incoming index row and field
return this.dom.querySelector(`[data-index="${index}"][data-field="${field}"][focus]`)}getFocusList(index) {
// Get all focusable DOM for the current row
return this.dom.querySelectorAll(`[data-index="${index}"][focus]`)}ArrowUp(target, index, field) {
// Get the input box of the same field on the previous line without using itself
const el = this.getNext(index - 1, field) || target
el.focus()
el.select()
}
ArrowDown(target, index, field) {
const el = this.getNext(index + 1, field) || target
el.focus()
el.select()
}
ArrowLeft(target, index, field) {
// Get all focusable input fields for the current row and find the last one based on its own index
const els = [...this.dom.querySelectorAll(`[data-index="${index}"][focus]`)]
const i = els.indexOf(target)
const prv = els[i - 1] || target
prv.focus()
prv.select()
}
ArrowRight(target, index, field) {
const els = [...this.dom.querySelectorAll(`[data-index="${index}"][focus]`)]
const i = els.indexOf(target)
const next = els[i + 1] || target
next.focus()
next.select()
}
renderHeader() {
const vm = document.createElement('table')
const ths = this._config.cols.reduce((prv, next) = > `${prv}<th>${next.title || ' '}</th>`.' ')
vm.innerHTML = `<thead><tr>${ths}< th > action < / th > < / tr > < thead > `
this.dom.append(... vm.children) }load() {
const { cols, data } = this._config
const body = this.dom.querySelector('tbody')
// Render a string of all line contents
const trs = data.reduce((prv, next, index) = > {
return prv + MyTable.renderTr(cols, next, index)
}, ' ')
body.innerHTML = trs // Replace the table body
console.table(this._config.data)
}
static renderTr(cols, row, index) {
// Render row data in batches
return `<tr>
${cols.reduce((prv, next) => {
return prv + MyTable.renderTD(row, index, next.field)
}, ' ')}<td> <input value=" delete "type="button" e-click="removeRow" data-index="${index}"/>
</td>
</tr>`
}
static renderTD(row, index, field) {
// Render single data content with TD tags containing input
// focus tag => Can focus content
// e-input => The input event is triggered after the input
// data-index => Index of the data object
// data-field => Data field
return `<td><input focus type="text" e-input="update" data-index="${index}" data-field="${field}" value="${row[field] || ' '}" /></td>`
}
addRow() {
// Push empty data
this._config.data.push({})
// Redraw the table
this.load()
}
removeRow(e) {
const { target } = e
const { index } = target.dataset
// Retrieve the index
this._config.data.splice(index, 1)
// Delete the index contents of the array
this.load()
// Redraw the table
}
update(e) {
const { target } = e
const { index, field, value } = target.dataset
// Extract index and data fields
// Retrieve objects by index update values by field
this._config.data[index][field] = target.value
console.table(this._config.data)
}
}
new MyTable({
cols: [{field: 'msg'.title: 'message'
},
{
field: 'user'.title: 'users'}, {field: 'remark'.title: 'note'}].data: [{}]
})
</script>
</html>
Copy the code