Skip to content

Commit 436d200

Browse files
committed
Update docs
1 parent 0056139 commit 436d200

File tree

2 files changed

+205
-33
lines changed

2 files changed

+205
-33
lines changed

docs/src/content/docs/learn/services.mdx

+6-9
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,16 @@ application.NewServiceWithOptions(fileserver.New(&fileserver.Config{
133133
Let's look at a simplified version of the `fileserver` service as an example:
134134

135135
```go
136+
type Config struct {
137+
RootPath string
138+
}
139+
136140
type Service struct {
137-
config *Config
138-
fs http.Handler
141+
fs http.Handler
139142
}
140143

141-
func New(config *Config) *Service {
144+
func NewWithConfig(config *Config) *Service {
142145
return &Service{
143-
config: config,
144146
fs: http.FileServer(http.Dir(config.RootPath)),
145147
}
146148
}
@@ -149,11 +151,6 @@ func (s *Service) ServiceName() string {
149151
return "github.com/wailsapp/wails/v3/services/fileserver"
150152
}
151153

152-
func (s *Service) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
153-
// Any initialization code here
154-
return nil
155-
}
156-
157154
func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
158155
s.fs.ServeHTTP(w, r)
159156
}

docs/src/content/docs/tutorials/01-creating-a-service.mdx

+199-24
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ This will show you how to organize your code into reusable services and handle e
4040
return &QRService{}
4141
}
4242

43-
// GenerateQRCode creates a QR code from the given text
44-
func (s *QRService) GenerateQRCode(text string, size int) ([]byte, error) {
43+
// Generate creates a QR code from the given text
44+
func (s *QRService) Generate(text string, size int) ([]byte, error) {
4545
// Generate the QR code
4646
qr, err := qrcode.New(text, qrcode.Medium)
4747
if err != nil {
@@ -149,21 +149,21 @@ This will show you how to organize your code into reusable services and handle e
149149
// This file is automatically generated. DO NOT EDIT
150150

151151
/**
152-
* QRService handles QR code generation
153-
* @module
154-
*/
152+
* QRService handles QR code generation
153+
* @module
154+
*/
155155

156156
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
157157
// @ts-ignore: Unused imports
158158
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
159159

160160
/**
161-
* GenerateQRCode creates a QR code from the given text
162-
* @param {string} text
163-
* @param {number} size
164-
* @returns {Promise<string> & { cancel(): void }}
165-
*/
166-
export function GenerateQRCode(text, size) {
161+
* Generate creates a QR code from the given text
162+
* @param {string} text
163+
* @param {number} size
164+
* @returns {Promise<string> & { cancel(): void }}
165+
*/
166+
export function Generate(text, size) {
167167
let $resultPromise = /** @type {any} */($Call.ByID(3576998831, text, size));
168168
let $typingPromise = /** @type {any} */($resultPromise.then(($result) => {
169169
return $Create.ByteSlice($result);
@@ -173,7 +173,7 @@ This will show you how to organize your code into reusable services and handle e
173173
}
174174
```
175175

176-
We can see that the bindings are generated for the `GenerateQRCode` method. The parameter names have been preserved,
176+
We can see that the bindings are generated for the `Generate` method. The parameter names have been preserved,
177177
as well as the comments. JSDoc has also been generated for the method to provide type information to your IDE.
178178

179179
:::note
@@ -190,14 +190,36 @@ This will show you how to organize your code into reusable services and handle e
190190
The bindings generator also supports generating Typescript bindings. You can do this by running `wails3 generate bindings -ts`.
191191
:::
192192

193-
<br/>
193+
The generated service is re-exported by an `index.js` file:
194+
195+
```js title="bindings/changeme/index.js"
196+
// @ts-check
197+
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
198+
// This file is automatically generated. DO NOT EDIT
199+
200+
import * as QRService from "./qrservice.js";
201+
export {
202+
QRService
203+
};
204+
```
205+
206+
You may then access it through the simplified import path
207+
`./bindings/changeme` consisting just of your Go package path,
208+
without specifying any file name.
209+
210+
:::note
211+
Simplified import paths are only available when using frontend bundlers.
212+
If have a vanilla frontend that does not employ a bundler,
213+
you will have to import either `index.js` or `qrservice.js` manually.
214+
:::
215+
<br/>
194216

195217
6. ## Use Bindings in Frontend
196218

197219
Firstly, update `frontend/src/main.js` to use the new bindings:
198220

199221
```js title="frontend/src/main.js"
200-
import { GenerateQRCode } from './bindings/changeme/qrservice.js';
222+
import { QRService } from './bindings/changeme';
201223

202224
async function generateQR() {
203225
const text = document.getElementById('text').value;
@@ -208,7 +230,7 @@ This will show you how to organize your code into reusable services and handle e
208230

209231
try {
210232
// Generate QR code as base64
211-
const qrCodeBase64 = await GenerateQRCode(text, 256);
233+
const qrCodeBase64 = await QRService.Generate(text, 256);
212234

213235
// Display the QR code
214236
const qrDiv = document.getElementById('qrcode');
@@ -285,7 +307,7 @@ This will show you how to organize your code into reusable services and handle e
285307

286308
Type in some text and click the "Generate QR Code" button. You should see a QR code in the center of the page:
287309

288-
<Image src={qr1} alt="QR Code"/>
310+
<Image src={qr1} alt="QR Code"/>
289311

290312
<br/>
291313
<br/>
@@ -303,13 +325,14 @@ This will show you how to organize your code into reusable services and handle e
303325
If your service defines Go's standard http handler function `ServeHTTP(w http.ResponseWriter, r *http.Request)`,
304326
then it can be made accessible on the frontend. Let's extend our QR code service to do this:
305327

306-
```go title="qrservice.go" ins={5-6,36-63}
328+
```go title="qrservice.go" ins={4-5,37-65}
307329
package main
308330

309331
import (
310-
"github.com/skip2/go-qrcode"
311332
"net/http"
312333
"strconv"
334+
335+
"github.com/skip2/go-qrcode"
313336
)
314337

315338
// QRService handles QR code generation
@@ -322,8 +345,8 @@ This will show you how to organize your code into reusable services and handle e
322345
return &QRService{}
323346
}
324347

325-
// GenerateQRCode creates a QR code from the given text
326-
func (s *QRService) GenerateQRCode(text string, size int) ([]byte, error) {
348+
// Generate creates a QR code from the given text
349+
func (s *QRService) Generate(text string, size int) ([]byte, error) {
327350
// Generate the QR code
328351
qr, err := qrcode.New(text, qrcode.Medium)
329352
if err != nil {
@@ -358,7 +381,7 @@ This will show you how to organize your code into reusable services and handle e
358381
}
359382

360383
// Generate the QR code
361-
qrCodeData, err := s.GenerateQRCode(text, size)
384+
qrCodeData, err := s.Generate(text, size)
362385
if err != nil {
363386
http.Error(w, err.Error(), http.StatusInternalServerError)
364387
return
@@ -408,11 +431,14 @@ This will show you how to organize your code into reusable services and handle e
408431
}
409432
```
410433

434+
:::note
435+
If you do not set the `Route` option explicitly,
436+
the HTTP handler won't be accessible from the frontend.
437+
:::
438+
411439
Finally, update `main.js` to make the image source the path to the QR code service, passing the text as a query parameter:
412440

413441
```js title="frontend/src/main.js"
414-
import { GenerateQRCode } from './bindings/changeme/qrservice.js';
415-
416442
async function generateQR() {
417443
const text = document.getElementById('text').value;
418444
if (!text) {
@@ -422,7 +448,7 @@ This will show you how to organize your code into reusable services and handle e
422448

423449
const img = document.getElementById('qrcode');
424450
// Make the image source the path to the QR code service, passing the text
425-
img.src = `/qrservice?text=${text}`
451+
img.src = `/qrservice?text=${encodeURIComponent(text)}`
426452
}
427453

428454
export function initializeQRGenerator() {
@@ -440,4 +466,153 @@ This will show you how to organize your code into reusable services and handle e
440466
<Image src={qr1} alt="QR Code"/>
441467
<br/>
442468

469+
8. ## Supporting dynamic configurations
470+
471+
In the example above we used an hardcoded route `/qrservice`.
472+
If you edit `main.go` and change the `Route` option without updating `main.js`,
473+
the application will break:
474+
475+
```go title="main.go" ins={3}
476+
// ...
477+
application.NewService(NewQRService(), application.ServiceOptions{
478+
Route: "/services/qr",
479+
}),
480+
// ...
481+
```
482+
483+
Hardcoded routes can be good for many applications,
484+
but if you need more flexibility, method bindings and HTTP handlers
485+
can work together to improve the development experience.
486+
487+
The `ServiceStartup` Lifecycle method provides access to service options at startup,
488+
and a custom method can be used to announce the configured route to the frontend.
489+
490+
First, implement the `ServiceStartup` interface and add a new `URL` method:
491+
492+
```go title="qrservice.go" ins={4,6,10,15,23-27,46-55}
493+
package main
494+
495+
import (
496+
"context"
497+
"net/http"
498+
"net/url"
499+
"strconv"
500+
501+
"github.com/skip2/go-qrcode"
502+
"github.com/wailsapp/wails/v3/pkg/application"
503+
)
504+
505+
// QRService handles QR code generation
506+
type QRService struct {
507+
route string
508+
}
509+
510+
// NewQRService creates a new QR service
511+
func NewQRService() *QRService {
512+
return &QRService{}
513+
}
514+
515+
// ServiceStartup runs at application startup.
516+
func (s *QRService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
517+
s.route = options.Route
518+
return nil
519+
}
520+
521+
// Generate creates a QR code from the given text
522+
func (s *QRService) Generate(text string, size int) ([]byte, error) {
523+
// Generate the QR code
524+
qr, err := qrcode.New(text, qrcode.Medium)
525+
if err != nil {
526+
return nil, err
527+
}
528+
529+
// Convert to PNG
530+
png, err := qr.PNG(size)
531+
if err != nil {
532+
return nil, err
533+
}
534+
535+
return png, nil
536+
}
537+
538+
// URL returns an URL that may be used to fetch
539+
// a QR code with the given text and size.
540+
// It returns an error if the HTTP handler is not available.
541+
func (s *QRService) URL(text string, size int) (string, error) {
542+
if s.route == "" {
543+
return "", errors.New("http handler unavailable")
544+
}
545+
546+
return fmt.Sprintf("%s?text=%s&size=%d", s.route, url.QueryEscape(text), size), nil
547+
}
548+
549+
func (s *QRService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
550+
// Extract the text parameter from the request
551+
text := r.URL.Query().Get("text")
552+
if text == "" {
553+
http.Error(w, "Missing 'text' parameter", http.StatusBadRequest)
554+
return
555+
}
556+
// Extract Size parameter from the request
557+
sizeText := r.URL.Query().Get("size")
558+
if sizeText == "" {
559+
sizeText = "256"
560+
}
561+
size, err := strconv.Atoi(sizeText)
562+
if err != nil {
563+
http.Error(w, "Invalid 'size' parameter", http.StatusBadRequest)
564+
return
565+
}
566+
567+
// Generate the QR code
568+
qrCodeData, err := s.Generate(text, size)
569+
if err != nil {
570+
http.Error(w, err.Error(), http.StatusInternalServerError)
571+
return
572+
}
573+
574+
// Write the QR code data to the response
575+
w.Header().Set("Content-Type", "image/png")
576+
w.Write(qrCodeData)
577+
}
578+
```
579+
580+
Now update `main.js` to use the `URL` method in place of an hardcoded path:
581+
582+
```js title="frontend/src/main.js" ins={1,11-12}
583+
import { QRService } from "./bindings/changeme";
584+
585+
async function generateQR() {
586+
const text = document.getElementById('text').value;
587+
if (!text) {
588+
alert('Please enter some text');
589+
return;
590+
}
591+
592+
const img = document.getElementById('qrcode');
593+
// Invoke the URL method to obtain an URL for the given text.
594+
img.src = await QRService.URL(text, 256);
595+
}
596+
597+
export function initializeQRGenerator() {
598+
const button = document.getElementById('generateButton');
599+
if (button) {
600+
button.addEventListener('click', generateQR);
601+
} else {
602+
console.error('Generate button not found');
603+
}
604+
}
605+
```
606+
607+
It should work just like the previous example,
608+
but changing the service route in `main.go`
609+
will not break the frontend anymore.
610+
611+
:::note
612+
If a Go method returns a non-nil error,
613+
the promise on the JS side will reject
614+
and await statements will throw an exception.
615+
:::
616+
<br/>
617+
443618
</Steps>

0 commit comments

Comments
 (0)