diff --git a/sui/api/api.go b/sui/api/api.go index 7dfad437..74522334 100644 --- a/sui/api/api.go +++ b/sui/api/api.go @@ -10,6 +10,26 @@ var dsl = []byte(` "guard": "bearer-jwt", "group": "__yao/sui/v1", "paths": [ + { + "label": "Render", + "description": "Render the frontend page", + "path": "/render/*route", + "method": "POST", + "guard": "-", + "process": "sui.Render", + "in": [":context", "$param.route", ":payload"], + "out": { "status": 200, "type": "text/html; charset=utf-8" } + }, + { + "label": "Run", + "description": "Run the backend script, with Api prefix method", + "path": "/run/*route", + "guard": "-", + "method": "POST", + "process": "sui.Run", + "in": [":context", "$param.route", ":payload"], + "out": { "status": 200, "type": "application/json" } + }, { "path": "/:id/setting", "method": "GET", diff --git a/sui/api/process.go b/sui/api/process.go index 5459bb62..093f1d09 100644 --- a/sui/api/process.go +++ b/sui/api/process.go @@ -20,6 +20,9 @@ func init() { process.RegisterGroup("sui", map[string]process.Handler{ "setting": Setting, + "render": Render, + "run": Run, + "template.get": TemplateGet, "template.find": TemplateFind, "template.asset": TemplateAsset, diff --git a/sui/api/render.go b/sui/api/render.go new file mode 100644 index 00000000..b01ddbf2 --- /dev/null +++ b/sui/api/render.go @@ -0,0 +1,104 @@ +package api + +import ( + "fmt" + + "github.com/gin-gonic/gin" + "github.com/yaoapp/gou/process" + "github.com/yaoapp/yao/sui/core" +) + +// Render the frontend page +func Render(process *process.Process) interface{} { + process.ValidateArgNums(3) + ctx, ok := process.Args[0].(*gin.Context) + if !ok { + return "The context is required" + } + + ctx.Header("Content-Type", "text/html; charset=utf-8") + route := process.ArgsString(1) + payload := process.ArgsMap(2) + + if route == "" { + return "The route is required" + } + + if payload["name"] == nil { + return "The render name is required" + } + + ctx.Request.URL.Path = route + r, _, err := NewRequestContext(ctx) + if err != nil { + return fmt.Sprintf("
%s
", err.Error()) + } + + c, _, err := r.MakeCache() + if err != nil { + return fmt.Sprintf("
%s
", err.Error()) + } + + if c == nil { + return fmt.Sprintf("
Cache not found
") + } + + data, ok := payload["data"].(map[string]interface{}) + if !ok { + return fmt.Sprintf("
Data not found
") + } + + name, ok := payload["name"].(string) + if !ok { + return fmt.Sprintf("
Name not found
") + } + + html, err := r.renderHTML(c, name, c.HTML, core.Data(data)) + if err != nil { + return fmt.Sprintf("
%s
", err.Error()) + } + + return html +} + +func (r *Request) renderHTML(c *core.Cache, name string, html string, data core.Data) (string, error) { + + doc, err := core.NewDocument([]byte(html)) + if err != nil { + return "", fmt.Errorf("Document error: %w", err) + } + + sel := doc.Find(fmt.Sprintf("[s\\:render='%s']", name)) + if sel.Length() == 0 { + return "", fmt.Errorf("Render %s not found", name) + } + + // Set the page request data + option := core.ParserOption{ + Theme: r.Request.Theme, + Locale: r.Request.Locale, + Debug: r.Request.DebugMode(), + DisableCache: r.Request.DisableCache(), + Route: r.Request.URL.Path, + Root: c.Root, + Script: c.Script, + Imports: c.Imports, + Request: r.Request, + } + + // Parse the template + parser := core.NewTemplateParser(data, &option) + err = parser.RenderSelection(sel) + if err != nil { + return "", fmt.Errorf("Parser error: %w", err) + } + + sel.Find("[sui-hide]").Remove() + parser.Tidy(sel) + html, err = sel.Html() + if err != nil { + return "", fmt.Errorf("Html error: %w", err) + } + + return html, nil +} diff --git a/sui/api/run.go b/sui/api/run.go new file mode 100644 index 00000000..b4aaa388 --- /dev/null +++ b/sui/api/run.go @@ -0,0 +1,8 @@ +package api + +import "github.com/yaoapp/gou/process" + +// Run the backend script, with Api prefix method +func Run(process *process.Process) interface{} { + return nil +} diff --git a/sui/core/injections.go b/sui/core/injections.go index 65f7a072..923c8620 100644 --- a/sui/core/injections.go +++ b/sui/core/injections.go @@ -284,13 +284,15 @@ const pageEventScriptTmpl = ` const compEventScriptTmpl = ` if (document.querySelector("[s\\:event=%s]")) { - document.querySelector("[s\\:event=%s]").addEventListener("%s", function (event) { - const dataKeys = %s; - const jsonKeys = %s; - const root = __sui_component_root(this, "%s"); - handler = new %s(root).%s; - const target = event.target || null; - __sui_event_handler(event, dataKeys, jsonKeys, target, root, handler); + let elms = document.querySelectorAll("[s\\:event=%s]"); + elms.forEach(function (element) { + element.addEventListener("%s", function (event) { + const dataKeys = %s; + const jsonKeys = %s; + const root = __sui_component_root(element, "%s"); + handler = new %s(root).%s; + __sui_event_handler(event, dataKeys, jsonKeys, element, root, handler); + }); }); } ` diff --git a/sui/core/parser.go b/sui/core/parser.go index 86edb613..b4d7367f 100644 --- a/sui/core/parser.go +++ b/sui/core/parser.go @@ -68,17 +68,19 @@ var keepWords = map[string]bool{ } var allowUsePropAttrs = map[string]bool{ - "s:if": true, - "s:elif": true, - "s:for": true, - "s:event": true, + "s:if": true, + "s:elif": true, + "s:for": true, + "s:event": true, + "s:render": true, } var keepAttrs = map[string]bool{ - "s:ns": true, - "s:cn": true, - "s:ready": true, - "s:event": true, + "s:ns": true, + "s:cn": true, + "s:ready": true, + "s:event": true, + "s:render": true, } // NewTemplateParser create a new template parser @@ -157,7 +159,7 @@ func (parser *TemplateParser) Render(html string) (string, error) { if parser.option != nil && (parser.option.Request != nil || parser.option.Preview) { // Remove the sui-hide attribute doc.Find("[sui-hide]").Remove() - parser.tidy(doc.Selection) + parser.Tidy(doc.Selection) } // fmt.Println(doc.Html()) @@ -837,13 +839,13 @@ func (parser *TemplateParser) show(sel *goquery.Selection) { // sel.SetAttr("style", style) } -func (parser *TemplateParser) tidy(s *goquery.Selection) { +func (parser *TemplateParser) Tidy(s *goquery.Selection) { s.Contents().Each(func(i int, child *goquery.Selection) { node := child.Get(0) if _, exist := child.Attr("s:jit"); node.Data == "slot" || exist { - parser.tidy(child) + parser.Tidy(child) parser.removeWrapper(child) return } @@ -872,7 +874,7 @@ func (parser *TemplateParser) tidy(s *goquery.Selection) { } node.Attr = attrs - parser.tidy(child) + parser.Tidy(child) }) }