Refactor event binding logic in SUI core
This commit is contained in:
parent
258163497a
commit
f32a677265
4 changed files with 92 additions and 52 deletions
|
|
@ -266,6 +266,9 @@ func (script ScriptNode) ComponentHTML(ns string) string {
|
|||
}
|
||||
|
||||
source := fmt.Sprintf(`function %s(){%s};`, script.Component, script.Source)
|
||||
if script.Component == "" {
|
||||
return "<script " + strings.Join(attrs, " ") + ">\n" + script.Source + "\n</script>"
|
||||
}
|
||||
return "<script " + strings.Join(attrs, " ") + ">\n" + source + "\n</script>"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,22 +9,38 @@ import (
|
|||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
var eventMatcher = NewAttrPrefixMatcher(`s:on-`)
|
||||
|
||||
// BindEvent is a method that binds events to the page.
|
||||
func (page *Page) BindEvent(ctx *BuildContext, sel *goquery.Selection, component string, ispage bool) {
|
||||
matcher := NewAttrPrefixMatcher(`s:on-`)
|
||||
sel.FindMatcher(matcher).Each(func(i int, s *goquery.Selection) {
|
||||
func (page *Page) BindEvent(ctx *BuildContext, sel *goquery.Selection, cn string, ispage bool) {
|
||||
|
||||
sel.FindMatcher(eventMatcher).Each(func(i int, s *goquery.Selection) {
|
||||
if comp, has := s.Attr("is"); has && ctx.isJitComponent(comp) {
|
||||
return
|
||||
}
|
||||
script := GetEventScript(&ctx.sequence, s, page.namespace, component, ispage)
|
||||
script := GetEventScript(ctx.sequence, s, page.namespace, cn, "event", ispage)
|
||||
if script != nil {
|
||||
ctx.scripts = append(ctx.scripts, *script)
|
||||
ctx.sequence++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BindEvent is a method that binds events to the component in just-in-time mode.
|
||||
func (parser *TemplateParser) BindEvent(sel *goquery.Selection, ns string, cn string) {
|
||||
sel.FindMatcher(eventMatcher).Each(func(i int, s *goquery.Selection) {
|
||||
script := GetEventScript(parser.sequence, s, ns, cn, "event-jit", false)
|
||||
if script != nil {
|
||||
script.Component = ""
|
||||
script.Parent = "body"
|
||||
parser.scripts = append(parser.scripts, *script)
|
||||
parser.sequence++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// GetEventScript the event script
|
||||
func GetEventScript(sequence *int, sel *goquery.Selection, ns string, cn string, ispage bool) *ScriptNode {
|
||||
func GetEventScript(sequence int, sel *goquery.Selection, ns string, cn string, prefix string, ispage bool) *ScriptNode {
|
||||
|
||||
if len(sel.Nodes) == 0 {
|
||||
return nil
|
||||
|
|
@ -34,9 +50,7 @@ func GetEventScript(sequence *int, sel *goquery.Selection, ns string, cn string,
|
|||
events := map[string]string{}
|
||||
dataUnique := map[string]string{}
|
||||
jsonUnique := map[string]string{}
|
||||
id := fmt.Sprintf("event-%d", *sequence)
|
||||
*sequence = *sequence + 1
|
||||
|
||||
id := fmt.Sprintf("%s-%d", prefix, sequence)
|
||||
for _, attr := range sel.Nodes[0].Attr {
|
||||
|
||||
if strings.HasPrefix(attr.Key, "s:on-") {
|
||||
|
|
|
|||
108
sui/core/jit.go
108
sui/core/jit.go
|
|
@ -6,7 +6,6 @@ import (
|
|||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
|
|
@ -69,15 +68,17 @@ func (parser *TemplateParser) parseComponent(sel *goquery.Selection) {
|
|||
// RenderComponent render the component
|
||||
func (parser *TemplateParser) RenderComponent(comp *JitComponent, props map[string]interface{}, slots *goquery.Selection, children *goquery.Selection) (string, string, error) {
|
||||
html := comp.html
|
||||
randvar := fmt.Sprintf("__%s_$props", time.Now().Format("20060102150405"))
|
||||
html = replaceRandVar(html, randvar)
|
||||
data := Data{}
|
||||
data[randvar] = props
|
||||
if parser.data != nil {
|
||||
for key, val := range parser.data {
|
||||
data[key] = val
|
||||
}
|
||||
}
|
||||
|
||||
html = replaceRandVar(html, Data(props))
|
||||
option := *parser.option
|
||||
option.Route = comp.route
|
||||
compParser := NewTemplateParser(parser.data, &option)
|
||||
compParser.sequence = parser.sequence + 1
|
||||
locale := compParser.Locale()
|
||||
|
||||
// Parse the node
|
||||
ns := Namespace(comp.route, compParser.sequence, comp.buildOption.ScriptMinify)
|
||||
cn := ComponentName(comp.route, comp.buildOption.ScriptMinify)
|
||||
|
||||
sel, err := NewDocumentString(`<body>` + html + `</body>`)
|
||||
if err != nil {
|
||||
|
|
@ -107,24 +108,26 @@ func (parser *TemplateParser) RenderComponent(comp *JitComponent, props map[stri
|
|||
children.Find("slot").Remove()
|
||||
root.Find("children").ReplaceWithSelection(children)
|
||||
|
||||
option := *parser.option
|
||||
option.Route = comp.route
|
||||
compParser := NewTemplateParser(data, &option)
|
||||
locale := compParser.Locale()
|
||||
// Replace the props
|
||||
if locale != nil {
|
||||
locale.replaceVars(randvar)
|
||||
locale.replaceVars(Data(props))
|
||||
}
|
||||
compParser.locale = locale
|
||||
|
||||
// Parse the node
|
||||
ns := Namespace(comp.route, compParser.sequence, comp.buildOption.ScriptMinify)
|
||||
cn := ComponentName(comp.route, comp.buildOption.ScriptMinify)
|
||||
compParser.locale = locale
|
||||
compParser.BindEvent(root, ns, cn)
|
||||
compParser.parseNode(root.Get(0))
|
||||
for sel, nodes := range compParser.replace {
|
||||
sel.ReplaceWithNodes(nodes...)
|
||||
delete(parser.replace, sel)
|
||||
}
|
||||
|
||||
if compParser.scripts != nil {
|
||||
for _, script := range compParser.scripts {
|
||||
script.Namespace = ns
|
||||
parser.scripts = append(parser.scripts, script)
|
||||
}
|
||||
}
|
||||
|
||||
if comp.scripts != nil {
|
||||
for _, script := range comp.scripts {
|
||||
script.Namespace = ns
|
||||
|
|
@ -145,6 +148,7 @@ func (parser *TemplateParser) RenderComponent(comp *JitComponent, props map[stri
|
|||
first.SetAttr("s:ns", ns)
|
||||
first.SetAttr("s:cn", cn)
|
||||
first.SetAttr("s:ready", cn+"()")
|
||||
|
||||
html, err = root.Html()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
|
|
@ -152,15 +156,28 @@ func (parser *TemplateParser) RenderComponent(comp *JitComponent, props map[stri
|
|||
return html, ns, nil
|
||||
}
|
||||
|
||||
func (parser *TemplateParser) addScripts(sel *goquery.Selection, scripts []ScriptNode) {
|
||||
func (parser *TemplateParser) filterScripts(parent string, scripts []ScriptNode) []ScriptNode {
|
||||
if scripts == nil {
|
||||
return
|
||||
return []ScriptNode{}
|
||||
}
|
||||
filtered := []ScriptNode{}
|
||||
for _, script := range scripts {
|
||||
query := fmt.Sprintf(`script[s\:cn="%s"]`, script.Component)
|
||||
if sel.Find(query).Length() > 0 {
|
||||
if script.Parent != parent {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, script)
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func (parser *TemplateParser) addScripts(sel *goquery.Selection, scripts []ScriptNode) {
|
||||
for _, script := range scripts {
|
||||
if script.Component != "" {
|
||||
query := fmt.Sprintf(`script[s\:cn="%s"]`, script.Component)
|
||||
if sel.Find(query).Length() > 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
sel.AppendHtml(script.ComponentHTML(script.Namespace))
|
||||
}
|
||||
}
|
||||
|
|
@ -227,6 +244,13 @@ func (parser *TemplateParser) componentProps(sel *goquery.Selection) (map[string
|
|||
}
|
||||
|
||||
for _, attr := range sel.Nodes[0].Attr {
|
||||
|
||||
// s:on , s:data , s:json
|
||||
if strings.HasPrefix(attr.Key, "s:on") || strings.HasPrefix(attr.Key, "s:data") || strings.HasPrefix(attr.Key, "s:json") {
|
||||
props[attr.Key] = attr.Val
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(attr.Key, "s:") || attr.Key == "is" {
|
||||
continue
|
||||
}
|
||||
|
|
@ -269,22 +293,13 @@ func (parser *TemplateParser) parseComponentProps(props map[string]string) (map[
|
|||
}
|
||||
|
||||
if _, ok := values.(map[string]interface{}); ok {
|
||||
for k, v := range values.(map[string]interface{}) {
|
||||
result[k] = v
|
||||
for k := range values.(map[string]interface{}) {
|
||||
result[k] = k
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(val, "{{") && strings.HasSuffix(val, "}}") {
|
||||
value, err := parser.data.Exec(val)
|
||||
if err != nil {
|
||||
return map[string]interface{}{}, err
|
||||
}
|
||||
result[key] = value
|
||||
continue
|
||||
}
|
||||
|
||||
result[key] = val
|
||||
}
|
||||
return result, nil
|
||||
|
|
@ -304,16 +319,16 @@ func (parser *TemplateParser) componentFile(sel *goquery.Selection, props map[st
|
|||
return file, route, nil
|
||||
}
|
||||
|
||||
func (locale *Locale) replaceVars(randvar string) {
|
||||
func (locale *Locale) replaceVars(data Data) {
|
||||
if locale.Keys != nil {
|
||||
for key, val := range locale.Keys {
|
||||
locale.Keys[key] = replaceRandVar(val, randvar)
|
||||
locale.Keys[key] = replaceRandVar(val, data)
|
||||
}
|
||||
}
|
||||
|
||||
if locale.Messages != nil {
|
||||
for key, val := range locale.Messages {
|
||||
locale.Messages[key] = replaceRandVar(val, randvar)
|
||||
locale.Messages[key] = replaceRandVar(val, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -415,11 +430,18 @@ func readComponent(route string, file string) (*JitComponent, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func replaceRandVar(html string, randvar string) string {
|
||||
return slotRe.ReplaceAllStringFunc(html, func(exp string) string {
|
||||
exp = strings.ReplaceAll(exp, "[{", "{{")
|
||||
exp = strings.ReplaceAll(exp, "}]", "}}")
|
||||
exp = strings.ReplaceAll(exp, "$props", randvar)
|
||||
return exp
|
||||
func replaceRandVar(value string, data Data) string {
|
||||
|
||||
value = propNewRe.ReplaceAllStringFunc(value, func(exp string) string {
|
||||
exp = strings.TrimPrefix(exp, "{%")
|
||||
exp = strings.TrimSuffix(exp, "%}")
|
||||
res, _ := data.ExecString(fmt.Sprintf("{{ %s }}", exp))
|
||||
return res
|
||||
})
|
||||
|
||||
data = Data{"$props": data}
|
||||
return slotRe.ReplaceAllStringFunc(value, func(exp string) string {
|
||||
res, _ := data.ExecString(exp)
|
||||
return res
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ func (parser *TemplateParser) Render(html string) (string, error) {
|
|||
}
|
||||
|
||||
head.AppendHtml(headInjectionScript(data))
|
||||
parser.addScripts(head, parser.scripts)
|
||||
parser.addScripts(head, parser.filterScripts("head", parser.scripts))
|
||||
parser.addStyles(head, parser.styles)
|
||||
}
|
||||
|
||||
|
|
@ -146,6 +146,7 @@ func (parser *TemplateParser) Render(html string) (string, error) {
|
|||
data, _ = jsoniter.MarshalToString(map[string]string{"error": err.Error()})
|
||||
}
|
||||
body.AppendHtml(bodyInjectionScript(data, parser.debug()))
|
||||
parser.addScripts(body, parser.filterScripts("body", parser.scripts))
|
||||
}
|
||||
|
||||
// Fmt
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue