Add support for interface generation and enums (#3047)

* Add support to output ts models as interfaces

* Add support to generate enums from golang

* cleanup logs

* add missing documentation

* fix package names for enum. Fix processing enums that are in separate packages

* revert golang 1.21

* Fix spelling

* Add support for simplified version of Enum for typescriptify

* update docs

* removed unused logs

* Add tests. Fix imported enums types in models

---------

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
This commit is contained in:
Andrey Pshenkin
2023-11-26 06:50:49 +11:00
committed by GitHub
co-authored by Lea Anthony
parent 929dfb4123
commit b9de31e38e
24 changed files with 717 additions and 44 deletions
+8 -3
View File
@@ -43,10 +43,15 @@ func generateModule(f *flags.GenerateModule) error {
return err
}
if projectConfig.Bindings.TsGeneration.OutputType == "" {
projectConfig.Bindings.TsGeneration.OutputType = "classes"
}
_, err = bindings.GenerateBindings(bindings.Options{
Tags: buildTags,
TsPrefix: projectConfig.Bindings.TsGeneration.Prefix,
TsSuffix: projectConfig.Bindings.TsGeneration.Suffix,
Tags: buildTags,
TsPrefix: projectConfig.Bindings.TsGeneration.Prefix,
TsSuffix: projectConfig.Bindings.TsGeneration.Suffix,
TsOutputType: projectConfig.Bindings.TsGeneration.OutputType,
})
if err != nil {
return err
+11 -1
View File
@@ -31,6 +31,7 @@ func (a *App) Run() error {
var tsPrefixFlag *string
var tsPostfixFlag *string
var tsOutputTypeFlag *string
tsPrefix := os.Getenv("tsprefix")
if tsPrefix == "" {
@@ -42,6 +43,11 @@ func (a *App) Run() error {
tsPostfixFlag = bindingFlags.String("tssuffix", "", "Suffix for generated typescript entities")
}
tsOutputType := os.Getenv("tsoutputtype")
if tsOutputType == "" {
tsOutputTypeFlag = bindingFlags.String("tsoutputtype", "", "Output type for generated typescript entities (classes|interfaces)")
}
_ = bindingFlags.Parse(os.Args[1:])
if tsPrefixFlag != nil {
tsPrefix = *tsPrefixFlag
@@ -49,11 +55,15 @@ func (a *App) Run() error {
if tsPostfixFlag != nil {
tsSuffix = *tsPostfixFlag
}
if tsOutputTypeFlag != nil {
tsOutputType = *tsOutputTypeFlag
}
appBindings := binding.NewBindings(a.logger, a.options.Bind, bindingExemptions, IsObfuscated())
appBindings := binding.NewBindings(a.logger, a.options.Bind, bindingExemptions, IsObfuscated(), a.options.EnumBind)
appBindings.SetTsPrefix(tsPrefix)
appBindings.SetTsSuffix(tsSuffix)
appBindings.SetOutputType(tsOutputType)
err := generateBindings(appBindings)
if err != nil {
+1 -1
View File
@@ -209,7 +209,7 @@ func CreateApp(appoptions *options.App) (*App, error) {
appoptions.OnDomReady,
appoptions.OnBeforeClose,
}
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions, false)
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions, false, appoptions.EnumBind)
eventHandler := runtime.NewEvents(myLogger)
ctx = context.WithValue(ctx, "events", eventHandler)
+1 -1
View File
@@ -72,7 +72,7 @@ func CreateApp(appoptions *options.App) (*App, error) {
appoptions.OnDomReady,
appoptions.OnBeforeClose,
}
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions, IsObfuscated())
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions, IsObfuscated(), appoptions.EnumBind)
eventHandler := runtime.NewEvents(myLogger)
ctx = context.WithValue(ctx, "events", eventHandler)
// Attach logger to context
+106 -1
View File
@@ -23,17 +23,20 @@ type Bindings struct {
exemptions slicer.StringSlicer
structsToGenerateTS map[string]map[string]interface{}
enumsToGenerateTS map[string]map[string]interface{}
tsPrefix string
tsSuffix string
tsInterface bool
obfuscate bool
}
// NewBindings returns a new Bindings object
func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exemptions []interface{}, obfuscate bool) *Bindings {
func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exemptions []interface{}, obfuscate bool, enumsToBind []interface{}) *Bindings {
result := &Bindings{
db: newDB(),
logger: logger.CustomLogger("Bindings"),
structsToGenerateTS: make(map[string]map[string]interface{}),
enumsToGenerateTS: make(map[string]map[string]interface{}),
obfuscate: obfuscate,
}
@@ -47,6 +50,10 @@ func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exem
result.exemptions.Add(name)
}
for _, enum := range enumsToBind {
result.AddEnumToGenerateTS(enum)
}
// Add the structs to bind
for _, ptr := range structPointersToBind {
err := result.Add(ptr)
@@ -88,16 +95,21 @@ func (b *Bindings) ToJSON() (string, error) {
func (b *Bindings) GenerateModels() ([]byte, error) {
models := map[string]string{}
var seen slicer.StringSlicer
var seenEnumsPackages slicer.StringSlicer
allStructNames := b.getAllStructNames()
allStructNames.Sort()
allEnumNames := b.getAllEnumNames()
allEnumNames.Sort()
for packageName, structsToGenerate := range b.structsToGenerateTS {
thisPackageCode := ""
w := typescriptify.New()
w.WithPrefix(b.tsPrefix)
w.WithSuffix(b.tsSuffix)
w.WithInterface(b.tsInterface)
w.Namespace = packageName
w.WithBackupDir("")
w.KnownStructs = allStructNames
w.KnownEnums = allEnumNames
// sort the structs
var structNames []string
for structName := range structsToGenerate {
@@ -112,6 +124,20 @@ func (b *Bindings) GenerateModels() ([]byte, error) {
structInterface := structsToGenerate[structName]
w.Add(structInterface)
}
// if we have enums for this package, add them as well
var enums, enumsExist = b.enumsToGenerateTS[packageName]
if enumsExist {
for enumName, enum := range enums {
fqemumname := packageName + "." + enumName
if seen.Contains(fqemumname) {
continue
}
w.AddEnum(enum)
}
seenEnumsPackages.Add(packageName)
}
str, err := w.Convert(nil)
if err != nil {
return nil, err
@@ -121,6 +147,35 @@ func (b *Bindings) GenerateModels() ([]byte, error) {
models[packageName] = thisPackageCode
}
// Add outstanding enums to the models that were not in packages with structs
for packageName, enumsToGenerate := range b.enumsToGenerateTS {
if seenEnumsPackages.Contains(packageName) {
continue
}
thisPackageCode := ""
w := typescriptify.New()
w.WithPrefix(b.tsPrefix)
w.WithSuffix(b.tsSuffix)
w.WithInterface(b.tsInterface)
w.Namespace = packageName
w.WithBackupDir("")
for enumName, enum := range enumsToGenerate {
fqemumname := packageName + "." + enumName
if seen.Contains(fqemumname) {
continue
}
w.AddEnum(enum)
}
str, err := w.Convert(nil)
if err != nil {
return nil, err
}
thisPackageCode += str
models[packageName] = thisPackageCode
}
// Sort the package names first to make the output deterministic
sortedPackageNames := make([]string, 0)
for packageName := range models {
@@ -163,6 +218,39 @@ func (b *Bindings) WriteModels(modelsDir string) error {
return nil
}
func (b *Bindings) AddEnumToGenerateTS(e interface{}) {
enumType := reflect.TypeOf(e)
var packageName string
var enumName string
// enums should be represented as array of all possible values
if hasElements(enumType) {
enum := enumType.Elem()
// simple enum represented by struct with Value/TSName fields
if enum.Kind() == reflect.Struct {
_, tsNamePresented := enum.FieldByName("TSName")
enumT, valuePresented := enum.FieldByName("Value")
if tsNamePresented && valuePresented {
packageName = getPackageName(enumT.Type.String())
enumName = enumT.Type.Name()
} else {
return
}
// otherwise expecting implementation with TSName() https://github.com/tkrajina/typescriptify-golang-structs#enums-with-tsname
} else {
packageName = getPackageName(enumType.Elem().String())
enumName = enumType.Elem().Name()
}
if b.enumsToGenerateTS[packageName] == nil {
b.enumsToGenerateTS[packageName] = make(map[string]interface{})
}
if b.enumsToGenerateTS[packageName][enumName] != nil {
return
}
b.enumsToGenerateTS[packageName][enumName] = e
}
}
func (b *Bindings) AddStructToGenerateTS(packageName string, structName string, s interface{}) {
if b.structsToGenerateTS[packageName] == nil {
b.structsToGenerateTS[packageName] = make(map[string]interface{})
@@ -231,6 +319,13 @@ func (b *Bindings) SetTsSuffix(postfix string) *Bindings {
return b
}
func (b *Bindings) SetOutputType(outputType string) *Bindings {
if outputType == "interfaces" {
b.tsInterface = true
}
return b
}
func (b *Bindings) getAllStructNames() *slicer.StringSlicer {
var result slicer.StringSlicer
for packageName, structsToGenerate := range b.structsToGenerateTS {
@@ -241,6 +336,16 @@ func (b *Bindings) getAllStructNames() *slicer.StringSlicer {
return &result
}
func (b *Bindings) getAllEnumNames() *slicer.StringSlicer {
var result slicer.StringSlicer
for packageName, enumsToGenerate := range b.enumsToGenerateTS {
for enumName := range enumsToGenerate {
result.Add(packageName + "." + enumName)
}
}
return &result
}
func (b *Bindings) hasExportedJSONFields(typeOf reflect.Type) bool {
for i := 0; i < typeOf.NumField(); i++ {
jsonFieldName := ""
@@ -42,7 +42,7 @@ func TestConflictingPackageName(t *testing.T) {
// setup
testLogger := &logger.Logger{}
b := binding.NewBindings(testLogger, []interface{}{&HandlerTest{}}, []interface{}{}, false)
b := binding.NewBindings(testLogger, []interface{}{&HandlerTest{}}, []interface{}{}, false, []interface{}{})
// then
err := b.GenerateGoBindings(generationDir)
@@ -0,0 +1,50 @@
package binding_test
import "github.com/wailsapp/wails/v2/internal/binding/binding_test/binding_test_import"
type ImportedEnumStruct struct {
EnumValue binding_test_import.ImportedEnum `json:"EnumValue"`
}
func (s ImportedEnumStruct) Get() ImportedEnumStruct {
return s
}
var ImportedEnumTest = BindingTest{
name: "ImportedEnum",
structs: []interface{}{
&ImportedEnumStruct{},
},
enums: []interface{}{
binding_test_import.AllImportedEnumValues,
},
exemptions: nil,
shouldError: false,
want: `export namespace binding_test {
export class ImportedEnumStruct {
EnumValue: binding_test_import.ImportedEnum;
static createFrom(source: any = {}) {
return new ImportedEnumStruct(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.EnumValue = source["EnumValue"];
}
}
}
export namespace binding_test_import {
export enum ImportedEnum {
Value1 = "value1",
Value2 = "value2",
Value3 = "value3",
}
}
`,
}
@@ -59,7 +59,7 @@ func TestPromises(t *testing.T) {
// setup
testLogger := &logger.Logger{}
b := binding.NewBindings(testLogger, []interface{}{&PromisesTest{}}, []interface{}{}, false)
b := binding.NewBindings(testLogger, []interface{}{&PromisesTest{}}, []interface{}{}, false, []interface{}{})
// then
err := b.GenerateGoBindings(generationDir)
@@ -13,6 +13,7 @@ import (
type BindingTest struct {
name string
structs []interface{}
enums []interface{}
exemptions []interface{}
want string
shouldError bool
@@ -20,8 +21,9 @@ type BindingTest struct {
}
type TsGenerationOptionsTest struct {
TsPrefix string
TsSuffix string
TsPrefix string
TsSuffix string
TsOutputType string
}
func TestBindings_GenerateModels(t *testing.T) {
@@ -31,12 +33,17 @@ func TestBindings_GenerateModels(t *testing.T) {
ImportedStructTest,
ImportedSliceTest,
ImportedMapTest,
ImportedEnumTest,
NestedFieldTest,
NonStringMapKeyTest,
SingleFieldTest,
MultistructTest,
EmptyStructTest,
GeneratedJsEntityTest,
GeneratedJsEntityWithIntEnumTest,
GeneratedJsEntityWithStringEnumTest,
GeneratedJsEntityWithEnumTsName,
GeneratedJsEntityWithNestedStructInterfacesTest,
AnonymousSubStructTest,
AnonymousSubStructMultiLevelTest,
GeneratedJsEntityWithNestedStructTest,
@@ -46,13 +53,14 @@ func TestBindings_GenerateModels(t *testing.T) {
testLogger := &logger.Logger{}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := binding.NewBindings(testLogger, tt.structs, tt.exemptions, false)
b := binding.NewBindings(testLogger, tt.structs, tt.exemptions, false, tt.enums)
for _, s := range tt.structs {
err := b.Add(s)
require.NoError(t, err)
}
b.SetTsPrefix(tt.TsPrefix)
b.SetTsSuffix(tt.TsSuffix)
b.SetOutputType(tt.TsOutputType)
got, err := b.GenerateModels()
if (err != nil) != tt.shouldError {
t.Errorf("GenerateModels() error = %v, shouldError %v", err, tt.shouldError)
@@ -13,3 +13,20 @@ type ASliceWrapper struct {
type AMapWrapper struct {
AMap map[string]binding_test_nestedimport.A `json:"AMap"`
}
type ImportedEnum string
const (
ImportedEnumValue1 ImportedEnum = "value1"
ImportedEnumValue2 ImportedEnum = "value2"
ImportedEnumValue3 ImportedEnum = "value3"
)
var AllImportedEnumValues = []struct {
Value ImportedEnum
TSName string
}{
{ImportedEnumValue1, "Value1"},
{ImportedEnumValue2, "Value2"},
{ImportedEnumValue3, "Value3"},
}
@@ -275,3 +275,235 @@ export namespace binding_test {
`,
}
type IntEnum int
const (
IntEnumValue1 IntEnum = iota
IntEnumValue2
IntEnumValue3
)
var AllIntEnumValues = []struct {
Value IntEnum
TSName string
}{
{IntEnumValue1, "Value1"},
{IntEnumValue2, "Value2"},
{IntEnumValue3, "Value3"},
}
type EntityWithIntEnum struct {
Name string `json:"name"`
Enum IntEnum `json:"enum"`
}
func (e EntityWithIntEnum) Get() EntityWithIntEnum {
return e
}
var GeneratedJsEntityWithIntEnumTest = BindingTest{
name: "GeneratedJsEntityWithIntEnumTest",
structs: []interface{}{
&EntityWithIntEnum{},
},
enums: []interface{}{
AllIntEnumValues,
},
exemptions: nil,
shouldError: false,
TsGenerationOptionsTest: TsGenerationOptionsTest{
TsPrefix: "MY_PREFIX_",
TsSuffix: "_MY_SUFFIX",
},
want: `export namespace binding_test {
export enum MY_PREFIX_IntEnum_MY_SUFFIX {
Value1 = 0,
Value2 = 1,
Value3 = 2,
}
export class MY_PREFIX_EntityWithIntEnum_MY_SUFFIX {
name: string;
enum: MY_PREFIX_IntEnum_MY_SUFFIX;
static createFrom(source: any = {}) {
return new MY_PREFIX_EntityWithIntEnum_MY_SUFFIX(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.name = source["name"];
this.enum = source["enum"];
}
}
}
`,
}
type StringEnum string
const (
StringEnumValue1 StringEnum = "value1"
StringEnumValue2 StringEnum = "value2"
StringEnumValue3 StringEnum = "value3"
)
var AllStringEnumValues = []struct {
Value StringEnum
TSName string
}{
{StringEnumValue1, "Value1"},
{StringEnumValue2, "Value2"},
{StringEnumValue3, "Value3"},
}
type EntityWithStringEnum struct {
Name string `json:"name"`
Enum StringEnum `json:"enum"`
}
func (e EntityWithStringEnum) Get() EntityWithStringEnum {
return e
}
var GeneratedJsEntityWithStringEnumTest = BindingTest{
name: "GeneratedJsEntityWithStringEnumTest",
structs: []interface{}{
&EntityWithStringEnum{},
},
enums: []interface{}{
AllStringEnumValues,
},
exemptions: nil,
shouldError: false,
TsGenerationOptionsTest: TsGenerationOptionsTest{
TsPrefix: "MY_PREFIX_",
TsSuffix: "_MY_SUFFIX",
},
want: `export namespace binding_test {
export enum MY_PREFIX_StringEnum_MY_SUFFIX {
Value1 = "value1",
Value2 = "value2",
Value3 = "value3",
}
export class MY_PREFIX_EntityWithStringEnum_MY_SUFFIX {
name: string;
enum: MY_PREFIX_StringEnum_MY_SUFFIX;
static createFrom(source: any = {}) {
return new MY_PREFIX_EntityWithStringEnum_MY_SUFFIX(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.name = source["name"];
this.enum = source["enum"];
}
}
}
`,
}
type EnumWithTsName string
const (
EnumWithTsName1 EnumWithTsName = "value1"
EnumWithTsName2 EnumWithTsName = "value2"
EnumWithTsName3 EnumWithTsName = "value3"
)
var AllEnumWithTsNameValues = []EnumWithTsName{EnumWithTsName1, EnumWithTsName2, EnumWithTsName3}
func (v EnumWithTsName) TSName() string {
switch v {
case EnumWithTsName1:
return "TsName1"
case EnumWithTsName2:
return "TsName2"
case EnumWithTsName3:
return "TsName3"
default:
return "???"
}
}
type EntityWithEnumTsName struct {
Name string `json:"name"`
Enum EnumWithTsName `json:"enum"`
}
func (e EntityWithEnumTsName) Get() EntityWithEnumTsName {
return e
}
var GeneratedJsEntityWithEnumTsName = BindingTest{
name: "GeneratedJsEntityWithEnumTsName",
structs: []interface{}{
&EntityWithEnumTsName{},
},
enums: []interface{}{
AllEnumWithTsNameValues,
},
exemptions: nil,
shouldError: false,
TsGenerationOptionsTest: TsGenerationOptionsTest{
TsPrefix: "MY_PREFIX_",
TsSuffix: "_MY_SUFFIX",
},
want: `export namespace binding_test {
export enum MY_PREFIX_EnumWithTsName_MY_SUFFIX {
TsName1 = "value1",
TsName2 = "value2",
TsName3 = "value3",
}
export class MY_PREFIX_EntityWithEnumTsName_MY_SUFFIX {
name: string;
enum: MY_PREFIX_EnumWithTsName_MY_SUFFIX;
static createFrom(source: any = {}) {
return new MY_PREFIX_EntityWithEnumTsName_MY_SUFFIX(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.name = source["name"];
this.enum = source["enum"];
}
}
}
`,
}
var GeneratedJsEntityWithNestedStructInterfacesTest = BindingTest{
name: "GeneratedJsEntityWithNestedStructInterfacesTest",
structs: []interface{}{
&ParentEntity{},
},
exemptions: nil,
shouldError: false,
TsGenerationOptionsTest: TsGenerationOptionsTest{
TsPrefix: "MY_PREFIX_",
TsSuffix: "_MY_SUFFIX",
TsOutputType: "interfaces",
},
want: `export namespace binding_test {
export interface MY_PREFIX_ChildEntity_MY_SUFFIX {
name: string;
childProp: number;
}
export interface MY_PREFIX_ParentEntity_MY_SUFFIX {
name: string;
ref: MY_PREFIX_ChildEntity_MY_SUFFIX;
parentProp: string;
}
}
`,
}
@@ -41,7 +41,7 @@ func TestAliases(t *testing.T) {
// setup
testLogger := &logger.Logger{}
b := binding.NewBindings(testLogger, []interface{}{&AliasTest{}}, []interface{}{}, false)
b := binding.NewBindings(testLogger, []interface{}{&AliasTest{}}, []interface{}{}, false, []interface{}{})
// then
err := b.GenerateGoBindings(generationDir)
+1 -1
View File
@@ -25,7 +25,7 @@ type B struct {
func TestNestedStruct(t *testing.T) {
bind := &BindForTest{}
testBindings := NewBindings(logger.New(nil), []interface{}{bind}, []interface{}{}, false)
testBindings := NewBindings(logger.New(nil), []interface{}{bind}, []interface{}{}, false, []interface{}{})
namesStrSlicer := testBindings.getAllStructNames()
names := []string{}
+3 -2
View File
@@ -242,8 +242,9 @@ type Bindings struct {
}
type TsGeneration struct {
Prefix string `json:"prefix"`
Suffix string `json:"suffix"`
Prefix string `json:"prefix"`
Suffix string `json:"suffix"`
OutputType string `json:"outputType"`
}
// Parse the given JSON data into a Project struct
+11 -1
View File
@@ -104,6 +104,7 @@ type TypeScriptify struct {
Namespace string
KnownStructs *slicer.StringSlicer
KnownEnums *slicer.StringSlicer
}
func New() *TypeScriptify {
@@ -723,7 +724,16 @@ func (t *TypeScriptify) convertType(depth int, typeOf reflect.Type, customCode m
}
} else { // Simple field:
t.logf(depth, "- simple field %s.%s", typeOf.Name(), field.Name)
err = builder.AddSimpleField(jsonFieldName, field, fldOpts)
// check if type is in known enum. If so, then replace TStype with enum name to avoid missing types
isKnownEnum := t.KnownEnums.Contains(getStructFQN(field.Type.String()))
if isKnownEnum {
err = builder.AddSimpleField(jsonFieldName, field, TypeOptions{
TSType: getStructFQN(field.Type.String()),
TSTransform: fldOpts.TSTransform,
})
} else {
err = builder.AddSimpleField(jsonFieldName, field, fldOpts)
}
}
if err != nil {
return "", err
+2
View File
@@ -21,6 +21,7 @@ type Options struct {
GoModTidy bool
TsPrefix string
TsSuffix string
TsOutputType string
}
// GenerateBindings generates bindings for the Wails project in the given ProjectDirectory.
@@ -65,6 +66,7 @@ func GenerateBindings(options Options) (string, error) {
env := os.Environ()
env = shell.SetEnv(env, "tsprefix", options.TsPrefix)
env = shell.SetEnv(env, "tssuffix", options.TsSuffix)
env = shell.SetEnv(env, "tsoutputtype", options.TsOutputType)
stdout, stderr, err = shell.RunCommandWithEnv(env, workingDirectory, filename)
if err != nil {
+9 -4
View File
@@ -219,12 +219,17 @@ func GenerateBindings(buildOptions *Options) error {
printBulletPoint("Generating bindings: ")
}
if buildOptions.ProjectData.Bindings.TsGeneration.OutputType == "" {
buildOptions.ProjectData.Bindings.TsGeneration.OutputType = "classes"
}
// Generate Bindings
output, err := bindings.GenerateBindings(bindings.Options{
Tags: buildOptions.UserTags,
GoModTidy: !buildOptions.SkipModTidy,
TsPrefix: buildOptions.ProjectData.Bindings.TsGeneration.Prefix,
TsSuffix: buildOptions.ProjectData.Bindings.TsGeneration.Suffix,
Tags: buildOptions.UserTags,
GoModTidy: !buildOptions.SkipModTidy,
TsPrefix: buildOptions.ProjectData.Bindings.TsGeneration.Prefix,
TsSuffix: buildOptions.ProjectData.Bindings.TsGeneration.Suffix,
TsOutputType: buildOptions.ProjectData.Bindings.TsGeneration.OutputType,
})
if err != nil {
return err
+1
View File
@@ -63,6 +63,7 @@ type App struct {
OnShutdown func(ctx context.Context) `json:"-"`
OnBeforeClose func(ctx context.Context) (prevent bool) `json:"-"`
Bind []interface{}
EnumBind []interface{}
WindowStartState WindowStartState
// ErrorFormatter overrides the formatting of errors returned by backend methods
@@ -144,6 +144,65 @@ func main() {
}
```
Also you might want to use Enums in your structs and have models for them on frontend.
In that case you should create array that will contain all possible enum values, instrument enum type and bind it to the app:
```go {16-18} title="app.go"
type Weekday string
const (
Sunday Weekday = "Sunday"
Monday Weekday = "Monday"
Tuesday Weekday = "Tuesday"
Wednesday Weekday = "Wednesday"
Thursday Weekday = "Thursday"
Friday Weekday = "Friday"
Saturday Weekday = "Saturday"
)
var AllWeekdays = []struct {
Value Weekday
TSName string
}{
{Sunday, "SUNDAY"},
{Monday, "MONDAY"},
{Tuesday, "TUESDAY"},
{Wednesday, "WEDNESDAY"},
{Thursday, "THURSDAY"},
{Friday, "FRIDAY"},
{Saturday, "SATURDAY"},
}
```
In the main application configuration, the `EnumBind` key is where we can tell Wails what we want to bind enums as well:
```go {11-13} title="main.go"
func main() {
app := NewApp()
err := wails.Run(&options.App{
Title: "My App",
Width: 800,
Height: 600,
OnStartup: app.startup,
OnShutdown: app.shutdown,
Bind: []interface{}{
app,
},
EnumBind: []interface{}{
AllWeekdays,
},
})
if err != nil {
log.Fatal(err)
}
}
```
This will add missing enums to your `model.ts` file.
More information on Binding can be found [here](../howdoesitwork.mdx#method-binding).
## Application Menu
+51
View File
@@ -206,6 +206,57 @@ You may bind as many structs as you like. Just make sure you create an instance
```
You may bind enums types as well.
In that case you should create array that will contain all possible enum values, instrument enum type and bind it to the app via `EnumBind`:
```go {16-18} title="app.go"
type Weekday string
const (
Sunday Weekday = "Sunday"
Monday Weekday = "Monday"
Tuesday Weekday = "Tuesday"
Wednesday Weekday = "Wednesday"
Thursday Weekday = "Thursday"
Friday Weekday = "Friday"
Saturday Weekday = "Saturday"
)
var AllWeekdays = []struct {
Value Weekday
TSName string
}{
{Sunday, "SUNDAY"},
{Monday, "MONDAY"},
{Tuesday, "TUESDAY"},
{Wednesday, "WEDNESDAY"},
{Thursday, "THURSDAY"},
{Friday, "FRIDAY"},
{Saturday, "SATURDAY"},
}
```
```go {10-12}
//...
err := wails.Run(&options.App{
Title: "Basic Demo",
Width: 1024,
Height: 768,
AssetServer: &assetserver.Options{
Assets: assets,
},
Bind: []interface{}{
app,
&mystruct1{},
&mystruct2{},
},
EnumBind: []interface{}{
AllWeekdays,
},
})
```
When you run `wails dev` (or `wails generate module`), a frontend module will be generated containing the following:
- JavaScript bindings for all bound methods

Some files were not shown because too many files have changed in this diff Show More