Moved pager to the controller package. Added additional documentation.

This commit is contained in:
mikestefanello 2021-12-18 16:55:35 -05:00
parent 1fb4d83e0d
commit d28f02f8d6
5 changed files with 117 additions and 40 deletions

View file

@ -23,6 +23,11 @@ up:
docker-compose up -d docker-compose up -d
sleep 3 sleep 3
.PHONY: reset
reset:
docker-compose down
make up
.PHONY: run .PHONY: run
run: run:
clear clear

View file

@ -1,8 +1,15 @@
package context package context
const ( const (
// AuthenticatedUserKey is the key value used to store the authenticated user in context
AuthenticatedUserKey = "auth_user" AuthenticatedUserKey = "auth_user"
// UserKey is the key value used to store a user in context
UserKey = "user" UserKey = "user"
// FormKey is the key value used to store a form in context
FormKey = "form" FormKey = "form"
// PasswordTokenKey is the key value used to store a password token in context
PasswordTokenKey = "password_token" PasswordTokenKey = "password_token"
) )

View file

@ -27,12 +27,13 @@ import (
) )
var ( var (
// Cache of compiled page templates // templates stores a cache of parsed page templates
templates = sync.Map{} templates = sync.Map{}
// Template function map // funcMap stores the Template function map
funcMap = funcmap.GetFuncMap() funcMap = funcmap.GetFuncMap()
// templatePath stores the complete path to the templates directory
templatePath = getTemplatesDirectoryPath() templatePath = getTemplatesDirectoryPath()
) )
@ -170,6 +171,7 @@ func (t *Controller) SetValidationErrorMessages(c echo.Context, err error, data
} }
// Provide better error messages depending on the failed validation tag // Provide better error messages depending on the failed validation tag
// This should be expanded as you use additional tags in your validation
switch ve.Tag() { switch ve.Tag() {
case "required": case "required":
message = "%s is required." message = "%s is required."
@ -187,7 +189,7 @@ func (t *Controller) SetValidationErrorMessages(c echo.Context, err error, data
// getTemplatesDirectoryPath gets the templates directory path // getTemplatesDirectoryPath gets the templates directory path
// This is needed incase this is called from a package outside of main, // This is needed incase this is called from a package outside of main,
// such as testing // such as within tests
func getTemplatesDirectoryPath() string { func getTemplatesDirectoryPath() string {
_, b, _, _ := runtime.Caller(0) _, b, _, _ := runtime.Caller(0)
d := path.Join(path.Dir(b)) d := path.Join(path.Dir(b))

View file

@ -7,51 +7,112 @@ import (
"goweb/context" "goweb/context"
"goweb/msg" "goweb/msg"
"goweb/pager"
echomw "github.com/labstack/echo/v4/middleware" echomw "github.com/labstack/echo/v4/middleware"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
) )
const ( // Page consists of all data that will be used to render a page response for a given controller.
DefaultItemsPerPage = 20 // While it's not required for a controller to render a Page on a route, this is the common data
) // object that will be passed to the templates, making it easy for all controllers to share
// functionality both on the back and frontend. The Page can be expanded to include anything else
// your app wants to support.
// Methods on this page also then become available in the templates, which can be more useful than
// the funcmap if your methods require data stored in the page, such as the context.
type Page struct { type Page struct {
// AppName stores the name of the application.
// If omitted, the configuration value will be used.
AppName string AppName string
// Title stores the title of the page
Title string Title string
// Context stores the request context
Context echo.Context Context echo.Context
Reverse func(name string, params ...interface{}) string
// ToURL is a function to convert a route name and optional route parameters to a URL
ToURL func(name string, params ...interface{}) string
// Path stores the path of the current request
Path string Path string
// URL stores the URL of the current request
URL string
// Data stores whatever additional data that needs to be passed to the templates.
// This is what the controller uses to pass the content of the page.
Data interface{} Data interface{}
// Layout stores the name of the layout base template file which will be used when the page is rendered.
// This should match a template file located within the layouts directory inside the templates directory.
// The template extension should not be included in this value.
Layout string Layout string
// Name stores the name of the page as well as the name of the template file which will be used to render
// the content portion of the layout template.
// This should match a template file located within the pages directory inside the templates directory.
// The template extension should not be included in this value.
Name string Name string
// IsHome stores whether the requested page is the home page or not
IsHome bool IsHome bool
// IsAuth stores whether or not the user is authenticated
IsAuth bool IsAuth bool
// StatusCode stores the HTTP status code that will be returned
StatusCode int StatusCode int
// Metatags stores metatag values
Metatags struct { Metatags struct {
// Description stores the description metatag value
Description string Description string
// Keywords stores the keywords metatag values
Keywords []string Keywords []string
} }
Pager pager.Pager
// Pager stores a pager which can be used to page lists of results
Pager Pager
// CSRF stores the CSRF token for the given request.
// This will only be populated if the CSRF middleware is in effect for the given request.
// If this is populated, all forms must include this value otherwise the requests will be rejected.
CSRF string CSRF string
// Headers stores a list of HTTP headers and values to be set on the response
Headers map[string]string Headers map[string]string
// RequestID stores the ID of the given request.
// This will only be populated if the request ID middleware is in effect for the given request.
RequestID string
// Cache stores values for caching the response of this page
Cache struct { Cache struct {
// Enabled dictates if the response of this page should be cached.
// Cached responses are served via middleware.
Enabled bool Enabled bool
// Expiration stores the amount of time that the cache entry should live for before expiring.
// If omitted, the configuration value will be used.
Expiration time.Duration Expiration time.Duration
// Tags stores a list of tags to apply to the cache entry.
// These are useful when invalidating cache for dynamic events such as entity operations.
Tags []string Tags []string
} }
RequestID string
} }
// NewPage creates and initiatizes a new page for a given request context
func NewPage(c echo.Context) Page { func NewPage(c echo.Context) Page {
p := Page{ p := Page{
Context: c, Context: c,
Reverse: c.Echo().Reverse, ToURL: c.Echo().Reverse,
Path: c.Request().URL.Path, Path: c.Request().URL.Path,
URL: c.Request().URL.String(),
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
Pager: pager.NewPager(c, DefaultItemsPerPage), Pager: NewPager(c),
Headers: make(map[string]string), Headers: make(map[string]string),
RequestID: c.Response().Header().Get(echo.HeaderXRequestID), RequestID: c.Response().Header().Get(echo.HeaderXRequestID),
} }
@ -62,7 +123,6 @@ func NewPage(c echo.Context) Page {
p.CSRF = csrf.(string) p.CSRF = csrf.(string)
} }
// TODO: Use container?
if u := c.Get(context.AuthenticatedUserKey); u != nil { if u := c.Get(context.AuthenticatedUserKey); u != nil {
p.IsAuth = true p.IsAuth = true
} }
@ -70,10 +130,8 @@ func NewPage(c echo.Context) Page {
return p return p
} }
func (p Page) SetMessage(typ msg.Type, value string) { // GetMessages gets all flash messages for a given type.
msg.Set(p.Context, typ, value) // This allows for easy access to flash messages from the templates.
}
func (p Page) GetMessages(typ msg.Type) []template.HTML { func (p Page) GetMessages(typ msg.Type) []template.HTML {
strs := msg.Get(p.Context, typ) strs := msg.Get(p.Context, typ)
ret := make([]template.HTML, len(strs)) ret := make([]template.HTML, len(strs))

View file

@ -1,4 +1,4 @@
package pager package controller
import ( import (
"math" "math"
@ -7,6 +7,11 @@ import (
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
) )
const (
// DefaultItemsPerPage stores the default amount of items per page
DefaultItemsPerPage = 20
)
type Pager struct { type Pager struct {
Items int Items int
Page int Page int
@ -14,9 +19,9 @@ type Pager struct {
Pages int Pages int
} }
func NewPager(c echo.Context, itemsPerPage int) Pager { func NewPager(c echo.Context) Pager {
p := Pager{ p := Pager{
ItemsPerPage: itemsPerPage, ItemsPerPage: DefaultItemsPerPage,
Page: 1, Page: 1,
} }