@@ -40,8 +40,8 @@ This will show you how to organize your code into reusable services and handle e
40
40
return &QRService{}
41
41
}
42
42
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) {
45
45
// Generate the QR code
46
46
qr , err := qrcode.New (text, qrcode.Medium )
47
47
if err != nil {
@@ -149,21 +149,21 @@ This will show you how to organize your code into reusable services and handle e
149
149
// This file is automatically generated. DO NOT EDIT
150
150
151
151
/**
152
- * QRService handles QR code generation
153
- * @module
154
- */
152
+ * QRService handles QR code generation
153
+ * @module
154
+ */
155
155
156
156
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
157
157
// @ts-ignore: Unused imports
158
158
import {Call as $Call , Create as $Create } from " @wailsio/runtime" ;
159
159
160
160
/**
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 ) {
167
167
let $resultPromise = /** @type {any} */ ($Call .ByID (3576998831 , text, size));
168
168
let $typingPromise = /** @type {any} */ ($resultPromise .then (($result ) => {
169
169
return $Create .ByteSlice ($result);
@@ -173,7 +173,7 @@ This will show you how to organize your code into reusable services and handle e
173
173
}
174
174
```
175
175
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,
177
177
as well as the comments. JSDoc has also been generated for the method to provide type information to your IDE.
178
178
179
179
:::note
@@ -190,14 +190,36 @@ This will show you how to organize your code into reusable services and handle e
190
190
The bindings generator also supports generating Typescript bindings. You can do this by running ` wails3 generate bindings -ts ` .
191
191
:::
192
192
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 />
194
216
195
217
6 . ## Use Bindings in Frontend
196
218
197
219
Firstly, update ` frontend/src/main.js ` to use the new bindings:
198
220
199
221
``` js title="frontend/src/main.js"
200
- import { GenerateQRCode } from ' ./bindings/changeme/qrservice.js ' ;
222
+ import { QRService } from ' ./bindings/changeme' ;
201
223
202
224
async function generateQR () {
203
225
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
208
230
209
231
try {
210
232
// Generate QR code as base64
211
- const qrCodeBase64 = await GenerateQRCode (text, 256 );
233
+ const qrCodeBase64 = await QRService . Generate (text, 256 );
212
234
213
235
// Display the QR code
214
236
const qrDiv = document .getElementById (' qrcode' );
@@ -285,7 +307,7 @@ This will show you how to organize your code into reusable services and handle e
285
307
286
308
Type in some text and click the "Generate QR Code" button. You should see a QR code in the center of the page:
287
309
288
- <Image src = { qr1 } alt = " QR Code" />
310
+ <Image src = { qr1 } alt = " QR Code" />
289
311
290
312
<br />
291
313
<br />
@@ -303,13 +325,14 @@ This will show you how to organize your code into reusable services and handle e
303
325
If your service defines Go's standard http handler function ` ServeHTTP(w http.ResponseWriter, r *http.Request) ` ,
304
326
then it can be made accessible on the frontend. Let's extend our QR code service to do this:
305
327
306
- ``` go title="qrservice.go" ins={5-6,36-63 }
328
+ ``` go title="qrservice.go" ins={4-5,37-65 }
307
329
package main
308
330
309
331
import (
310
- " github.com/skip2/go-qrcode"
311
332
" net/http"
312
333
" strconv"
334
+
335
+ " github.com/skip2/go-qrcode"
313
336
)
314
337
315
338
// QRService handles QR code generation
@@ -322,8 +345,8 @@ This will show you how to organize your code into reusable services and handle e
322
345
return &QRService{}
323
346
}
324
347
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) {
327
350
// Generate the QR code
328
351
qr , err := qrcode.New (text, qrcode.Medium )
329
352
if err != nil {
@@ -358,7 +381,7 @@ This will show you how to organize your code into reusable services and handle e
358
381
}
359
382
360
383
// Generate the QR code
361
- qrCodeData , err := s.GenerateQRCode (text, size)
384
+ qrCodeData , err := s.Generate (text, size)
362
385
if err != nil {
363
386
http.Error (w, err.Error (), http.StatusInternalServerError )
364
387
return
@@ -408,11 +431,14 @@ This will show you how to organize your code into reusable services and handle e
408
431
}
409
432
```
410
433
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
+
411
439
Finally, update ` main.js ` to make the image source the path to the QR code service, passing the text as a query parameter:
412
440
413
441
``` js title="frontend/src/main.js"
414
- import { GenerateQRCode } from ' ./bindings/changeme/qrservice.js' ;
415
-
416
442
async function generateQR () {
417
443
const text = document .getElementById (' text' ).value ;
418
444
if (! text) {
@@ -422,7 +448,7 @@ This will show you how to organize your code into reusable services and handle e
422
448
423
449
const img = document .getElementById (' qrcode' );
424
450
// 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) } `
426
452
}
427
453
428
454
export function initializeQRGenerator () {
@@ -440,4 +466,153 @@ This will show you how to organize your code into reusable services and handle e
440
466
<Image src = { qr1 } alt = " QR Code" />
441
467
<br />
442
468
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
+
443
618
</Steps >
0 commit comments