binder.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. // Cache DOM elements
  2. const inputElements = document.querySelectorAll('[data-model]');
  3. const boundElements = document.querySelectorAll('[data-bind]');
  4. const loopElements = document.querySelectorAll('[data-loop]');
  5. /* --- Bind element --- */
  6. function binder(data) {
  7. let scope = {}
  8. // Add data
  9. if (data) {
  10. for (const propName of Object.getOwnPropertyNames(data)) {
  11. scope[propName] = data[propName];
  12. setBindValue(propName, data[propName]);
  13. }
  14. }
  15. // Loop through input elements
  16. for (let el of inputElements) {
  17. if (el.type === 'text' || el.type === 'hidden') {
  18. // Get property name from each input with an attribute of 'mm-model'
  19. let propName = el.getAttribute('data-model');
  20. // Set property update logic
  21. setPropUpdateLogic(scope, propName);
  22. // Update bound scope property on input change
  23. scope[propName] = el.value;
  24. el.addEventListener('keyup', e => {
  25. scope[propName] = el.value;
  26. });
  27. }
  28. }
  29. return scope;
  30. };
  31. function setPropUpdateLogic(scope, prop) {
  32. if (!scope.hasOwnProperty(prop)) {
  33. let value;
  34. Object.defineProperty(scope, prop, {
  35. // Automatically update bound dom elements when a scope property is set to a new value
  36. set: (newValue) => {
  37. value = newValue;
  38. // Set input elements to new value
  39. for (let el of inputElements) {
  40. if (el.getAttribute('data-model') === prop) {
  41. if (el.type) {
  42. el.value = newValue;
  43. }
  44. }
  45. }
  46. // Set all other bound dom elements to new value
  47. setBindValue(prop, newValue);
  48. },
  49. get: () => {
  50. return value;
  51. },
  52. enumerable: true
  53. })
  54. }
  55. }
  56. function setBindValue(prop, newValue) {
  57. for (let el of boundElements) {
  58. if (el.getAttribute('data-bind') === prop) {
  59. if (!el.type) {
  60. el.innerHTML = newValue;
  61. }
  62. }
  63. }
  64. }
  65. /* --- For each --- */
  66. function looper(scope) {
  67. for (let el of loopElements) {
  68. let propName = el.getAttribute('data-loop');
  69. let data = scope[propName];
  70. let html = [];
  71. // If data is an array (if not do nothing)
  72. if (data && Array.isArray(data)) {
  73. // If data is already computed back to the initial value
  74. const defaultValue = el.querySelector('[data-looped]')
  75. if (defaultValue) {
  76. el.innerHTML = defaultValue.innerHTML;
  77. }
  78. // Add the not computed value
  79. html.push(`<div data-looped="${propName}" style="display: none">${el.innerHTML}</div>`);
  80. // Add info to replace value
  81. for (let i = 0; i < data.length; i++) {
  82. html.push(el.innerHTML.replace(/data-val="/g, 'data-index="' + i + '" data-val="'));
  83. }
  84. el.innerHTML = html.join("\n");
  85. // Replace value for the current loop
  86. const currentLoopElements = document.querySelectorAll('[data-loop=' + propName + ']');
  87. for (const currentElement of currentLoopElements) {
  88. valElements = currentElement.querySelectorAll('[data-val][data-index]');
  89. for (const valElement of valElements) {
  90. const val = scope[propName][valElement.getAttribute('data-index')][valElement.getAttribute('data-val')];
  91. if (val) {
  92. valElement.innerHTML = val;
  93. }
  94. }
  95. }
  96. }
  97. }
  98. }