The cog package provides functionality to develop reusable components (cogs) for Isomorphic Go web applications.

uxcog.go 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // The UXToolkit Project
  2. // Copyright (c) Wirecog, LLC. All rights reserved.
  3. // Use of this source code is governed by a BSD-style
  4. // license, which can be found in the LICENSE file.
  5. package cog
  6. import (
  7. "errors"
  8. "reflect"
  9. "strings"
  10. "github.com/isomorphicgo/isokit"
  11. "github.com/uxtoolkit/reconcile"
  12. _ "golang.org/x/net/html"
  13. "honnef.co/go/js/dom"
  14. )
  15. type UXCog struct {
  16. Cog
  17. cogType reflect.Type
  18. cogPrefixName string
  19. cogPackagePath string
  20. cogTemplatePath string
  21. templateSet *isokit.TemplateSet
  22. Props map[string]interface{}
  23. element *dom.Element
  24. id string
  25. hasBeenRendered bool
  26. parseTree *reconcile.ParseTree
  27. }
  28. func (u *UXCog) getCogPrefixName() string {
  29. if u.cogType != nil {
  30. result := strings.Split(u.cogType.PkgPath(), `/`)
  31. return "cog:" + result[len(result)-1]
  32. } else {
  33. return ""
  34. }
  35. }
  36. func (u *UXCog) ID() string {
  37. return u.id
  38. }
  39. func (u *UXCog) SetID(id string) {
  40. u.id = id
  41. }
  42. func (u *UXCog) SetElement(element *dom.Element) {
  43. u.element = element
  44. }
  45. func (u *UXCog) Element() *dom.Element {
  46. return u.element
  47. }
  48. func (u *UXCog) CogInit(ts *isokit.TemplateSet) {
  49. u.hasBeenRendered = false
  50. u.Props = make(map[string]interface{})
  51. if ts != nil {
  52. u.templateSet = ts
  53. }
  54. u.cogTemplatePath = DefaultGoSourcePath + "/" + u.cogType.PkgPath() + "/" + DefaultTemplatesDirectoryName
  55. u.cogPrefixName = u.getCogPrefixName()
  56. if isokit.OperatingEnvironment() == isokit.ServerEnvironment {
  57. u.RegisterCogTemplates()
  58. }
  59. }
  60. func (u *UXCog) TemplateSet() *isokit.TemplateSet {
  61. return u.templateSet
  62. }
  63. func (u *UXCog) SetTemplateSet(ts *isokit.TemplateSet) {
  64. u.templateSet = ts
  65. }
  66. func (u *UXCog) CogType() reflect.Type {
  67. return u.cogType
  68. }
  69. func (u *UXCog) SetCogType(cogType reflect.Type) {
  70. u.cogType = cogType
  71. }
  72. func (u *UXCog) CogTemplatePath() string {
  73. return u.cogTemplatePath
  74. }
  75. func (u *UXCog) SetCogTemplatePath(path string) {
  76. u.cogTemplatePath = path
  77. }
  78. func (u *UXCog) RegisterCogTemplates() {
  79. u.templateSet.GatherCogTemplates(u.cogTemplatePath, u.cogPrefixName, ".tmpl")
  80. }
  81. func (u *UXCog) GetProps() map[string]interface{} {
  82. return u.Props
  83. }
  84. func (u *UXCog) SetProp(key string, value interface{}) {
  85. u.Props[key] = value
  86. if ReactivityEnabled == true && u.hasBeenRendered == true {
  87. u.Render()
  88. }
  89. }
  90. func (u *UXCog) BatchPropUpdate(props map[string]interface{}) {
  91. for k, v := range props {
  92. u.Props[k] = v
  93. }
  94. if ReactivityEnabled == true && u.hasBeenRendered == true {
  95. u.Render()
  96. }
  97. }
  98. func (u *UXCog) RenderCogTemplate() {
  99. var populateRenderedContent bool
  100. if u.hasBeenRendered == false {
  101. populateRenderedContent = true
  102. } else {
  103. populateRenderedContent = false
  104. }
  105. rp := isokit.RenderParams{Data: u.Props, Disposition: isokit.PlacementReplaceInnerContents, Element: *u.element, ShouldPopulateRenderedContent: populateRenderedContent}
  106. u.templateSet.Render(u.getCogPrefixName()+"/"+strings.Split(u.getCogPrefixName(), ":")[1], &rp)
  107. if u.hasBeenRendered == false {
  108. u.hasBeenRendered = true
  109. D := dom.GetWindow().Document()
  110. cogRoot := D.GetElementByID(u.id).FirstChild().(*dom.HTMLDivElement)
  111. contents := cogRoot.InnerHTML()
  112. parseTree, err := reconcile.NewParseTree([]byte(contents))
  113. if err != nil {
  114. println("Encountered an error: ", err)
  115. } else {
  116. u.parseTree = parseTree
  117. }
  118. }
  119. }
  120. func (u *UXCog) Render() error {
  121. document := dom.GetWindow().Document()
  122. e := document.GetElementByID(u.ID())
  123. if strings.ToLower(e.GetAttribute("data-component")) != "cog" {
  124. return errors.New("The cog container div must have a \"data-component\" attribute with a value specified as \"cog\".")
  125. }
  126. if u.hasBeenRendered == false {
  127. // Initial Render
  128. u.SetElement(&e)
  129. u.RenderCogTemplate()
  130. return nil
  131. } else if u.element != nil {
  132. // Re-render
  133. if VDOMEnabled == true {
  134. rp := isokit.RenderParams{Data: u.Props, Disposition: isokit.PlacementReplaceInnerContents, Element: *u.element, ShouldPopulateRenderedContent: true, ShouldSkipFinalRenderStep: true}
  135. u.templateSet.Render(u.getCogPrefixName()+"/"+strings.Split(u.getCogPrefixName(), ":")[1], &rp)
  136. D := dom.GetWindow().Document()
  137. cogRoot := D.GetElementByID(u.id).FirstChild().(*dom.HTMLDivElement)
  138. //contents := cogRoot.InnerHTML()
  139. newTree, err := reconcile.NewParseTree([]byte(rp.RenderedContent))
  140. if err != nil {
  141. println("Encountered an error: ", err)
  142. }
  143. changes, err := u.parseTree.Compare(newTree)
  144. if err != nil {
  145. println("Encountered an error: ", err)
  146. }
  147. if len(changes) > 0 {
  148. changes.ApplyChanges(cogRoot)
  149. u.parseTree = newTree
  150. }
  151. } else {
  152. u.RenderCogTemplate()
  153. }
  154. return nil
  155. }
  156. return nil
  157. }