list.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. const app = require('../../app')
  2. const {editIcon, createIcon, deleteIcon, viewIcon} = require('../../assets')
  3. /**
  4. * @param {CrudPagesOptions} opts
  5. */
  6. const list = (opts) => {
  7. const hideCriteria = column => column.routeParam ? `$ctrl.$routeParams.${column.routeParam} !== '${opts.paramAll}'` : 'false'
  8. const defaultHeader = column =>
  9. html`<th ng-hide="${hideCriteria(column)}" md-column>${column.titleName}</th>`
  10. const defaultCell = column => {
  11. if (column.type === 'autocomplete') {
  12. return html`<td ng-hide="${hideCriteria(column)}" md-cell>{{${raw(opts.camelName)}.${raw(column.camelName)} && ($ctrl.autocomplete.${raw(column.camelName)}.lookup[${raw(opts.camelName)}.${raw(column.camelName)}] || $ctrl.autocomplete.${raw(column.camelName)}.load(${raw(opts.camelName)}.${raw(column.camelName)}))}}</td>`
  13. } else {
  14. return html`<td ng-hide="${hideCriteria(column)}" md-cell>{{${raw(opts.camelName)}.${raw(column.camelName)}}}</td>`
  15. }
  16. }
  17. const template = html`
  18. <app-user-area title-text="${opts.titles && opts.titles.details ? `{{$ctrl.titleFn($ctrl)}}` : opts.titlePlural}">
  19. <md-table-container>
  20. <table md-table md-row-select md-auto-select ng-model="$ctrl.selected" md-progress="$ctrl.promise">
  21. <thead md-head md-order="query.order" md-on-reorder="$ctrl.getRecords">
  22. <tr md-row>
  23. ${opts.columns.filter(c => c.inList).map(c => c.header || defaultHeader(c))}
  24. <th md-column>Actions</th>
  25. </tr>
  26. </thead>
  27. <tbody md-body>
  28. <tr md-row md-select="${opts.camelName}" md-select-id="name" md-auto-select ng-repeat="${raw(opts.camelName)} in $ctrl.data track by ${raw(opts.camelName)}.id">
  29. ${opts.columns.filter(c => c.inList).map(c => c.cell || defaultCell(c))}
  30. <td md-cell>
  31. <md-button
  32. ng-if="$ctrl.api.claims.${opts.constantName}_UPDATE"
  33. ng-href="{{::$ctrl.appPrefix}}/{{${raw(opts.camelName)}.id}}">
  34. <md-icon md-svg-icon="${editIcon}"></md-icon>
  35. Edit
  36. </md-button>
  37. <md-button
  38. ng-if="!$ctrl.api.claims.${opts.constantName}_UPDATE"
  39. ng-href="{{::$ctrl.appPrefix}}/{{${raw(opts.camelName)}.id}}">
  40. <md-icon md-svg-icon="${viewIcon}"></md-icon>
  41. View
  42. </md-button>
  43. <md-button
  44. ng-if="$ctrl.api.claims.${opts.constantName}_DELETE"
  45. ng-click="$ctrl.delete(${raw(opts.camelName)})">
  46. <md-icon md-svg-src="${deleteIcon}"></md-icon>
  47. Delete
  48. </md-button>
  49. </td>
  50. </tr>
  51. </tbody>
  52. </table>
  53. </md-table-container>
  54. <div layout="row" layout-align="end">
  55. <md-button
  56. ng-if="$ctrl.api.claims.${opts.constantName}_CREATE"
  57. class="md-fab" aria-label="Add ${opts.titleName}" ng-href="{{::$ctrl.appPrefix}}/new">
  58. <md-icon md-svg-src="${createIcon}"></md-icon>
  59. </md-button>
  60. </div>
  61. </app-user-area>
  62. `
  63. console.log(`crud: app${opts.pascalPlural}Page`)
  64. app.component(`app${opts.pascalPlural}Page`, {
  65. template,
  66. controller: function(api, $mdToast, $routeParams, util, $interpolate, $scope) {
  67. this.api = api
  68. this.titleFn = opts.titles && opts.titles.list
  69. this.$routeParams = $routeParams
  70. this.apiPrefix = util.fillPath(opts.apiPrefix, $routeParams)
  71. this.appPrefix = util.fillPath(opts.appPrefix, $routeParams)
  72. const crud = api.crud(this.apiPrefix)
  73. this.template = template
  74. this.selected = []
  75. this.data = []
  76. this.getRecords = () => {
  77. this.promise = crud.list().then(data => {
  78. this.data = data
  79. })
  80. }
  81. this.delete = async (record) => {
  82. try {
  83. await crud.delete(record.id)
  84. $mdToast.showSimple(`${opts.titleName} deleted.`)
  85. this.data = this.data.filter(x => x.id !== record.id)
  86. } catch (err) {
  87. console.error(err)
  88. $mdToast.showSimple(`Could not delete ${opts.titleName}: ${err.message || err}`)
  89. }
  90. }
  91. this.getRecords()
  92. /* Autocomplete fields */
  93. this.autocomplete = {}
  94. opts.columns.filter(c => c.type === 'autocomplete').forEach(c => {
  95. const crud = api.crud(util.fillPath(c.apiPrefix, $routeParams))
  96. let timer = null
  97. let ids = []
  98. const load = () => {
  99. timer = null
  100. console.log('Loading', ids)
  101. crud.lookup(ids).then(records => {
  102. records.forEach(rec => {
  103. ac.lookup[rec.id] = rec.name || rec.key || rec.id
  104. })
  105. })
  106. }
  107. const ac = this.autocomplete[c.camelName] = {
  108. lookup: {},
  109. load(id) {
  110. if (!ac.lookup.hasOwnProperty(id)) {
  111. console.log('Queuing to load', id)
  112. ac.lookup[id] = '...'
  113. if (!ids.includes(id)) ids.push(id)
  114. if (!timer) timer = setTimeout(load)
  115. }
  116. return ac.lookup[id]
  117. }
  118. }
  119. })
  120. }
  121. })
  122. }
  123. module.exports = list