HTML5-Tutorium: JavaScript: Hello World Vue 06: Unterschied zwischen den Versionen
Kowa (Diskussion | Beiträge) |
Kowa (Diskussion | Beiträge) |
||
| Zeile 242: | Zeile 242: | ||
git checkout v06 # Wechsle in den Branch v06 | git checkout v06 # Wechsle in den Branch v06 | ||
git checkout -b v06a # Klone v06 in einen neuen Branch v06a | git checkout -b v06a # Klone v06 in einen neuen Branch v06a | ||
</source> | |||
=== <code>config.json</code> === | |||
Um die vorhandenen Sprachen durchblättern zu können, wird ein einfacher Automat verwendet. | |||
<source lang="json"> | |||
// src/public/json/config.json | |||
{ "startSection": "question", | |||
"apiRoot": "/json/i18n_$1.json", | |||
"XapiRoot": "/api/$1", | |||
"defaultLanguage": "en", | |||
"nextLanguage": {"de": "en", "en": "de"} | |||
} | |||
</source> | |||
=== StoreI18n.js: Integration von nextLanguage === | |||
Der Automat <code>nextLanguage</code> wird in den StoreI18n integriert. Wenn die Methode <code>changeLang</code> ohne Argument aufgerufen wird, soll der Automat die aktuelle Sprache <code>lang</code> durch die Nachfolgersprache ersetzen. | |||
Dazu sollte zunächst die zunächst Konstante <code>nextLanguage</code> definiert werden. | |||
<source lang="ecmascript"> | |||
... | |||
lang = | |||
ref(config.defaultLanguage), | |||
nextLanguage = | |||
reactive(config.nextLanguage), | |||
... | |||
</source> | |||
Diese neue Konstante wird verwendet, um die Sprache mittels <code>changeLang</code> zu definieren, sofern | |||
kein Argument übergeben wurde. | |||
<source lang="ecmascript"> | |||
changeLang = | |||
async (p_lang = null) => | |||
{ lang.value = p_lang ?? nextLanguage[lang.value]; | |||
await getI18n(lang.value); | |||
} | |||
</source> | |||
=== Bilder von Flaggen === | |||
Zur Visualisierung der gewählten Sprache werden Bilder von Flaggen eingesetzt. | |||
Wir kopieren die Bilder im SVG-Format aus dem NPM-Paket "https://github.com/lipis/flag-icon-css". | |||
<source lang="bash"> | |||
npm i -D flag-icon-css | |||
</source> | |||
Im Ordner "node_modules/flag-icon-css/flags/4x3" finden Sie Flaggen zahlreicher Länder im 4-zu-3-Format. | |||
Kopieren Sie die Flaggen der Länder Deutschland, Großbritanien und Frankreich in den Ordner <code>public/img</code>. | |||
In die Datei config.js wird der Pfad, unter dem die Bilder zu finden sind, eingefügt. | |||
<source lang="ecmascript"> | |||
// public/config.js | |||
... | |||
"apiRoot": "/json/i18n_$1.json", | |||
"imgPath": "/img/$1.svg", | |||
"XapiRoot": "/api/$1", | |||
"XimgPath": "/api/$1/img", | |||
... | |||
</source> | |||
=== <code>FormButtonIcon</code> === | |||
Eine neue Komponente wird benötigt: FormButtonIcon | |||
Dabei handelt es sich um einen Button, der ein Icon anzeigt. | |||
Das Bild kann zur Laufzeit geändert werden. | |||
<source lang="ecmascript"> | |||
<script setup> | |||
import { computed, inject } from 'vue' | |||
const | |||
config = inject('config'), | |||
props = | |||
defineProps | |||
({ image: { type: String }, | |||
width: { type: String, default: '3em'}, | |||
}), | |||
emit = defineEmits(['click']), | |||
click = () => emit('click'), | |||
require = v_url => import.meta.url == null | |||
? v_url | |||
: new URL(v_url, import.meta.url).href, | |||
imgPath = config.imgPath, | |||
buttonImage = computed(() => require(imgPath.replace('$1', props.image))) | |||
</script> | |||
<template> | |||
<input type="image" :src="buttonImage" :style="{width}" @click="click" /> | |||
</template> | |||
<style scoped lang="scss"> | |||
@import '/css/form/FormButtonIcon.scss'; | |||
</style> | |||
</source> | |||
<source lang="ecmascript"> | |||
/* src/css/FormButtonIcon.scss */ | |||
@import 'FormButton'; | |||
input | |||
{ width: $button_icon_width !important; | |||
margin-left: $nav-dist !important; | |||
} | |||
</source> | |||
=== Erweiterung von <code>AppNav.vue/code> === | |||
Der Sprachwahl-Button muss noch in die Navigation eingebaut werden. | |||
<source lang="html"> | |||
<!-- src/components/AppNav --> | |||
<script setup> | |||
import FormButtonIcon from '@/components/form/FormButtonIcon.vue' | |||
import storeI18n from '@/store/StoreI18n' | |||
const i18n = storeI18n() | |||
</script> | |||
<template> | |||
<nav> | |||
<ul> | |||
<li><router-link to="/">Home</router-link></li> | |||
<li><router-link to="/hello">Hello</router-link></li> | |||
<li><FormButtonIcon :image="i18n.lang" @click="i18n.changeLang()"/></li> | |||
</ul> | |||
</nav> | |||
</template> | |||
<style scoped lang="scss"> | |||
@import '/css/AppNav'; | |||
</style> | |||
</source> | |||
<source lang="ecmascript"> | |||
</source> | |||
<source lang="ecmascript"> | |||
</source> | |||
<source lang="ecmascript"> | |||
</source> | |||
<source lang="ecmascript"> | |||
</source> | </source> | ||
Version vom 16. Mai 2023, 10: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) |
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 v06, git checkout v06a, git checkout v06b (JavaScript)
Git-Repository (TypeScript), git checkout v06
Anwendungsfälle (Use Cases)
Die Version des fünften Teils des Vue-Tutoriums wird so geändert, dass die Sprache zur Laufzeit geändert werden kann.
Zunächst wird jedoch eine Navigationskomponente eingeführt. Dies hat den Zweck, das Routing an einem Beispiel zu erklären.
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 v05b # Wechsle in den Branch v05b
git checkout -b v06 # Klone v05b in einen neuen Branch v06
npm i
Views
Anstelle von Sections werden Views verwendet. Dazu werden die Section-Komponenten in den Ordner View verschoben und umbenannt:
src/view/ViewHello.vue
src/view/ViewHome.vue // an Stelle von ViewQuestion.vue
src/view/ViewError404.vue // neu
ViewQuestion.vue wird in ViewHome.vue unbenannt, da diese Seite als Startseite verwendet wird.
Zusätzlich wird eine Seite Vue404.vue angelegt, die immer dann ausgeliefert wird, wenn der Benutzer auf eine URL zugreift, der keine View-Datei zugeordnet ist.
Die SCSS-Dateien sollten analog umbenannt und unter den neuen Namen importiert werden. Für ViewError404.vue muss natürlich auch eine SCSS-Datei angelegt werden. Diese verweist allerdings nur auf _View.scss.
<!-- src/view/ViewError404.vue -->
<script setup>
import router from '@/router'
import storeI18n from '@/store/StoreI18n'
const phrases = storeI18n().phrases
</script>
<template>
<h1>404</h1>
<p>{{phrases.pageNotFound.replace('$1', router.currentRoute.value.path)}}</p>
</template>
<style scoped lang="scss">
@import '/css/view/ViewError404';
</style>
Router-Plugin
Um mit Vue Multipage-Anwendungen realisieren zu können, benötigt man das Router-Plugin.
// src/main.js
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import getJson from '@/service/getJson'
import router from './router'
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)
.use(router)
.mount('#app') // app is shown to the user
}
window.addEventListener('load', init)
Dieses muss jedoch händisch implementiert werden.
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import ViewHome from '@/view/ViewHome.vue'
const
routes =
[ { path: '/',
name: 'home',
component: ViewHome,
},
{ path: '/hello',
name: 'hello',
component: () => import('@/view/ViewHello.vue'),
},
{ path: '/:pathMatch(.*)',
component: () => import('@/view/ViewError404.vue'),
}
],
router =
createRouter({ history: createWebHistory(), routes })
export default router
App.vue
Nun kann in die App eine Navigation eingebaut werden.
<!-- /src/App.vue -->
<script setup>
import AppNav from '@/components/AppNav.vue'
</script>
<template>
<AppNav/>
<main>
<router-view/>
</main>
</template>
<style lang="scss">
@import '/css/body';
</style>
Die zugehörige Navigationskomponente muss auch noch implementiert werden.
<!-- /src/components/AppNav.vue -->
<script setup>
</script>
<template>
<nav>
<ul>
<li><router-link to="/">Home</router-link></li>
<li><router-link to="/hello">Hello</router-link></li>
</ul>
</nav>
</template>
<style scoped lang="scss">
@import '/css/AppNav';
</style>
/* src/css/AppNav.scss */
@import 'config';
nav
{ top: 0;
width: 100%;
position: fixed;
ul
{ list-style-type: none;
margin: 0 $nav-dist;
padding: 0;
li
{ display: inline;
a { padding: 0 $nav-dist 0.2em; }
}
a
{ text-decoration: none;
color: $link-color;
font-weight: bold;
}
a:hover, .router-link-exact-active
{ text-decoration: underline;
color: $link-color;
font-weight: bold;
}
}
label, input
{ margin: 0;
}
}
Die Navigation muss in allen Komponenten, in den Seiten gewechselt werden, angepasst werden.
Das ist in der App.vue der Fall (siehe zuvor) sowie in der ViewHome.vue.
<!-- /src/App.vue -->
<script setup>
import FormButton from '@/component/form/FormButton.vue'
import FromTextfield from '@/component/form/FormTextfield.vue'
import storeGreeting from '@/store/StoreGreeting'
import router from '@/router'
const
greeting = storeGreeting(),
i18n = greeting.i18n,
sayHello = () => router.push('/hello')
</script>
Hello World Vue 06a
Aufgabe: Die Ausgabesprache soll vom Benutzer zur Laufzeit geändert werden können. Die Sprachen sollen durch Bilder (Flaggen) symbolisiert werden.
git checkout v06 # Wechsle in den Branch v06
git checkout -b v06a # Klone v06 in einen neuen Branch v06a
config.json
Um die vorhandenen Sprachen durchblättern zu können, wird ein einfacher Automat verwendet.
// src/public/json/config.json
{ "startSection": "question",
"apiRoot": "/json/i18n_$1.json",
"XapiRoot": "/api/$1",
"defaultLanguage": "en",
"nextLanguage": {"de": "en", "en": "de"}
}
StoreI18n.js: Integration von nextLanguage
Der Automat nextLanguage wird in den StoreI18n integriert. Wenn die Methode changeLang ohne Argument aufgerufen wird, soll der Automat die aktuelle Sprache lang durch die Nachfolgersprache ersetzen.
Dazu sollte zunächst die zunächst Konstante nextLanguage definiert werden.
...
lang =
ref(config.defaultLanguage),
nextLanguage =
reactive(config.nextLanguage),
...
Diese neue Konstante wird verwendet, um die Sprache mittels changeLang zu definieren, sofern
kein Argument übergeben wurde.
changeLang =
async (p_lang = null) =>
{ lang.value = p_lang ?? nextLanguage[lang.value];
await getI18n(lang.value);
}
Bilder von Flaggen
Zur Visualisierung der gewählten Sprache werden Bilder von Flaggen eingesetzt. Wir kopieren die Bilder im SVG-Format aus dem NPM-Paket "https://github.com/lipis/flag-icon-css".
npm i -D flag-icon-css
Im Ordner "node_modules/flag-icon-css/flags/4x3" finden Sie Flaggen zahlreicher Länder im 4-zu-3-Format.
Kopieren Sie die Flaggen der Länder Deutschland, Großbritanien und Frankreich in den Ordner public/img.
In die Datei config.js wird der Pfad, unter dem die Bilder zu finden sind, eingefügt.
// public/config.js
...
"apiRoot": "/json/i18n_$1.json",
"imgPath": "/img/$1.svg",
"XapiRoot": "/api/$1",
"XimgPath": "/api/$1/img",
...
FormButtonIcon
Eine neue Komponente wird benötigt: FormButtonIcon Dabei handelt es sich um einen Button, der ein Icon anzeigt. Das Bild kann zur Laufzeit geändert werden.
<script setup>
import { computed, inject } from 'vue'
const
config = inject('config'),
props =
defineProps
({ image: { type: String },
width: { type: String, default: '3em'},
}),
emit = defineEmits(['click']),
click = () => emit('click'),
require = v_url => import.meta.url == null
? v_url
: new URL(v_url, import.meta.url).href,
imgPath = config.imgPath,
buttonImage = computed(() => require(imgPath.replace('$1', props.image)))
</script>
<template>
<input type="image" :src="buttonImage" :style="{width}" @click="click" />
</template>
<style scoped lang="scss">
@import '/css/form/FormButtonIcon.scss';
</style>
/* src/css/FormButtonIcon.scss */
@import 'FormButton';
input
{ width: $button_icon_width !important;
margin-left: $nav-dist !important;
}
Der Sprachwahl-Button muss noch in die Navigation eingebaut werden.
<!-- src/components/AppNav -->
<script setup>
import FormButtonIcon from '@/components/form/FormButtonIcon.vue'
import storeI18n from '@/store/StoreI18n'
const i18n = storeI18n()
</script>
<template>
<nav>
<ul>
<li><router-link to="/">Home</router-link></li>
<li><router-link to="/hello">Hello</router-link></li>
<li><FormButtonIcon :image="i18n.lang" @click="i18n.changeLang()"/></li>
</ul>
</nav>
</template>
<style scoped lang="scss">
@import '/css/AppNav';
</style>
Quellen
- Kowarschick (WebProg): Wolfgang Kowarschick; Vorlesung „Web-Programmierung“; Hochschule: Hochschule Augsburg; Adresse: Augsburg; Web-Link; 2024; Quellengüte: 3 (Vorlesung)
