details.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. const _ = require('lodash')
  2. const app = require('../../app')
  3. /**
  4. * @param {CrudPagesOptions} opts
  5. */
  6. const details = (opts) => {
  7. const autocompleteInput = column => {
  8. if (!column.apiPrefix) throw new Error('apiPrefix is required for autocomplete fields')
  9. return html`
  10. <md-autocomplete flex
  11. md-selected-item="ctrl.autocomplete.${raw(column.camelName)}.selectedItem"
  12. md-search-text="ctrl.autocomplete.${raw(column.camelName)}.searchText"
  13. md-items="item in ctrl.autocomplete.${raw(column.camelName)}.getItems(ctrl.autocomplete.${raw(column.camelName)}.searchText)"
  14. md-item-text="item.name || item.key || item.id"
  15. md-require-match="true"
  16. md-selected-item-change="ctrl.autocomplete.${raw(column.camelName)}.onChange()"
  17. md-min-length="0"s
  18. placeholder="${column.titleName}"
  19. >
  20. <md-item-template>
  21. <span md-highlight-text="ctrl.searchText.${raw(column.camelName)}" md-highlight-flags="^i">{{item.name || item.key || item.id}}</span>
  22. </md-item-template>
  23. </md-autocomplete>
  24. `
  25. }
  26. const standardInput = column => html`
  27. <md-input-container flex>
  28. <label>${column.titleName}</label>
  29. <input type="${column.type || 'text'}" ng-model="model.${raw(column.camelName)}" />
  30. </md-input-container>
  31. `
  32. const defaultField = column =>
  33. column.type === 'autocomplete'
  34. ? autocompleteInput(column)
  35. : standardInput(column)
  36. const layout = () => {
  37. if (opts.layout) {
  38. const cols = _.fromPairs(opts.columns.map(col => [col.camelName, col]))
  39. return opts.layout.map(section => html`
  40. ${section.section ? html`<md-subheader>${section.section}</md-subheader>`:''}
  41. ${raw(section.rows ? section.rows.map(
  42. row => html`
  43. <div layout-gt-sm="row" layout-padding>
  44. ${raw(row
  45. .map(field => cols[field])
  46. .map(c => c.field || defaultField(c))
  47. .join('\n')
  48. )}
  49. </div>
  50. `).join('\n')
  51. : '')}
  52. `).join('\n')
  53. } else {
  54. return html`
  55. <div layout-gt-sm="row" layout-padding>
  56. ${opts.columns.map(c => c.field || defaultField(c))}
  57. </div>
  58. `
  59. }
  60. }
  61. const template = html`
  62. <app-user-area>
  63. <h1>{{ctrl.isNew ? 'New ${opts.titleName}' : '${opts.titleName} Details'}}</h1>
  64. <form name="form" ng-submit="ctrl.submit()">
  65. ${raw(layout())}
  66. <div>
  67. <md-button type="submit" class="md-raised md-primary">Submit</md-button>
  68. </div>
  69. </form>
  70. </app-user-area>
  71. `
  72. console.log(`crud: app${opts.pascalName}DetailsPage`)
  73. app.component(`app${opts.pascalName}DetailsPage`, {
  74. template,
  75. controllerAs: 'ctrl',
  76. controller: function(api, $scope, $routeParams, $mdToast, $location, $q) {
  77. this.template = template // For inspection purposes
  78. this.isNew = $routeParams[opts.routeParam] === 'new'
  79. const crud = api.crud(opts.apiPrefix)
  80. let original
  81. if (this.isNew) {
  82. original = {}
  83. $scope.model = Object.create(original)
  84. this.loadingPromise = $q.resolve($scope.model)
  85. } else {
  86. this.loadingPromise = crud.read($routeParams[opts.routeParam]).then(model => {
  87. original = model
  88. $scope.model = Object.create(original)
  89. return $scope.model
  90. })
  91. }
  92. this.submit = async () => {
  93. try {
  94. if (this.isNew) {
  95. await crud.create($scope.model)
  96. } else {
  97. const obj = {}
  98. for (var key in $scope.model) {
  99. if ($scope.model.hasOwnProperty(key)) {
  100. obj[key] = $scope.model[key]
  101. }
  102. }
  103. await crud.update(original.id, obj)
  104. }
  105. $mdToast.showSimple(`${opts.titleName} saved.`)
  106. $location.url(opts.appPrefix)
  107. } catch (err) {
  108. console.error(err)
  109. $mdToast.showSimple(`Could not save ${opts.titleName}: ${err.message || err}`)
  110. }
  111. }
  112. /* Autocomplete fields */
  113. this.searchText = {}
  114. this.autocomplete = {}
  115. opts.columns.filter(c => c.type === 'autocomplete').forEach(c => {
  116. const crud = api.crud(c.apiPrefix)
  117. const ac = this.autocomplete[c.camelName] = {
  118. onChange() {
  119. $scope.model[c.camelName] = ac.selectedItem
  120. ? ac.selectedItem.id
  121. : null
  122. },
  123. getItems(searchText) {
  124. return crud.autocomplete(searchText)
  125. }
  126. }
  127. this.loadingPromise.then(model => {
  128. if (model[c.camelName]) {
  129. crud.read(model[c.camelName]).then(record => {
  130. ac.selectedItem = record
  131. })
  132. }
  133. })
  134. })
  135. }
  136. })
  137. }
  138. module.exports = details