Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Custom Render Support via Reply #103

Closed
jeevatkm opened this issue Aug 22, 2017 · 10 comments
Closed

Add Custom Render Support via Reply #103

jeevatkm opened this issue Aug 22, 2017 · 10 comments
Assignees
Labels
aah Framework scope enhancement
Projects

Comments

@jeevatkm
Copy link
Member

jeevatkm commented Aug 22, 2017

Add custom render support via Reply. So that aah user can implement interface aah.Render and supply it as custom render.

Classic real time usage is JSON API response - https://github.com/google/jsonapi.

Samples

Sample 1

// CustomRender implements the interface `aah.Render`.
type CustomRender struct {
    // ... your fields goes here
}

func (cr *CustomRender) Render(w io.Writer) error {
    // implement your rendering
    fmt.Fprint(w, "This is custom render struct")
    return nil
}

// Using it via Reply()
Reply().ContentType(jsonapi.MediaType).Render(&CustomRender{
    // your fields initialize goes here
})

Sample 2

Reply().ContentType(jsonapi.MediaType).
	Render(aah.RenderFunc(func(w io.Writer) error {
		// implement your rendering
		fmt.Fprint(w, "This is custom render func")
		return nil
	}))
@jeevatkm
Copy link
Member Author

It's done :)

@jeevatkm jeevatkm moved this from v0.8 - In Progress to v0.8 - Completed in aah Roadmap Aug 23, 2017
@jeevatkm jeevatkm moved this from v0.8 - Completed to Released to Audience in aah Roadmap Sep 3, 2017
@AugustHell
Copy link

AugustHell commented Apr 24, 2018

Cant get custom rendering to work for me

Hi, first of all, coding in php for almost 20 years, I'm pretty new to go (since a few months), but I'm having some experience in c and c# therefore go looks familiar to me.
I really like the coding style of aah and seriously trying it out since some days to see if I can get used to it and it meets my needs.

One of them is to have some kind of web components in my html templates. As our web design is made using the pinegrow web editor, I need to have a way to replace blocks with representing default content in the layout (always full pages, content of the block may be just an image of the output) with dynamic content of the database in template(s) - best way each block gets his own template.

Simple Example:
Layout:

<% block "content" %><img src="PresentationOfAProductDetail.jpg"><% end %>
</body></html>

Template:
<% define "content" %><div>picture and data of selected product</div><% end %>

This I can do with go html/template, but I don't find a way to do it with aah :(

First of all I expected Reply().HTMLlf(layout,template,data) would do it, but I don't know why the template isn't overriding the default of the layout.

Now I'm trying to build a custom render method, but yet the both base examples don't work:

package controllers

import (
	"fmt"
	"io"

	"aahframework.org/aah.v0"
)

// FastController struct application controller
type FastController struct {
	*aah.Context
}

// CustomRender aka interface `aah.Render`.
type CustomRender struct {
	// ... your fields goes here
}

// Render test
func (cr *CustomRender) Render(w io.Writer) error {
	// implement your rendering
	fmt.Fprint(w, "This is custom render struct")
	return nil
}

// Index method is application home page.
func (a *FastController) Index() {
	a.Reply().Render(&CustomRender{})
}

Internal Server Error:
interface conversion: aah.Render is *controllers.CustomRender, not *aah.HTML

RenderFunc:

package controllers

import (
	"fmt"
	"io"

	"aahframework.org/aah.v0"
)

// FastController struct application controller
type FastController struct {
	*aah.Context
}
// Index method is application home page.
func (a *FastController) Index() {

	// Using it via Reply()
	a.Reply().ContentType("text/html").Render(aah.RenderFunc(func(w io.Writer) error {
		// implement your rendering
		fmt.Fprint(w, "This is custom render func")
		return nil
	}))

}

Internal Server Error:
interface conversion: aah.Render is aah.RenderFunc, not *aah.HTML

$ go version
go version go1.9.4 windows/amd64

I've tried some things to get this conversion right, but got no success. Good chance I don't know yet, how to do it right.

@jeevatkm
Copy link
Member Author

jeevatkm commented Apr 24, 2018

@AugustHell Thank you for using aah for your project. I'm glad to hear your appreciation.

Feel free to reach out on Gitter or here for any assistance with aah.


aah go view engine is enhanced features with default go template engine. So it has all the capabilities 😄

Let's get to your questions.

First you might have a read the aah views directory structure. Just give you a high level tour-

  • Each Controller would have sub-directory under directory pages For e.g. Controller named FastController
  • And Each Controller Action would have view template file. For e.g.: Action named Index

Now view template path is views/pages/fast/index.html

Next move on to Layout, if you take a closer look at layout master.html, you will see a line {{ template "body" . -}} and if you look at index.html you will see {{ define "body" -}}. In your example you have called this as content; I called that as body. It same as block keyword but with more options. Of course if you would like, you can use block too.

Next move on to Rendering-

  • When you call Reply().HTML(data) from action FastController.Index - aah knows and it would render views/pages/fast/index.html with views/layouts/master.html
  • When you call Reply().HTMLl("mylayout1.html", data) from action FastController.Index - aah knows and it would render views/pages/fast/index.html with views/layouts/mylayout1.html
  • So as other methods Reply().HTML* gives your a control over layout, template file, etc.

I hope I have describe it alright. Bottomline, you can use all capabilities of go template engine with aah. If you see a trouble, that's where aah have to improve. please let me know, I would take care.


Regarding custom render, this error interface conversion: aah.Render is *controllers.CustomRender, not *aah.HTML smells. I will check and get back to you.


Thanks again for using aah.

@jeevatkm
Copy link
Member Author

@AugustHell Thanks finding a bug #174, I have fixed it. Made v0.10.1 release. Please update aah at your end. You're good to go 👍

@AugustHell
Copy link

Thank you very much for your quick detailed reply and the aah update!

I understand the part of the layout and page rendering. Only, it doesn't work like I'd expect it. Maybe another quirk?

I'm using block instead of template, cause I like to have default content in the layout in place, that should be replaced by the controller with dynamic content on runtime.
The goal is, that web design is decoupled from the programming part in order to view and edit the layout "master.html" just from file without running a go server.

That could be done with template too, it's just a line more, but neither way is working if you look at this example:

layouts/master.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Block Test</title>
  </head>
  <body>
    <% template "body" . %>
    <% define "body" -%>
    <h1>Welcome to the Block Test</h1>
    <% end %>

    <section name="footer">
      <% block "footer" . %>
      <p><i>(served by master layout)</i></p>
      <% end %>
    </section>
  </body>
</html>

pages/app/index.html

<% define "body" %>
  <h1><% .Greet.Message %></h1>
<% end %>
<% define "footer" -%>
  <p><i>(served by app index controller)</i></p>
<%- end %>

app/controllers/app.go

package controllers

import (
	"aahframework.org/aah.v0"
	"athell.com/xhimport/v007fasttpl/app/models"
)

// AppController struct application controller
type AppController struct {
	*aah.Context
}

// Index method is application home page.
func (a *AppController) Index() {
	data := aah.Data{
		"Greet": models.Greet{
			Message: "Welcome to aah framework - Web Application",
		},
	}
	a.Reply().Ok().HTML(data)
}

If I run this, neither the body nor the footer default content is replaced, so nothing of pages/app/index.html is in the output.
Same result with
a.Reply().Ok().HTMLlf("master.html", "index.html", data)

Output:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Block Test</title>
  </head>
  <body>
    <h1>Welcome to the Block Test</h1>
    <section name="footer">
     <p><i>(served by master layout)</i></p>
    </section>
  </body>
</html>

BTW, highly welcomed would be, if there is a replay function that handles multiple "pages" like several blocks on a page

blocks := []string{
	"menu.html",
	"body.html",
	"links.html",
	"footer.html",
}
a.Reply().Ok().HTMLlf("master.html", blocks, data)

Another neat way would be if it is plugable, like in erlang/elixirs plug:
a.Replay().Ok().HTMLlf("master.html","menu.html",nil).HTMLf("body.html",data).HTMLf("footer.html",nil)

@jeevatkm
Copy link
Member Author

@AugustHell I understand your goal (generally it should be 😄). I'm wondering why block is not working. I will check and get back to you.

Thanks for the suggestion on reply design. First I would like to understand how erlang/elixirs works then I could think on design. (Added note on Roadmap)

@jeevatkm
Copy link
Member Author

@AugustHell Congrats you have found a bug #175 and I have fixed it go-aah/view@525d4de

Please take an update of view library: go get -u aahframework.org/view.v0

Use all the capabilities of Go template engine plus aah and share your inputs 😄 Thanks for contributing to aah.


BTW would you like to jump on aah edge version? you can try new features/enhancements and your findings could help me. v0.11.0 gonna come with considerable improvements, I believe aah users would love it.

@AugustHell
Copy link

Thanks a lot.
Oh, I shouldn't have mentioned erlang/elixir. It's a complete different language, a wonderous world from another continent with its own culture and history. Don't look it at as long as you don't have huge amounts of free time 😁
Plug is part of the Phoenix framework and it aims to make every "component" plugable, so that you can chain togethers the ones you need.
Erlang is a strict functional programming language. Pattern matching and immutable data is what it makes a complete different approach to develop applications.

As I'm working on an administration tool at the moment, I can use the edge version, but need to stay focused on things that the project needs. Websockets might be a topic instead of ajax, but more interesting for me is the database layer part. Keep on your great work!

@jeevatkm
Copy link
Member Author

@AugustHell Got it, so will do when I get a time.

need to stay focused on things that the project needs

I think, for you better to stay on release version so that you can focus on your project; because edge version has possiblity of build failures.

Thank you for the appreciation.

@jeevatkm
Copy link
Member Author

@AugustHell I thought check with you. how is your experience so far with aah? Please let me know your candid feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
aah Framework scope enhancement
Projects
aah Roadmap
  
Released to Audience
Development

No branches or pull requests

2 participants