HTML5-Tutorium: JavaScript: Hello World Vue 05: Unterschied zwischen den Versionen

aus GlossarWiki, der Glossar-Datenbank der Fachhochschule Augsburg
Kowa (Diskussion | Beiträge)
Kowa (Diskussion | Beiträge)
 
Zeile 514: Zeile 514:
                   { lang.value = p_lang ?? config.defaultLanguage;
                   { lang.value = p_lang ?? config.defaultLanguage;
                     await getI18n(lang.value);
                     await getI18n(lang.value);
                    document.getElementsByTagName('html')[0].lang = lang.value;
                   },
                   },


Zeile 529: Zeile 530:


export default storeI18n
export default storeI18n
</source>
<code>changeLang</code> enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei "index.html" einzufügen.
<source lang="ecmascript">
document.getElementsByTagName('html')[0].lang = lang
</source>
Damit wird im Document Tree der Datei "index.html" jeweils die aktuelle Sprache verwendet:
<source lang="html">
<html lang="de">
<html lang="en">
</source>
</source>



Aktuelle Version vom 24. Mai 2024, 13:37 Uhr

Dieser Artikel erfüllt die GlossarWiki-Qualitätsanforderungen nur teilweise:

Korrektheit: 3
(zu größeren Teilen überprüft)
Umfang: 4
(unwichtige Fakten fehlen)
Quellenangaben: 3
(wichtige Quellen vorhanden)
Quellenarten: 5
(ausgezeichnet)
Konformität: 3
(gut)

Vorlesung WebProg

Inhalt | Teil 1 | Teil 2 | Teil 3 | Teil 4 | Teil 5 | Teil 6 | Vue 1 | Vue 2 | Vue 3 | Vue 4 | Vue 5 | Vue 6

Musterlösung
Git-Repository, git checkout v05, ... v05a, ... v05b, ... v05c

Anwendungsfälle (Use Cases)

Gegenüber dem ersten, zweiten und dritten Teil des Vue-Tutoriums ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.

Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.

Aufgabe

Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.

Erstellen eines neuen Projektzweigs

Erstellen Sie einen neuen Projektzweig (branch) innerhalb von hello_world_vue und fügen Sie das Package uuid hinzu:

git checkout v04     # Wechsle in den Branch v04
git checkout -b v05  # Klone v04 in einen neuen Branch v05
npm i

Kofigurationsdateien

Konfigurationsdatei

Die folgende Datei dient zur statischen Konfiguration der Anwendung. Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.

// src/json/config.json

{ "startSection": "form" }

Internationalisierungsdateien

Die folgenden Dateien sollen zunächst statisch eingelesen werden. Sie dienen der Internationalisierung (internationalization = i..................n = i18n).

// src/json/i18n_de.json

{ "phrases":
  { "hello": "Hallo, $1!"
  },

  "dictionary":
  { "stranger":       "Fremder",
    "askName":        "Wie heißen Sie?",
    "welcome":        "Willkommen zu Web-Programmierung!",
    "buttonReset":    "Reset",
    "buttonSayHello": "Begrüßung"
  }
}
// src/json/i18n_en.json

{ "phrases":
  { "hello": "Hello, $1!"
  },

  "dictionary":
  { "stranger":       "Stranger",
    "askName":        "What's your name?",
    "welcome":        "Welcome to Web Programing!",
    "buttonReset":    "reset",
    "buttonSayHello": "say hello"
  }
}

Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.

Statische JSON-Dateien (v05)

Initialisierung von StoreSection.js mit Hilfe einer JSON-Datei

Die JSON-Datei config.json wird von StoreSection.js statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie folgenden Import-Befehl in diese Datei ein:

import config from '@/json/config.json'

Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.

Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.

Nun kann der Name der Start-Section initialisiert werden:

const
  currentSection = ref(config.startSection),
...

Die Konstante "currentSection" enthält das Objekt, das in der JSON-Datei definiert wurde. In diesem Objekt gibt es das Attribut "name", das den Namen der Start-Section enthält.

Die Initialisierung von StoreSection erfolgt bislang in der Datei HelloWorld.vue. Entfernen Sie in dieser Datei den Aufruf Init-Funktion.

Sie können nun auch noch die Funktion "init" aus der Datei StoreSection.js entfernen, da die Initialisierung jetzt über JSON erfolgt.

StoreGreeting.js

Die Webanwendung soll internationalisiert werden. Dazu muss StoreGreeting.js um Internationalisierungstexte erweitert werden.

Die Konstante config wird wie in StoreSectiong.js definiert. In config.i18n sind Internationalisierungstexte enthalten. (Sie wurden in der Datei main.js separat geladen und eingefügt.) Die Datei StoreGreeting.js und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.

Beachten Sie, dass f_hello gegenüber den früheren Implementierungen einen leeren Namen durch den Namen dictionary.stranger ersetzt. (Isabella, danke für den Tipp.)

Beachten Sie außerdem, dass f_hello früher sayHello hieß. Allerdings gibt es in SectionHello.vue eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in StoreGreeting.js umbenannt.

// src/store/StoreGreeting.js

import { defineStore }                       from 'pinia'
import { reactive, ref, computed, readonly } from 'vue'

import config from '@/json/i18n_de.json'

const storeGreeting =
defineStore
( 'greeting',

  () =>
  { const
      phrases =
        reactive(config.phrases),

      dictionary =
        reactive(config.dictionary),

      f_hello =
        p_name => phrases.hello.replace('$1', p_name === '' ? dictionary.stranger : p_name),
      //p_name => phrases?.hello?.replace('$1', p_name === '' ? dictionary.stranger : p_name),


      name = 
        ref(''),

      helloStranger =
        computed(() => f_hello(dictionary.stranger)),

      hello =
        computed(() => f_hello(name.value))/*,

      askName =
        computed(() => dictionary.askName),

      welcome =
        computed(() => dictionary.welcome),

      buttonReset =
        computed(() => dictionary.buttonReset),  

      buttonSayHello =
        computed(() => dictionary.buttonSayHello)
      */

  return { name, helloStranger, hello, dictionary: readonly(dictionary)
           /*, askName, welcome, buttonReset, buttonSayHello */
         }
  }
)

export default storeGreeting

Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.

SectionHello.vue

Die Datei SectionHello.vue muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den StoreGreeting ersetzt werden.

<!-- src/section/SectionHello.vue -->

<script setup>
  import storeGreeting from '@/store/StoreGreeting'
 
  const
    greeting   = storeGreeting(),
    dictionary = greeting.dictionary
</script>

<template>
  <section id="section_hello">
    <h1>{{greeting.hello}}</h1>
    <p>{{dictionary.welcome}}</p>
  </section>
</template>

<style scoped lang="scss">
  @import '/css/section/SectionHello';
</style>

SectionForm.vue

Die Datei SectionHello.vue muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.

<!-- src/section/SectionForm.vue -->

<script setup>
  import FormButton    from '@/components/form/FormButton.vue'
  import FormTextfield from '@/components/form/FormTextfield.vue'

  import storeGreeting from '@/store/StoreGreeting'
  import storeSection  from '@/store/StoreSection'

  const
    section    = storeSection(),
    sayHello   = () => section.currentSection = 'hello',
    
    greeting   = storeGreeting(),
    dictionary = greeting.dictionary
</script>

<template>
  <section>
    <h1>{{greeting.helloStranger}}</h1>
    <form>
      <div>
        <FormTextfield :label="dictionary.askName" autofocus
                       v-model:text="greeting.name" @enter="sayHello"
        />
      </div>
      <div>
        <FormButton :label="dictionary.buttonReset" type="reset" />
        <FormButton :label="dictionary.buttonSayHello" @click="sayHello"/>
      </div>
    </form>
  </section>
</template>

<style scoped lang="scss">
  ...
</style>

Dynamisches Laden von JSON-Dateien (v05a)

Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien. Dazu wird die Version v05 erweitert:

git checkout v05      # Wechsle in den Branch v05
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a

Internationalisierungsdateien

Die folgenden Dateien sollen dynamisch zur Laufzeit eingelesen werden. Beachten Sie, dass sie in den Ordner "public/json" verschoben werden.

// public/json/i18n_de.json

{ "phrases":
  { "hello": "Hallo, $1!"
  },

  "dictionary":
  { "stranger":       "Fremder",
    "askName":        "Wie heißen Sie?",
    "welcome":        "Willkommen zu Web-Programmierung!",
    "buttonReset":    "Reset",
    "buttonSayHello": "Begrüßung"
  }
}
// public/json/i18n_en.json

{ "phrases":
  { "hello": "Hello, $1!"
  },

  "dictionary":
  { "stranger":       "Stranger",
    "askName":        "What's your name?",
    "welcome":        "Welcome to Web Programing!",
    "buttonReset":    "reset",
    "buttonSayHello": "say hello"
  }
}

Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.

getJson.js

Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.

// src/service/getJson.js

/**
 * @function
 * @param  { string } p_url - The URL of a JSON file zo be loaded
 * @return { JSON }   The content of the JSON file
 * @throws { Error }  If the file does not exists, an error is thrown
 */
async function getJson(p_url)
{ return fetch(p_url)
    .then
     (response =>
      { if (response.ok)
        { return response.json() }
        else
        { throw new Error(`'${response.url}' not found`) }
      }
     )
}

export default getJson

Globale Konfigurationsdatei

// public/json/config.json

{ "startSection":    "form",
  "apiRoot":         "/json/i18n_$1.json",
  "XapiRoot":        "/api/$1",
  "defaultLanguage": "de"
}
// src/main.js

import { createPinia } from 'pinia'
import { createApp }   from 'vue'
import getJson         from '@/service/getJson'
import App             from './App.vue'

const
  pinia   = createPinia(),
  app     = createApp(App),
  init    = async () =>
            { const
                config = await getJson('/json/config.json')
        
//            config.i18n =
//              await getJson(config.apiRoot.replace('$1', config.defaultLanguage))
// 
              app
                .provide('config', config)
                .use(pinia)
                .mount('#app') // app is shown to the user
            }

window.addEventListener('load', init)

In der Datei main.js könnte man die I18n-Daten abhängig von der in der Datei /json/config.json festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten getJson-Befehl erfolgen, der ausgeführt wird, nachdem config.json vollständig geladen wurde.

Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von config.json verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren.

StoreSession.js

Wir ersetzen die Datei StoreSection.js durch die Datei StoreSession.js, die alle möglichen Informationen zu aktuellen Session enthält.

  • Name: StoreSection.js => StoreSession.js
  • import config => vue.inject
  • autofocusId enthält das aktuelle Formularfeld, das das der Fokus bei einem Autofokus gesetzt werden soll. Das ist wichtig, weil die Textfelder eine zufällig generierte id erhalten. Der Resetbutton soll beispielsweise nach einem Reset den Fokus auf das Autofokusfeld setzten. Er kennt aber die id nicht, kann sie aber im Session-Store erfagen (sofern das entsprechende Textfeld die id dort hinterlegt).
  • Die größte Änderung ist die Ergänzung einer Initialisierungsfunktionalität.

Die Konstante initializers enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen. Diese werden von anderen Stores mit Hilfe der Funktion addInitializer hinzugefügt. Die Datei App.vue aktiviert die entsprechenden Stores und führt danach die Funktion initialize aus. Mit Hilfe des reaktiven Konstante isInitialized erfährt die App.vue-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.

Man muss auch in allen Dateien, die auf storeSection und StoreSection zugreifen, diese Werte durch storeSession und StoreSession ersezten. Diesen Vorgang nennt man Refactoring. Er wird von modernen IDEs üblicherweise (mal besser. mal schlechter) unterstützt.

  • Session.vue (Nachfolger von StoreSection; siehe nachfolgend)
  • App.vue (neue Version siehe nachfolgend)
  • HelloWorld.vue
  • FormTextField.vue
  • SectionForm.vue
// src/store/StoreSession.js

import { defineStore } from 'pinia'
import { ref, inject } from 'vue'

const storeSession =
defineStore
( 'session',

  () =>
  { const
      config         = inject('config'), // contains the app configuration object
      currentSection = ref(config.startSection),
      
      autofocusId    = ref(null),
      focus          = () => { if (autofocusId.value != null) 
                                 document.getElementById(autofocusId.value).focus()
                             },

      initializers   = [],
      isInitialized  = ref(false),
      addInitializer = p_initializer => 
                       initializers.push(new Promise(resolve => p_initializer(resolve))),
      initialize     = () => Promise.all(initializers)
                                    .then(() => isInitialized.value = true)

    return { currentSection, autofocusId, addInitializer, initialize, isInitialized }
  }
)
<!-- src/App.vue -->

<script setup>
  import HelloWorld   from './components/HelloWorld.vue'
  import storeSession from '@/store/StoreSession'
  import storeI18n    from '@/store/StoreI18n'
  
  const session = storeSession()

  storeI18n(); // activate initialization of storeI18n
  session.addInitializer(p_resolve => setTimeout(p_resolve, 2000));  
              // fake initializer, in order to see the loading information
  session.initialize();
</script>

<template>
  <p          v-if="!session.isInitialized">Loading ...</p>
  <HelloWorld v-if="session.isInitialized"/>
</template>

<style lang="scss">
  @import '/css/body';
</style>

StoreI18n.js

StoreI18n.js übernimmt die I18n-Funktionalität von StoreGreeting.js. Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. Das erhöht die Wiederverwendbarkeit dieses Stores.

Ergänzt wurden getI18n, eine Funktion zum Laden der I18n-Daten von einem Backendsystem, und changeLang, eine Funktion zum asynchronen Ändern der Sprache.

Außerdem gibt es einen Funktion initialize, die die I18n-Daten für die Defaultsprache lädt. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert.

Man bachate, dass im Skript-Bereich des Moduls App.vue der Store mittels storeI18n() erstmals aufgerufen und damit initialisiert wird. Insbesodere wird dabei der Befehl session.addInitializer(initialize) ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.

// src/store/StoreI18n.js

import { defineStore }           from 'pinia'
import { ref, reactive, inject } from 'vue'
import getJson                   from '@/service/getJson'
import storeSession              from '@/store/StoreSession'

const
  storeI18n =
    defineStore
    ( 'i18n',

      () =>
      { const
          config =
            inject('config'),

          session =
           storeSession(),

          lang =
            ref(null),

          phrases /* Record<string, string> */ =
            reactive({}),

          dictionary /* Record<string, string> */=
            reactive({}),

          getI18n =
            async p_lang =>
                  { const
                      c_i18n_json =
                        await getJson(config.apiRoot.replace('$1', p_lang ?? lang.value))
                    Object.assign(phrases,    c_i18n_json.phrases);
                    Object.assign(dictionary, c_i18n_json.dictionary);
                  },

          changeLang =
            async (p_lang = null) =>
                  { lang.value = p_lang ?? config.defaultLanguage;
                    await getI18n(lang.value);
                    document.getElementsByTagName('html')[0].lang = lang.value;
                  },

          initialize = 
             async resolve =>
                   { await changeLang(config.defaultLanguage); 
                     resolve();
                   }

        session.addInitializer(initialize);

        return { lang, phrases, dictionary, changeLang }
      }
    )

export default storeI18n

changeLang enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei "index.html" einzufügen.

document.getElementsByTagName('html')[0].lang = lang

Damit wird im Document Tree der Datei "index.html" jeweils die aktuelle Sprache verwendet:

<html lang="de">
<html lang="en">

StoreGreeting.js

Der StoreGreeting.js von vereinfacht sich entsprechend. Er stellt nur noch Funktionen zur Verfügung, der in der Phrase phrases.hello den String $1 durch einen Namen ersetzt.

// src/store/StoreGreeting.js

import { defineStore }             from 'pinia'
import { ref, computed, readonly } from 'vue'
import StoreI18n                   from './StoreI18n'

const storeGreeting =
defineStore
( 'greeting',
  () =>
  { const
      i18n =
        StoreI18n(),

      phrases =
        i18n.phrases,

      dictionary =
        i18n.dictionary,

      f_hello =
        p_name => phrases.hello.replace('$1', p_name === '' ? dictionary.stranger : p_name),

...

Autofokus-Funktionalität

Damit der Reset-Button den Autofokus nach Löschen der Formularinhalten den Fokus auf das Textfeld setzen kann, müssen die beiden Formular-Komponenten FormTextfield.vue und SectionForm.vue entsprechend angepasst werden.

<!-- src/components/FormTextfield.vue -->
  import { computed, onMounted, onBeforeUnmount } from 'vue'
...
  const
    ...,
    session     = storeSession(),
    ...
    doAutofocus = () => document.getElementById(id).focus(),
    ...
    session     = storeSession(),
    reset       = () => text.value = ''

  onMounted
  ( () => 
    { controllerKey.add();
      if (props.autofocus) 
      { window.addEventListener('focus', doAutofocus);
        doAutofocus();
        // If the surrounding form is reset, delete the text value.
        // This is not done automatically by vue.
        document.getElementById(id).closest('form').addEventListener('reset', reset);
      }
    }
  )

  onBeforeUnmount
  ( () => 
    { if (props.autofocus) 
      { document.getElementById(id).closest('form').removeEventListener('reset', reset);
        window.removeEventListener('focus', doAutofocus); 
     }
      controllerKey.remove()
    }
  )
...


<!-- src/components/SectionForm.vue -->

<template>
...
 <FormButton :label="dictionary.buttonReset" @click="session.focus" type="reset" />
...
</template>

Benutzung eines Backend-Servers (v05b)

Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.

Dazu wird die Version v05 erweitert:

git checkout v05a     # Wechsle in den Branch v05a
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b

Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.

npm i -D json-server

Um ihn mittels "npm run server" zu starten, wird folgendes Skript in die Datei package.json eingefügt.

"server": "npx json-server --watch db.json --port 4000"

JSON-Server-Daten

// db.json

{ "v1":
  [ { "id": "de",

      "phrases":
      { "hello": "Hallo, $1!"
      },

      "dictionary":
      { "stranger":       "Fremder",
        "askName":        "Wie heißen Sie?",
        "welcome":        "Willkommen zu Web-Programmierung!",
        "buttonReset":    "Reset",
        "buttonSayHello": "Begrüßung"
      }
    },

    { "id": "en",

      "phrases":
      { "hello": "Hello, $1!"
      },

      "dictionary":
      { "stranger":       "Stranger",
        "askName":        "What's your name?",
        "welcome":        "Welcome to Web Programing!",
        "buttonReset":    "reset",
        "buttonSayHello": "say hello"
      }
    }
  ]
}

Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei test.rest auf die jeweiligen Send-Request-Anweisungen klicken:

	
npm run server
	
	
// test.rest
	
###
GET http://localhost:4000/ HTTP/1.1
	
###
GET http://localhost:4000/v1/ HTTP/1.1
	
###
GET http://localhost:4000/v1/de HTTP/1.1
	
###
GET http://localhost:4000/v1/en HTTP/1.1 

###
GET http://localhost:4000/v1/en/dictionary HTTP/1.1

###
GET http://localhost:4000/v1/en/phrases HTTP/1.1

Vue-Server als Proxy

// vite.config.js

...

export default defineConfig
({plugins: [vue()],
  resolve:
  { ...
  },
  server:
  { proxy:
    { '/api':
      { target:       'http://localhost:4000/',
        changeOrigin: true,
        rewrite:      path => path.replace(/^\/api/, '/v1')
      }
    }
  },
})

Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:

npm run server  # 1. Terminal
npm run dev     # 2. Terminal

http://localhost:5173/api
http://localhost:5173/api/de
http://localhost:5173/api/en

http://localhost:4000/v1
http://localhost:4000/v1/de
http://localhost:4000/v1/en

Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in test.rest, da Vue keine echte REST-API zur Verfügung stellt.

Zugriff auf den REST-Server

Um auf den neuen REST-Server zuzugreifen, reicht es, die URL in der Datei config.json anzupassen:

// public/json/config.json

{ "startSection":    "form",
  "XapiRoot":        "/json/i18n_$1.json",
  "apiRoot":         "/api/$1",
  "defaultLanguage": "de"
}

Die Dateien public/json/i18n_de.json und public/json/i18n_en.json werden nicht mehr benötigt.

wk_express_hello_world (v00)

mkdir express_hello_world
cd express_hello_world
npm init -y
npm i express dotenv
npm i -D nodemon

# Dateien erstellen/bearbeiten
npm run server

package.json

// package.json

...
"scripts": {
 "server": "nodemon -r dotenv/config src/server.js",
 "prod":   "node    -r dotenv/config src/server.js"
},
"type": "module",
...

src/index.html

<!-- src/index.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport"
          content="width=device-width,
          initial-scale=1,
          shrink-to-fit=no"
    >
    <title>Express Hello World 01</title>
  </head>
  <body>
    <section>
      <h1>Hello, World!</h1>
    </section>
  </body>
</html>

src/server.js

// src/server.js

import express from 'express'
import path    from 'path'

const
  { PORT    = 4000,
    SERVER  = `http://localhost:${PORT}`
  }         = process.env,
  c_app     = express(),
  c_dirname = path.resolve(path.dirname(process.argv[1]))

c_app.get
('/',
 (req, res) =>
 { //console.log(c_dirname);
   res.sendFile('./index.html', {root: c_dirname});
 }
);

c_app.listen(PORT);

console.log(`Running on ${SERVER}`);

wk_express_hello_world (v01)

Kopieren Sie das Array aus der Datei db.json der Version v05a in die Datei src/db.json und ersetzen Sie in dieser Datei zweimal "id" durch "lang. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.

src/server.js

import express, { response } from 'express'
import path    from 'path'

let db;

const
  { PORT    = 4000,
    SERVER  = `http://localhost:${PORT}`,
    DB      = `${SERVER}/db.json`
  }         = process.env ,
  c_app     = express(),
  c_dirname = path.resolve(path.dirname(process.argv[1]))
;

c_app
.get
( '/',
  (req, res) =>
  { res.sendFile('./index.html', {root: c_dirname }); }
)
.get
( '/db.json',
  (req, res) =>
  { res.sendFile('./db.json', {root: c_dirname }); }
)
.get
( '/v1/:lang',
  (req, res) =>
  { //console.log(req.params.lang);
    const l_i18n = db.filter(i18n => i18n.lang === req.params.lang)[0];
    res.json(l_i18n);
  }
)
.get
( '/v1/:lang/:type',
  (req, res) =>
  { //console.log(req.params.lang, req.params.type);
    const l_i18n = db.filter(i18n => i18n.lang === req.params.lang)[0];

    res.json(l_i18n[req.params.type]);
  }
);

async function init()
{ c_app.listen(PORT);
  db = await fetch(DB).then(response => response.json())
  console.log(`Server is running on ${SERVER}`)
}

init()

Benutzung von Strapi als Backend-Servers (v05c)

Erstellen Sie eine Strap-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).

Um darauf zuzugreifen, müssen Sie drei Dateien anpassen:

Konfiguration

// /public/json/config.json: Api-Root

{ "startSection":    "form",
  "XapiRoot":        "/json/i18n_$1.json",
  "apiRoot":         "/api/$1",
  "defaultLanguage": "de"
}

Proxy

// /vite.config.js: Port 1337 und Rewrite-Regel

  ...

  server:
  { proxy:
    { '/api':
      { target:       'http://localhost:1337/',
        changeOrigin: true,
        rewrite:      path => path.replace(/^\/api\//, '/api/i18ns?filters[lang][$eq]=')
      }
    }
  },

Datenextraktion

...
  getI18n =
    async p_lang =>
          { const
              c_api_data = 
                await getJson(config.apiRoot.replace('$1', p_lang ?? lang.value))
              c_i18n_json =
                c_api_data.data[0].attributes;
            Object.assign(phrases,    c_i18n_json.phrases);
            Object.assign(dictionary, c_i18n_json.dictionary);
         },
...

Fortsetzung des Tutoriums

Sie sollten nun Teil 6 des Vue-Tutoriums bearbeiten. In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.

Quellen

  1. Kowarschick (WebProg): Wolfgang Kowarschick; Vorlesung „Web-Programmierung“; Hochschule: Hochschule Augsburg; Adresse: Augsburg; Web-Link; 2024; Quellengüte: 3 (Vorlesung)