Berkontribusi

Edit dokumentasi ini

Terjemahkan dokumentasi atau buka proyek lengkap di komputer Anda.

Tidak ada yang dipublikasikan hingga Anda memilih untuk mengirimkan pekerjaan Anda.

Model Data

Ikat struktur data kompleks antara Go dan JavaScript

Pengikatan Model Data

Wails secara otomatis menghasilkan kelas JavaScript/TypeScript dari struct Go, sehingga memberikan keamanan tipe penuh saat meneruskan data kompleks antara backend dan frontend. Tulis struct Go, hasilkan pengikatan, lalu dapatkan model frontend bertipe lengkap beserta konstruktor, anotasi tipe, dan komentar JSDoc.

Mulai Cepat

Struct Go:

go
type User struct {
    ID        int       `json:"id"`
    Name      string    `json:"name"`
    Email     string    `json:"email"`
    CreatedAt time.Time `json:"createdAt"`
}

func (s *UserService) GetUser(id int) (*User, error) {
    // Return user
}

Hasilkan:

bash
wails3 generate bindings

JavaScript:

javascript
import { GetUser } from './bindings/changeme/userservice'
import { User } from './bindings/changeme/models'

const user = await GetUser(1)
console.log(user.Name)  // Type-safe!

Selesai! Keamanan tipe penuh di seluruh penghubung.

Mendefinisikan Model

Struct Dasar

go
type Person struct {
    Name string
    Age  int
}

JavaScript yang dihasilkan:

javascript
export class Person {
    /** @type {string} */
    Name = ""
    
    /** @type {number} */
    Age = 0
    
    constructor(source = {}) {
        Object.assign(this, source)
    }
    
    static createFrom(source = {}) {
        return new Person(source)
    }
}

Dengan Tag JSON

go
type User struct {
    ID        int       `json:"id"`
    Name      string    `json:"name"`
    Email     string    `json:"email"`
    CreatedAt time.Time `json:"createdAt"`
}

JavaScript yang dihasilkan:

javascript
export class User {
    /** @type {number} */
    id = 0
    
    /** @type {string} */
    name = ""
    
    /** @type {string} */
    email = ""
    
    /** @type {Date} */
    createdAt = new Date()
    
    constructor(source = {}) {
        Object.assign(this, source)
    }
}

Tag JSON mengendalikan nama field dalam JavaScript.

Marshalling JSON Kustom

Hasil metode yang diikat di-marshalling dengan paket standar encoding/json milik Go. Perhatikan bahwa metode MarshalJSON yang dideklarasikan pada receiver pointer hanya dipanggil jika nilai yang di-marshalling dapat dialamati. Artinya, marshaler kustom dapat dipanggil untuk elemen slice yang dikembalikan, tetapi tidak untuk struct yang dikembalikan sebagai nilai:

go
func (n *Nullable[T]) MarshalJSON() ([]byte, error) {
    if n.Valid {
        return json.Marshal(n.V)
    }
    return []byte("null"), nil
}

func (s *UserService) GetUser() User {
    return User{} // pointer receiver may not be called
}

func (s *UserService) GetUsers() []User {
    return []User{{}} // pointer receiver is called for elements
}

Untuk memastikan keluaran yang konsisten, definisikan MarshalJSON pada receiver nilai jika tipe tersebut dapat disalin dengan aman, atau kembalikan pointer ke struct (misalnya, *User). Ini merupakan perilaku encoding/json milik Go, bukan perbedaan dalam pembuatan pengikatan Wails.

Dengan Komentar

go
// User represents an application user
type User struct {
    // Unique identifier
    ID int `json:"id"`
    
    // Full name of the user
    Name string `json:"name"`
    
    // Email address (must be unique)
    Email string `json:"email"`
}

JavaScript yang dihasilkan:

javascript
/**
 * User represents an application user
 */
export class User {
    /**
     * Unique identifier
     * @type {number}
     */
    id = 0
    
    /**
     * Full name of the user
     * @type {string}
     */
    name = ""
    
    /**
     * Email address (must be unique)
     * @type {string}
     */
    email = ""
}

Komentar menjadi JSDoc! IDE Anda akan menampilkannya.

Struct Bersarang

go
type Address struct {
    Street  string `json:"street"`
    City    string `json:"city"`
    Country string `json:"country"`
}

type User struct {
    ID      int     `json:"id"`
    Name    string  `json:"name"`
    Address Address `json:"address"`
}

JavaScript yang dihasilkan:

javascript
export class Address {
    /** @type {string} */
    street = ""
    
    /** @type {string} */
    city = ""
    
    /** @type {string} */
    country = ""
}

export class User {
    /** @type {number} */
    id = 0
    
    /** @type {string} */
    name = ""
    
    /** @type {Address} */
    address = new Address()
}

Penggunaan:

javascript
const user = new User({
    id: 1,
    name: "Alice",
    address: new Address({
        street: "123 Main St",
        city: "Springfield",
        country: "USA"
    })
})

Array dan Slice

go
type Team struct {
    Name    string   `json:"name"`
    Members []string `json:"members"`
}

JavaScript yang dihasilkan:

javascript
export class Team {
    /** @type {string} */
    name = ""
    
    /** @type {string[]} */
    members = []
}

Penggunaan:

javascript
const team = new Team({
    name: "Engineering",
    members: ["Alice", "Bob", "Charlie"]
})

Map

go
type Config struct {
    Settings map[string]string `json:"settings"`
}

JavaScript yang dihasilkan:

javascript
export class Config {
    /** @type {Record<string, string>} */
    settings = {}
}

Penggunaan:

javascript
const config = new Config({
    settings: {
        theme: "dark",
        language: "en"
    }
})

Pemetaan Tipe

Tipe Primitif

Tipe Go JavaScript/TypeScript
string string
bool boolean
int, int8, int16, int32, int64 number
uint, uint8, uint16, uint32, uint64 number
float32, float64 number
byte number
rune number

Tipe Kompleks

Tipe Go JavaScript/TypeScript Catatan
[]T T[] -
[N]T T[] -
map[string]T { [_: string]: T } map dengan kunci string
map[K]V { [_ in K]?: V } K non-string dirender sebagai tipe terpetakan, bukan Map JS
[]byte string dikodekan dengan base64
struct class / interface dengan field
time.Time any diserialisasikan sebagai string RFC3339Nano saat runtime
*T T | null pointer berarti dapat bernilai null
any / interface{} any -
error any / Exception Exception jika digunakan sebagai nilai kembalian; jika tidak, any

Tipe yang Tidak Didukung

  • chan T (channel)
  • func() (fungsi)
  • Interface kompleks (kecuali interface{})
  • Field yang tidak diekspor (huruf kecil)

Menggunakan Model

Membuat Instans

javascript
import { User } from './bindings/changeme/models'

// Empty instance
const user1 = new User()

// With data
const user2 = new User({
    id: 1,
    name: "Alice",
    email: "alice@example.com"
})

// From JSON string
const user3 = User.createFrom('{"id":1,"name":"Alice"}')

// From object
const user4 = User.createFrom({ id: 1, name: "Alice" })

Meneruskan ke Go

javascript
import { CreateUser } from './bindings/changeme/userservice'
import { User } from './bindings/changeme/models'

const user = new User({
    name: "Bob",
    email: "bob@example.com"
})

const created = await CreateUser(user)
console.log("Created user:", created.id)

Menerima dari Go

javascript
import { GetUser } from './bindings/changeme/userservice'

const user = await GetUser(1)

// user is already a User instance
console.log(user.name)
console.log(user.email)
console.log(user.createdAt.toISOString())

Memperbarui Model

javascript
import { GetUser, UpdateUser } from './bindings/changeme/userservice'

// Get user
const user = await GetUser(1)

// Modify
user.name = "Alice Smith"
user.email = "alice.smith@example.com"

// Save
await UpdateUser(user)

Dukungan TypeScript

TypeScript yang Dihasilkan

bash
wails3 generate bindings -ts

Hasil pembuatan:

typescript
/**
 * User represents an application user
 */
export class User {
    /**
     * Unique identifier
     */
    id: number = 0
    
    /**
     * Full name of the user
     */
    name: string = ""
    
    /**
     * Email address (must be unique)
     */
    email: string = ""
    
    /**
     * Account creation date
     */
    createdAt: Date = new Date()
    
    constructor(source: Partial<User> = {}) {
        Object.assign(this, source)
    }
    
    static createFrom(source: string | Partial<User> = {}): User {
        const parsedSource = typeof source === 'string' 
            ? JSON.parse(source) 
            : source
        return new User(parsedSource)
    }
}

Penggunaan di TypeScript

typescript
import { GetUser, CreateUser } from './bindings/changeme/userservice'
import { User } from './bindings/changeme/models'

async function example() {
    // Create user
    const newUser = new User({
        name: "Alice",
        email: "alice@example.com"
    })
    
    const created: User = await CreateUser(newUser)
    
    // Get user
    const user: User = await GetUser(created.id)
    
    // Type-safe access
    console.log(user.name.toUpperCase())  // ✅ string method
    console.log(user.id + 1)              // ✅ number operation
    console.log(user.createdAt.getTime()) // ✅ Date method
}

Pola Lanjutan

Field Opsional

go
type User struct {
    ID       int     `json:"id"`
    Name     string  `json:"name"`
    Nickname *string `json:"nickname,omitempty"`
}

JavaScript:

javascript
const user = new User({
    id: 1,
    name: "Alice",
    nickname: "Ally"  // Optional
})

// Check if set
if (user.nickname) {
    console.log("Nickname:", user.nickname)
}

Enum

Generator binding secara otomatis mendeteksi tipe bernama Go yang memiliki konstanta dan menghasilkan enum TypeScript atau objek const JavaScript—termasuk anggota $zero untuk nilai nol Go serta mempertahankan JSDoc secara utuh.

go
type UserRole string

const (
    RoleAdmin UserRole = "admin"
    RoleUser  UserRole = "user"
    RoleGuest UserRole = "guest"
)

TypeScript yang dihasilkan:

typescript
export enum UserRole {
    $zero = "",
    RoleAdmin = "admin",
    RoleUser = "user",
    RoleGuest = "guest",
}

Penggunaan:

javascript
import { User, UserRole } from './bindings/changeme/models'

const admin = new User({
    name: "Admin",
    role: UserRole.RoleAdmin
})

Untuk pembahasan lengkap tentang enum string, enum bilangan bulat, alias tipe, enum dari package yang diimpor, dan batasannya, lihat halaman khusus Enum.

Validasi

javascript
class User {
    validate() {
        if (!this.name) {
            throw new Error("Name is required")
        }
        if (!this.email.includes('@')) {
            throw new Error("Invalid email")
        }
        return true
    }
}

// Use
const user = new User({ name: "Alice", email: "alice@example.com" })
user.validate()  // ✅

const invalid = new User({ name: "", email: "invalid" })
invalid.validate()  // ❌ Throws

Serialisasi

javascript
// To JSON
const json = JSON.stringify(user)

// From JSON
const user = User.createFrom(json)

// To plain object
const obj = { ...user }

// From plain object
const user2 = new User(obj)

Contoh Lengkap

Go:

go
package main

import (
    "time"
    "github.com/wailsapp/wails/v3/pkg/application"
)

type Address struct {
    Street  string `json:"street"`
    City    string `json:"city"`
    Country string `json:"country"`
}

type User struct {
    ID        int       `json:"id"`
    Name      string    `json:"name"`
    Email     string    `json:"email"`
    Address   Address   `json:"address"`
    CreatedAt time.Time `json:"createdAt"`
}

type UserService struct {
    users []User
}

func (s *UserService) GetAll() []User {
    return s.users
}

func (s *UserService) GetByID(id int) (*User, error) {
    for _, user := range s.users {
        if user.ID == id {
            return &user, nil
        }
    }
    return nil, fmt.Errorf("user %d not found", id)
}

func (s *UserService) Create(user User) User {
    user.ID = len(s.users) + 1
    user.CreatedAt = time.Now()
    s.users = append(s.users, user)
    return user
}

func (s *UserService) Update(user User) error {
    for i, u := range s.users {
        if u.ID == user.ID {
            s.users[i] = user
            return nil
        }
    }
    return fmt.Errorf("user %d not found", user.ID)
}

func main() {
    app := application.New(application.Options{
        Services: []application.Service{
            application.NewService(&UserService{}),
        },
    })
    
    app.Window.New()
    app.Run()
}

JavaScript:

javascript
import { GetAll, GetByID, Create, Update } from './bindings/changeme/userservice'
import { User, Address } from './bindings/changeme/models'

class UserManager {
    async loadUsers() {
        const users = await GetAll()
        this.renderUsers(users)
    }
    
    async createUser(name, email, address) {
        const user = new User({
            name,
            email,
            address: new Address(address)
        })
        
        try {
            const created = await Create(user)
            console.log("Created user:", created.id)
            this.loadUsers()
        } catch (error) {
            console.error("Failed to create user:", error)
        }
    }
    
    async updateUser(id, updates) {
        try {
            const user = await GetByID(id)
            Object.assign(user, updates)
            await Update(user)
            this.loadUsers()
        } catch (error) {
            console.error("Failed to update user:", error)
        }
    }
    
    renderUsers(users) {
        const list = document.getElementById('users')
        list.innerHTML = users.map(user => `
            <div class="user">
                <h3>${user.name}</h3>
                <p>${user.email}</p>
                <p>${user.address.city}, ${user.address.country}</p>
                <small>Created: ${user.createdAt.toLocaleDateString()}</small>
            </div>
        `).join('')
    }
}

const manager = new UserManager()
manager.loadUsers()

Praktik Terbaik

✅ Lakukan

  • Gunakan tag JSON—Kendalikan nama field
  • Tambahkan komentar—Komentar tersebut akan menjadi JSDoc
  • Gunakan time.Time—Dikonversi menjadi Date
  • Lakukan validasi di sisi Go—Jangan percayai frontend
  • Pertahankan model tetap sederhana—Gunakan hanya sebagai wadah data
  • Gunakan pointer untuk field opsional—Gunakan *string agar dapat bernilai null

❌ Jangan Lakukan

  • Jangan tambahkan metode ke struct Go—Pertahankan sebagai data
  • Jangan gunakan field yang tidak diekspor—Field tersebut tidak akan di-binding
  • Jangan gunakan interface kompleks—Tidak didukung
  • Jangan lupakan tag JSON—Nama field itu penting
  • Jangan membuat struktur bersarang terlalu dalam—Pertahankan tetap sederhana

Langkah Berikutnya

Binding Metode

Pelajari cara melakukan binding pada metode Go.

Pelajari Selengkapnya →

Layanan

Atur kode dengan layanan.

Pelajari Selengkapnya →

Praktik Terbaik

Pola desain binding.

Pelajari Selengkapnya →

Jembatan Go-Frontend

Pahami mekanisme jembatan.

Pelajari Selengkapnya →


Ada pertanyaan? Tanyakan di Discord atau lihat contoh binding.

Edit halaman

Terakhir diperbarui: