field.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871
  1. package presets
  2. import (
  3. "context"
  4. "fmt"
  5. "log"
  6. "net/http"
  7. "reflect"
  8. "sort"
  9. "strconv"
  10. "strings"
  11. "time"
  12. "unicode"
  13. v "github.com/qor5/ui/vuetify"
  14. "github.com/qor5/web"
  15. "github.com/qor5/x/i18n"
  16. "github.com/sunfmin/reflectutils"
  17. h "github.com/theplant/htmlgo"
  18. funk "github.com/thoas/go-funk"
  19. )
  20. type FieldContext struct {
  21. Name string
  22. FormKey string
  23. Label string
  24. Errors []string
  25. ModelInfo *ModelInfo
  26. NestedFieldsBuilder *FieldsBuilder
  27. Context context.Context
  28. Disabled bool
  29. }
  30. func (fc *FieldContext) StringValue(obj interface{}) (r string) {
  31. val := fc.Value(obj)
  32. switch vt := val.(type) {
  33. case []rune:
  34. return string(vt)
  35. case []byte:
  36. return string(vt)
  37. case time.Time:
  38. return vt.Format("2006-01-02 15:04:05")
  39. case *time.Time:
  40. if vt == nil {
  41. return ""
  42. }
  43. return vt.Format("2006-01-02 15:04:05")
  44. }
  45. return fmt.Sprint(val)
  46. }
  47. func (fc *FieldContext) Value(obj interface{}) (r interface{}) {
  48. fieldName := fc.Name
  49. return reflectutils.MustGet(obj, fieldName)
  50. }
  51. func (fc *FieldContext) ContextValue(key interface{}) (r interface{}) {
  52. if fc.Context == nil {
  53. return
  54. }
  55. return fc.Context.Value(key)
  56. }
  57. type FieldBuilder struct {
  58. NameLabel
  59. compFunc FieldComponentFunc
  60. setterFunc FieldSetterFunc
  61. context context.Context
  62. rt reflect.Type
  63. nestedFieldsBuilder *FieldsBuilder
  64. }
  65. func (b *FieldsBuilder) appendNewFieldWithName(name string) (r *FieldBuilder) {
  66. r = &FieldBuilder{}
  67. if b.model == nil {
  68. panic("model must be provided")
  69. }
  70. fType := reflectutils.GetType(b.model, name)
  71. if fType == nil {
  72. fType = reflect.TypeOf("")
  73. }
  74. r.rt = fType
  75. // if b.defaults == nil {
  76. // panic("field defaults must be provided")
  77. // }
  78. // ft := b.defaults.fieldTypeByTypeOrCreate(fType)
  79. r.name = name
  80. // r.ComponentFunc(ft.compFunc).
  81. // SetterFunc(ft.setterFunc)
  82. b.fields = append(b.fields, r)
  83. return
  84. }
  85. func emptyComponentFunc(obj interface{}, field *FieldContext, ctx *web.EventContext) (r h.HTMLComponent) {
  86. log.Printf("No ComponentFunc for field %v\n", field.Name)
  87. return
  88. }
  89. func (b *FieldBuilder) Label(v string) (r *FieldBuilder) {
  90. b.label = v
  91. return b
  92. }
  93. func (b *FieldBuilder) Clone() (r *FieldBuilder) {
  94. r = &FieldBuilder{}
  95. r.name = b.name
  96. r.label = b.label
  97. r.compFunc = b.compFunc
  98. r.setterFunc = b.setterFunc
  99. return r
  100. }
  101. func (b *FieldBuilder) ComponentFunc(v FieldComponentFunc) (r *FieldBuilder) {
  102. if v == nil {
  103. panic("value required")
  104. }
  105. b.compFunc = v
  106. return b
  107. }
  108. func (b *FieldBuilder) SetterFunc(v FieldSetterFunc) (r *FieldBuilder) {
  109. b.setterFunc = v
  110. return b
  111. }
  112. func (b *FieldBuilder) WithContextValue(key interface{}, val interface{}) (r *FieldBuilder) {
  113. if b.context == nil {
  114. b.context = context.Background()
  115. }
  116. b.context = context.WithValue(b.context, key, val)
  117. return b
  118. }
  119. type NestedConfig interface {
  120. nested()
  121. }
  122. type DisplayFieldInSorter struct {
  123. Field string
  124. }
  125. func (i *DisplayFieldInSorter) nested() {}
  126. type AddListItemRowEvent struct {
  127. Event string
  128. }
  129. func (*AddListItemRowEvent) nested() {}
  130. type RemoveListItemRowEvent struct {
  131. Event string
  132. }
  133. func (*RemoveListItemRowEvent) nested() {}
  134. type SortListItemsEvent struct {
  135. Event string
  136. }
  137. func (*SortListItemsEvent) nested() {}
  138. func (b *FieldBuilder) Nested(fb *FieldsBuilder, cfgs ...NestedConfig) (r *FieldBuilder) {
  139. b.nestedFieldsBuilder = fb
  140. switch b.rt.Kind() {
  141. case reflect.Slice:
  142. var displayFieldInSorter string
  143. var addListItemRowEvent string
  144. var removeListItemRowEvent string
  145. var sortListItemsEvent string
  146. for _, cfg := range cfgs {
  147. switch t := cfg.(type) {
  148. case *DisplayFieldInSorter:
  149. displayFieldInSorter = t.Field
  150. case *AddListItemRowEvent:
  151. addListItemRowEvent = t.Event
  152. case *RemoveListItemRowEvent:
  153. removeListItemRowEvent = t.Event
  154. case *SortListItemsEvent:
  155. sortListItemsEvent = t.Event
  156. default:
  157. panic("unknown nested config")
  158. }
  159. }
  160. b.ComponentFunc(func(obj interface{}, field *FieldContext, ctx *web.EventContext) h.HTMLComponent {
  161. return NewListEditor(field).Value(field.Value(obj)).
  162. DisplayFieldInSorter(displayFieldInSorter).
  163. AddListItemRowEvnet(addListItemRowEvent).
  164. RemoveListItemRowEvent(removeListItemRowEvent).
  165. SortListItemsEvent(sortListItemsEvent)
  166. })
  167. default:
  168. b.ComponentFunc(func(obj interface{}, field *FieldContext, ctx *web.EventContext) h.HTMLComponent {
  169. val := field.Value(obj)
  170. if val == nil {
  171. t := reflectutils.GetType(obj, field.Name).Elem()
  172. val = reflect.New(t).Interface()
  173. }
  174. modifiedIndexes := ContextModifiedIndexesBuilder(ctx)
  175. body := b.nestedFieldsBuilder.toComponentWithFormValueKey(field.ModelInfo, val, field.FormKey, modifiedIndexes, ctx)
  176. return h.Div(
  177. h.Label(field.Label).Class("v-label theme--light text-caption"),
  178. v.VCard(body).Outlined(true).Class("mx-0 mt-1 mb-4 px-4 pb-0 pt-4"),
  179. )
  180. })
  181. }
  182. return b
  183. }
  184. type NameLabel struct {
  185. name string
  186. label string
  187. }
  188. type FieldsBuilder struct {
  189. model interface{}
  190. defaults *FieldDefaults
  191. fieldLabels []string
  192. fields []*FieldBuilder
  193. // string / []string / *FieldsSection
  194. fieldsLayout []interface{}
  195. }
  196. type FieldsSection struct {
  197. Title string
  198. Rows [][]string
  199. }
  200. func NewFieldsBuilder() *FieldsBuilder {
  201. return &FieldsBuilder{}
  202. }
  203. func (b *FieldsBuilder) Defaults(v *FieldDefaults) (r *FieldsBuilder) {
  204. b.defaults = v
  205. return b
  206. }
  207. func (b *FieldsBuilder) Unmarshal(toObj interface{}, info *ModelInfo, removeDeletedAndSort bool, ctx *web.EventContext) (vErr web.ValidationErrors) {
  208. t := reflect.TypeOf(toObj)
  209. if t.Kind() != reflect.Ptr {
  210. panic("toObj must be pointer")
  211. }
  212. var fromObj = reflect.New(t.Elem()).Interface()
  213. // don't panic for fields that set in SetterFunc
  214. _ = ctx.UnmarshalForm(fromObj)
  215. // testingutils.PrintlnJson("Unmarshal fromObj", fromObj)
  216. modifiedIndexes := ContextModifiedIndexesBuilder(ctx).FromHidden(ctx.R)
  217. return b.SetObjectFields(fromObj, toObj, &FieldContext{
  218. ModelInfo: info,
  219. }, removeDeletedAndSort, modifiedIndexes, ctx)
  220. }
  221. func (b *FieldsBuilder) SetObjectFields(fromObj interface{}, toObj interface{}, parent *FieldContext, removeDeletedAndSort bool, modifiedIndexes *ModifiedIndexesBuilder, ctx *web.EventContext) (vErr web.ValidationErrors) {
  222. for _, f := range b.fields {
  223. info := parent.ModelInfo
  224. if info != nil {
  225. if info.Verifier().Do(PermCreate).ObjectOn(toObj).SnakeOn("f_"+f.name).WithReq(ctx.R).IsAllowed() != nil && info.Verifier().Do(PermUpdate).ObjectOn(toObj).SnakeOn("f_"+f.name).WithReq(ctx.R).IsAllowed() != nil {
  226. continue
  227. }
  228. }
  229. if f.nestedFieldsBuilder != nil {
  230. formKey := f.name
  231. if parent != nil && parent.FormKey != "" {
  232. formKey = fmt.Sprintf("%s.%s", parent.FormKey, f.name)
  233. }
  234. switch f.rt.Kind() {
  235. case reflect.Slice:
  236. b.setWithChildFromObjs(fromObj, formKey, f, info, modifiedIndexes, toObj, removeDeletedAndSort, ctx)
  237. b.setToObjNilOrDelete(toObj, formKey, f, modifiedIndexes, removeDeletedAndSort)
  238. continue
  239. default:
  240. pf := &FieldContext{
  241. ModelInfo: info,
  242. FormKey: formKey,
  243. }
  244. rt := reflectutils.GetType(toObj, f.name)
  245. childFromObj := reflectutils.MustGet(fromObj, f.name)
  246. if childFromObj == nil {
  247. childFromObj = reflect.New(rt.Elem()).Interface()
  248. }
  249. childToObj := reflectutils.MustGet(toObj, f.name)
  250. if childToObj == nil {
  251. childToObj = reflect.New(rt.Elem()).Interface()
  252. }
  253. if rt.Kind() == reflect.Struct {
  254. prv := reflect.New(rt)
  255. prv.Elem().Set(reflect.ValueOf(childToObj))
  256. childToObj = prv.Interface()
  257. }
  258. f.nestedFieldsBuilder.SetObjectFields(childFromObj, childToObj, pf, removeDeletedAndSort, modifiedIndexes, ctx)
  259. if err := reflectutils.Set(toObj, f.name, childToObj); err != nil {
  260. panic(err)
  261. }
  262. continue
  263. }
  264. }
  265. val, err1 := reflectutils.Get(fromObj, f.name)
  266. if err1 == nil {
  267. reflectutils.Set(toObj, f.name, val)
  268. }
  269. if f.setterFunc == nil {
  270. continue
  271. }
  272. keyPath := f.name
  273. if parent != nil && parent.FormKey != "" {
  274. keyPath = fmt.Sprintf("%s.%s", parent.FormKey, f.name)
  275. }
  276. err1 = f.setterFunc(toObj, &FieldContext{
  277. ModelInfo: info,
  278. FormKey: keyPath,
  279. Name: f.name,
  280. Label: b.getLabel(f.NameLabel),
  281. }, ctx)
  282. if err1 != nil {
  283. vErr.FieldError(f.name, err1.Error())
  284. }
  285. }
  286. return
  287. }
  288. func (b *FieldsBuilder) setToObjNilOrDelete(toObj interface{}, formKey string, f *FieldBuilder, modifiedIndexes *ModifiedIndexesBuilder, removeDeletedAndSort bool) {
  289. if !removeDeletedAndSort {
  290. if modifiedIndexes.deletedValues != nil && modifiedIndexes.deletedValues[formKey] != nil {
  291. for _, idx := range modifiedIndexes.deletedValues[formKey] {
  292. sliceFieldName := fmt.Sprintf("%s[%s]", f.name, idx)
  293. err := reflectutils.Set(toObj, sliceFieldName, nil)
  294. if err != nil {
  295. panic(err)
  296. }
  297. }
  298. }
  299. return
  300. }
  301. childToObjs := reflectutils.MustGet(toObj, f.name)
  302. if childToObjs == nil {
  303. return
  304. }
  305. t := reflectutils.GetType(toObj, f.name)
  306. newSlice := reflect.MakeSlice(t, 0, 0)
  307. modifiedIndexes.SortedForEach(childToObjs, formKey, func(obj interface{}, i int) {
  308. // remove deleted
  309. if modifiedIndexes.DeletedContains(formKey, i) {
  310. return
  311. }
  312. newSlice = reflect.Append(newSlice, reflect.ValueOf(obj))
  313. })
  314. err := reflectutils.Set(toObj, f.name, newSlice.Interface())
  315. if err != nil {
  316. panic(err)
  317. }
  318. return
  319. }
  320. func (b *FieldsBuilder) setWithChildFromObjs(
  321. fromObj interface{},
  322. formKey string,
  323. f *FieldBuilder,
  324. info *ModelInfo,
  325. modifiedIndexes *ModifiedIndexesBuilder,
  326. toObj interface{},
  327. removeDeletedAndSort bool,
  328. ctx *web.EventContext) {
  329. childFromObjs := reflectutils.MustGet(fromObj, f.name)
  330. if childFromObjs == nil || reflect.TypeOf(childFromObjs).Kind() != reflect.Slice {
  331. return
  332. }
  333. var i = 0
  334. funk.ForEach(childFromObjs, func(childFromObj interface{}) {
  335. defer func() { i++ }()
  336. if childFromObj == nil {
  337. return
  338. }
  339. // if is deleted, do nothing, later, it will be set to nil
  340. if modifiedIndexes.DeletedContains(formKey, i) {
  341. return
  342. }
  343. sliceFieldName := fmt.Sprintf("%s[%d]", f.name, i)
  344. pf := &FieldContext{
  345. ModelInfo: info,
  346. FormKey: fmt.Sprintf("%s[%d]", formKey, i),
  347. }
  348. childToObj := reflectutils.MustGet(toObj, sliceFieldName)
  349. if childToObj == nil {
  350. arrayElementType := reflectutils.GetType(toObj, sliceFieldName)
  351. if arrayElementType.Kind() == reflect.Ptr {
  352. arrayElementType = arrayElementType.Elem()
  353. } else {
  354. panic(fmt.Sprintf("%s must be a pointer", sliceFieldName))
  355. }
  356. err := reflectutils.Set(toObj, sliceFieldName, reflect.New(arrayElementType).Interface())
  357. if err != nil {
  358. panic(err)
  359. }
  360. childToObj = reflectutils.MustGet(toObj, sliceFieldName)
  361. }
  362. // fmt.Printf("childFromObj %#+v\n", childFromObj)
  363. // fmt.Printf("childToObj %#+v\n", childToObj)
  364. // fmt.Printf("fieldContext %#+v\n", pf)
  365. f.nestedFieldsBuilder.SetObjectFields(childFromObj, childToObj, pf, removeDeletedAndSort, modifiedIndexes, ctx)
  366. })
  367. }
  368. func (b *FieldsBuilder) Clone() (r *FieldsBuilder) {
  369. r = &FieldsBuilder{
  370. model: b.model,
  371. defaults: b.defaults,
  372. fieldLabels: b.fieldLabels,
  373. }
  374. return
  375. }
  376. func (b *FieldsBuilder) Model(v interface{}) (r *FieldsBuilder) {
  377. b.model = v
  378. return b
  379. }
  380. func (b *FieldsBuilder) Field(name string) (r *FieldBuilder) {
  381. r = b.getField(name)
  382. if r != nil {
  383. return
  384. }
  385. r = b.appendNewFieldWithName(name)
  386. return
  387. }
  388. func (b *FieldsBuilder) Labels(vs ...string) (r *FieldsBuilder) {
  389. b.fieldLabels = append(b.fieldLabels, vs...)
  390. return b
  391. }
  392. // humanizeString humanize separates string based on capitalizd letters
  393. // e.g. "OrderItem" -> "Order Item, CNNName to CNN Name"
  394. func humanizeString(str string) string {
  395. var human []rune
  396. input := []rune(str)
  397. for i, l := range input {
  398. if i > 0 && unicode.IsUpper(l) {
  399. if (!unicode.IsUpper(input[i-1]) && input[i-1] != ' ') || (i+1 < len(input) && !unicode.IsUpper(input[i+1]) && input[i+1] != ' ' && input[i-1] != ' ') {
  400. human = append(human, rune(' '))
  401. }
  402. }
  403. human = append(human, l)
  404. }
  405. return strings.Title(string(human))
  406. }
  407. func (b *FieldsBuilder) getLabel(field NameLabel) (r string) {
  408. if len(field.label) > 0 {
  409. return field.label
  410. }
  411. for i := 0; i < len(b.fieldLabels)-1; i = i + 2 {
  412. if b.fieldLabels[i] == field.name {
  413. return b.fieldLabels[i+1]
  414. }
  415. }
  416. return humanizeString(field.name)
  417. }
  418. func (b *FieldsBuilder) getFieldOrDefault(name string) (r *FieldBuilder) {
  419. r = b.getField(name)
  420. if r.compFunc == nil {
  421. if b.defaults == nil {
  422. panic("field defaults must be provided")
  423. }
  424. fType := reflectutils.GetType(b.model, name)
  425. if fType == nil {
  426. fType = reflect.TypeOf("")
  427. }
  428. ft := b.defaults.fieldTypeByTypeOrCreate(fType)
  429. r.ComponentFunc(ft.compFunc).
  430. SetterFunc(ft.setterFunc)
  431. }
  432. return
  433. }
  434. func (b *FieldsBuilder) getField(name string) (r *FieldBuilder) {
  435. for _, f := range b.fields {
  436. if f.name == name {
  437. return f
  438. }
  439. }
  440. return
  441. }
  442. func (b *FieldsBuilder) Only(vs ...interface{}) (r *FieldsBuilder) {
  443. if len(vs) == 0 {
  444. return b
  445. }
  446. r = b.Clone()
  447. r.fieldsLayout = vs
  448. for _, iv := range vs {
  449. switch t := iv.(type) {
  450. case string:
  451. r.appendFieldAfterClone(b, t)
  452. case []string:
  453. for _, n := range t {
  454. r.appendFieldAfterClone(b, n)
  455. }
  456. case *FieldsSection:
  457. for _, row := range t.Rows {
  458. for _, n := range row {
  459. r.appendFieldAfterClone(b, n)
  460. }
  461. }
  462. default:
  463. panic("unknown fields layout, must be string/[]string/*FieldsSection")
  464. }
  465. }
  466. return
  467. }
  468. func (b *FieldsBuilder) appendFieldAfterClone(ob *FieldsBuilder, name string) {
  469. f := ob.getField(name)
  470. if f == nil {
  471. b.appendNewFieldWithName(name)
  472. } else {
  473. b.fields = append(b.fields, f.Clone())
  474. }
  475. }
  476. func (b *FieldsBuilder) Except(patterns ...string) (r *FieldsBuilder) {
  477. if len(patterns) == 0 {
  478. return
  479. }
  480. r = &FieldsBuilder{fieldLabels: b.fieldLabels}
  481. for _, f := range b.fields {
  482. if hasMatched(patterns, f.name) {
  483. continue
  484. }
  485. r.fields = append(r.fields, f.Clone())
  486. }
  487. return
  488. }
  489. func (b *FieldsBuilder) String() (r string) {
  490. var names []string
  491. for _, f := range b.fields {
  492. names = append(names, f.name)
  493. }
  494. return fmt.Sprint(names)
  495. }
  496. func (b *FieldsBuilder) ToComponent(info *ModelInfo, obj interface{}, ctx *web.EventContext) h.HTMLComponent {
  497. modifiedIndexes := ContextModifiedIndexesBuilder(ctx)
  498. return b.toComponentWithFormValueKey(info, obj, "", modifiedIndexes, ctx)
  499. }
  500. func (b *FieldsBuilder) toComponentWithFormValueKey(info *ModelInfo, obj interface{}, parentFormValueKey string, modifiedIndexes *ModifiedIndexesBuilder, ctx *web.EventContext) h.HTMLComponent {
  501. var comps []h.HTMLComponent
  502. if parentFormValueKey == "" {
  503. comps = append(comps, modifiedIndexes.ToFormHidden())
  504. }
  505. vErr, _ := ctx.Flash.(*web.ValidationErrors)
  506. if vErr == nil {
  507. vErr = &web.ValidationErrors{}
  508. }
  509. id, err := reflectutils.Get(obj, "ID")
  510. edit := false
  511. if err == nil && len(fmt.Sprint(id)) > 0 && fmt.Sprint(id) != "0" {
  512. edit = true
  513. }
  514. layout := b.fieldsLayout
  515. if layout == nil {
  516. layout = make([]interface{}, 0, len(b.fields))
  517. for _, f := range b.fields {
  518. layout = append(layout, f.name)
  519. }
  520. }
  521. for _, iv := range layout {
  522. var comp h.HTMLComponent
  523. switch t := iv.(type) {
  524. case string:
  525. comp = b.fieldToComponentWithFormValueKey(info, obj, parentFormValueKey, ctx, t, id, edit, vErr)
  526. case []string:
  527. colsComp := make([]h.HTMLComponent, 0, len(t))
  528. for _, n := range t {
  529. fComp := b.fieldToComponentWithFormValueKey(info, obj, parentFormValueKey, ctx, n, id, edit, vErr)
  530. if fComp == nil {
  531. continue
  532. }
  533. colsComp = append(colsComp, v.VCol(fComp).Class("pr-4"))
  534. }
  535. if len(colsComp) > 0 {
  536. comp = v.VRow(colsComp...).NoGutters(true)
  537. }
  538. case *FieldsSection:
  539. rowsComp := make([]h.HTMLComponent, 0, len(t.Rows))
  540. for _, row := range t.Rows {
  541. colsComp := make([]h.HTMLComponent, 0, len(row))
  542. for _, n := range row {
  543. fComp := b.fieldToComponentWithFormValueKey(info, obj, parentFormValueKey, ctx, n, id, edit, vErr)
  544. if fComp == nil {
  545. continue
  546. }
  547. colsComp = append(colsComp, v.VCol(fComp).Class("pr-4"))
  548. }
  549. if len(colsComp) > 0 {
  550. rowsComp = append(rowsComp, v.VRow(colsComp...).NoGutters(true))
  551. }
  552. }
  553. if len(rowsComp) > 0 {
  554. var titleComp h.HTMLComponent
  555. if t.Title != "" {
  556. titleComp = h.Label(t.Title).Class("v-label theme--light text-caption")
  557. }
  558. comp = h.Div(
  559. titleComp,
  560. v.VCard(rowsComp...).Outlined(true).Class("mx-0 mt-1 mb-4 px-4 pb-0 pt-4"),
  561. )
  562. }
  563. default:
  564. panic("unknown fields layout, must be string/[]string/*FieldsSection")
  565. }
  566. if comp == nil {
  567. continue
  568. }
  569. comps = append(comps, comp)
  570. }
  571. return h.Components(comps...)
  572. }
  573. func (b *FieldsBuilder) fieldToComponentWithFormValueKey(info *ModelInfo, obj interface{}, parentFormValueKey string, ctx *web.EventContext, name string, id interface{}, edit bool, vErr *web.ValidationErrors) h.HTMLComponent {
  574. f := b.getFieldOrDefault(name)
  575. // if f.compFunc == nil {
  576. // return nil
  577. // }
  578. if info != nil && info.Verifier().Do(PermGet).ObjectOn(obj).SnakeOn("f_"+f.name).WithReq(ctx.R).IsAllowed() != nil {
  579. return nil
  580. }
  581. label := b.getLabel(f.NameLabel)
  582. if info != nil {
  583. label = i18n.PT(ctx.R, ModelsI18nModuleKey, info.Label(), b.getLabel(f.NameLabel))
  584. }
  585. contextKeyPath := f.name
  586. if parentFormValueKey != "" {
  587. contextKeyPath = fmt.Sprintf("%s.%s", parentFormValueKey, f.name)
  588. }
  589. disabled := false
  590. if info != nil {
  591. if edit {
  592. disabled = info.Verifier().Do(PermUpdate).ObjectOn(obj).SnakeOn("f_"+f.name).WithReq(ctx.R).IsAllowed() != nil
  593. } else {
  594. disabled = info.Verifier().Do(PermCreate).ObjectOn(obj).SnakeOn("f_"+f.name).WithReq(ctx.R).IsAllowed() != nil
  595. }
  596. }
  597. return f.compFunc(obj, &FieldContext{
  598. ModelInfo: info,
  599. Name: f.name,
  600. FormKey: contextKeyPath,
  601. Label: label,
  602. Errors: vErr.GetFieldErrors(f.name),
  603. NestedFieldsBuilder: f.nestedFieldsBuilder,
  604. Context: f.context,
  605. Disabled: disabled,
  606. }, ctx)
  607. }
  608. type RowFunc func(obj interface{}, formKey string, content h.HTMLComponent, ctx *web.EventContext) h.HTMLComponent
  609. func defaultRowFunc(obj interface{}, formKey string, content h.HTMLComponent, ctx *web.EventContext) h.HTMLComponent {
  610. return content
  611. }
  612. func (b *FieldsBuilder) ToComponentForEach(field *FieldContext, slice interface{}, ctx *web.EventContext, rowFunc RowFunc) h.HTMLComponent {
  613. var info *ModelInfo
  614. var parentKeyPath = ""
  615. if field != nil {
  616. info = field.ModelInfo
  617. parentKeyPath = field.FormKey
  618. }
  619. if rowFunc == nil {
  620. rowFunc = defaultRowFunc
  621. }
  622. var r []h.HTMLComponent
  623. modifiedIndexes := ContextModifiedIndexesBuilder(ctx)
  624. modifiedIndexes.SortedForEach(slice, parentKeyPath, func(obj interface{}, i int) {
  625. if modifiedIndexes.DeletedContains(parentKeyPath, i) {
  626. return
  627. }
  628. formKey := fmt.Sprintf("%s[%d]", parentKeyPath, i)
  629. comps := b.toComponentWithFormValueKey(info, obj, formKey, modifiedIndexes, ctx)
  630. r = append(r, rowFunc(obj, formKey, comps, ctx))
  631. })
  632. return h.Components(r...)
  633. }
  634. type ModifiedIndexesBuilder struct {
  635. deletedValues map[string][]string
  636. sortedValues map[string][]string
  637. }
  638. type deletedIndexBuilderKeyType int
  639. const theDeleteIndexBuilderKey deletedIndexBuilderKeyType = iota
  640. const deletedHiddenNamePrefix = "__Deleted"
  641. const sortedHiddenNamePrefix = "__Sorted"
  642. func ContextModifiedIndexesBuilder(ctx *web.EventContext) (r *ModifiedIndexesBuilder) {
  643. r, ok := ctx.R.Context().Value(theDeleteIndexBuilderKey).(*ModifiedIndexesBuilder)
  644. if !ok {
  645. r = &ModifiedIndexesBuilder{}
  646. ctx.R = ctx.R.WithContext(context.WithValue(ctx.R.Context(), theDeleteIndexBuilderKey, r))
  647. }
  648. return r
  649. }
  650. func (b *ModifiedIndexesBuilder) AppendDeleted(sliceFormKey string, index int) (r *ModifiedIndexesBuilder) {
  651. if b.deletedValues == nil {
  652. b.deletedValues = make(map[string][]string)
  653. }
  654. b.deletedValues[sliceFormKey] = append(b.deletedValues[sliceFormKey], fmt.Sprint(index))
  655. return b
  656. }
  657. func (b *ModifiedIndexesBuilder) SetSorted(sliceFormKey string, indexes []string) (r *ModifiedIndexesBuilder) {
  658. if b.sortedValues == nil {
  659. b.sortedValues = make(map[string][]string)
  660. }
  661. b.sortedValues[sliceFormKey] = indexes
  662. return b
  663. }
  664. func (b *ModifiedIndexesBuilder) DeletedContains(sliceFormKey string, index int) (r bool) {
  665. if b.deletedValues == nil {
  666. return false
  667. }
  668. if b.deletedValues[sliceFormKey] == nil {
  669. return false
  670. }
  671. sIndex := fmt.Sprint(index)
  672. for _, v := range b.deletedValues[sliceFormKey] {
  673. if v == sIndex {
  674. return true
  675. }
  676. }
  677. return false
  678. }
  679. func (b *ModifiedIndexesBuilder) SortedForEach(slice interface{}, sliceFormKey string, f func(obj interface{}, i int)) {
  680. sortedIndexes, ok := b.sortedValues[sliceFormKey]
  681. if !ok {
  682. i := 0
  683. funk.ForEach(slice, func(obj interface{}) {
  684. defer func() { i++ }()
  685. f(obj, i)
  686. })
  687. return
  688. }
  689. sliceLen := reflect.ValueOf(slice).Len()
  690. for j1 := 0; j1 < sliceLen; j1++ {
  691. if funk.Contains(sortedIndexes, fmt.Sprint(j1)) {
  692. continue
  693. }
  694. sortedIndexes = append(sortedIndexes, fmt.Sprint(j1))
  695. }
  696. for _, j := range sortedIndexes {
  697. obj, err := reflectutils.Get(slice, fmt.Sprintf("[%s]", j))
  698. if obj == nil || err != nil {
  699. continue
  700. }
  701. j1, _ := strconv.Atoi(j)
  702. f(obj, j1)
  703. }
  704. }
  705. func deleteHiddenSliceFormKey(sliceFormKey string) string {
  706. return deletedHiddenNamePrefix + "." + sliceFormKey
  707. }
  708. func sortedHiddenSliceFormKey(sliceFormKey string) string {
  709. return sortedHiddenNamePrefix + "." + sliceFormKey
  710. }
  711. func (b *ModifiedIndexesBuilder) FromHidden(req *http.Request) (r *ModifiedIndexesBuilder) {
  712. if b.deletedValues == nil {
  713. b.deletedValues = make(map[string][]string)
  714. }
  715. if b.sortedValues == nil {
  716. b.sortedValues = make(map[string][]string)
  717. }
  718. for k, vs := range req.Form {
  719. if strings.HasPrefix(k, deletedHiddenNamePrefix) {
  720. b.deletedValues[k[len(deletedHiddenNamePrefix)+1:]] = strings.Split(vs[0], ",")
  721. }
  722. if strings.HasPrefix(k, sortedHiddenNamePrefix) {
  723. b.sortedValues[k[len(sortedHiddenNamePrefix)+1:]] = strings.Split(vs[0], ",")
  724. }
  725. }
  726. return b
  727. }
  728. func (b *ModifiedIndexesBuilder) ReversedmodifiedIndexes(sliceFormKey string) (r []int) {
  729. if b.deletedValues == nil {
  730. return
  731. }
  732. for _, v := range b.deletedValues[sliceFormKey] {
  733. i, err := strconv.Atoi(v)
  734. if err != nil {
  735. panic(err)
  736. }
  737. r = append(r, i)
  738. }
  739. sort.Sort(sort.Reverse(sort.IntSlice(r)))
  740. return
  741. }
  742. func (b *ModifiedIndexesBuilder) ToFormHidden() h.HTMLComponent {
  743. var hidden []h.HTMLComponent
  744. for sliceFormKey, values := range b.deletedValues {
  745. hidden = append(hidden, h.Input("").Type("hidden").
  746. Attr(web.VFieldName(deleteHiddenSliceFormKey(sliceFormKey))...).
  747. Value(strings.Join(values, ",")))
  748. }
  749. for sliceFormKey, values := range b.sortedValues {
  750. hidden = append(hidden, h.Input("").Type("hidden").
  751. Attr(web.VFieldName(sortedHiddenSliceFormKey(sliceFormKey))...).
  752. Value(strings.Join(values, ",")))
  753. }
  754. return h.Components(hidden...)
  755. }