Fehlersuche Flask, mit Vue.js

Django, Flask, Bottle, WSGI, CGI…
Benutzeravatar
Dennis89
User
Beiträge: 1185
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo zusammen,

ich benötige wieder eure Hilfe.
Ich habe ein Tutorial zu Vue.js durchgearbeitet, weil die damit erstellte Webseite nahe an dem ist, was ich brauche um einen Python-Code zu testen.

So jetzt hatte ich schon einen seitenlangen Post geschrieben, bis mir etwas im Terminal aufgefallen ist, das ich nicht verstehe. Eventuell löst das mein Problem schon, allerdings ist das keine Python-Frage (Die kommt danach vielleicht).

Ich habe einen Button:

Code: Alles auswählen

<button
              type="button"
              class="btn btn-primary btn-sm"
              @click="handleEditSubmit">
              Submit
            </button>
`handleEditSubmit` sieht dann so aus:

Code: Alles auswählen

    handleEditSubmit() {
      this.toggleEditBookModal(null);
      const payload = {
        author: this.editBookForm.author,
      };
      this.updateBook(payload, this.editBookForm.id);
    },
    updateBook(payload, bookID) {
      const path = `http://localhost:5000/books/${bookID}`;
      axios.put(path, payload)
        .then(() => {
          this.message = "Aktualisiert"
          this.showMessage = true;
        })
        .catch((error) => {
          console.error(error);
        });
    },
Ich erwarte jetzt das gar nichts sichtbares passiert. Es wird die Python-Funktion aufgerufen die an "/books/<book_id>" adressiert ist:

Code: Alles auswählen

@route("/books/<book_id>", methods=["PUT"])
    def update(self, book_id):
        if request.method == "PUT":
            self.process_user_data(request.get_json(), "update", book_id)
        return jsonify(self.books)
Und trotzdem bekomme ich im Terminal von Flask, nach dem bestätigen des "Update"-Buttons, diese Ausgabe:

Code: Alles auswählen

127.0.0.1 - - [28/Apr/2024 22:05:32] "OPTIONS /books/ HTTP/1.1" 404 -
Wer ruft "/books" auf?
Ich glaube, dass der Aufruf mein geplanten Ablauf stört. Ich habe im gesamten "<script>" - Bereich nur zwei Funktionen 'addBook' und 'getBooks' die diese Adresse aufrufen, aber wie man sieht, werden die in `handleEditSubmit' und auch in 'updateBook' nicht aufgerufen.

Ich weis auch nicht, was "OPTIONS" zu bedeuten hat. Bis jetzt hatte ich halt "GET", "PUT" und "POST".

Vielen Dank schon mal für eure Hilfe.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Sirius3
User
Beiträge: 17782
Registriert: Sonntag 21. Oktober 2012, 17:20

Das macht Dein Browser automatisch: https://developer.mozilla.org/en-US/doc ... ts_in_cors
Benutzeravatar
Dennis89
User
Beiträge: 1185
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Morgen und vielen Dank.

Dass war zwar nicht das Problem, aber ich konnte mit `console.log` und der Browser-Console mein Problem finden und "lösen". Die Lösung gefällt mir allerdings nicht und ich gehe davon aus, dass man das anders macht.

In dem erwähnten Tutorial verwendet man eine globale Liste um Bücher abzulegen, ändern oder zu löschen. Mein eigentlicher Python-Code, denn ich später laufen lassen will, hat eine ähnliche Datenstruktur. Ich brauche die Möglichkeit, dass der User soviele Datensätze wie er will anlegen kann und diese auch bearbeiten kann.
Damit ich keinen globalen Zustand habe, habe ich Flask-Classful entdeckt.
Ich habe zwei Python-Funktionen, eine nimmt Daten an, verarbeitet die und gibt eine Datenstruktur zurück. Die zweite Funktion ändert die gewünschte Datenstruktur und in meiner "Lösung" gibt die Funktion, die geänderte Datenstruktur zurück.

JavaScript-seitig sieht der Funktionsaufruf jetzt so aus:
Buch hinzufügen:
`handleAddSubmit` -> `addBook` -> `getBooks`

Buch updaten:
`handleEditSubmit` -> `updateBook` -> `getUpdatedBooks`

`getBooks` und `getUpdatedBooks` sind eigentlich gleich, nur einmal wird "/books/<book_id>" und einmal "/books" aufgerufen. Eigentlich dachte ich, dass ich beides mal die Daten über "/books" zurück geben kann, aber obwohl beide Funktionen `self.books' zurückgeben, sind das nicht die gleichen Objekte. Wieso?
Ich habe mir den Inhalt und die `id()` ausgeben lassen. Wird da pro `rule`eine neue Instanz der Klasse erstellt?

Code: Alles auswählen

#!/usr/bin/env python
from flask import Flask, jsonify, request
from flask_cors import CORS
from flask_classful import FlaskView, route
from uuid import uuid4


class UserInterface(FlaskView):
    def __init__(self, args):
        description_to_value, books = args
        self.description_to_value = description_to_value
        self.books = books

    def convert_input(self, user_input):
        self.description_to_value = {"id": uuid4()}
        for description, value in user_input.items():
            try:
                self.description_to_value[description] = float(value.replace(",", "."))
            except ValueError:
                # TODO show error to user and wrong value
                return False
        return True

    def update_book(self, book_id):
        updated_books = {"status": "success", "books": []}
        for book in self.books["books"]:
            if str(book["id"]) == book_id:
                updated_books["books"].append(self.description_to_value)
            else:
                updated_books["books"].append(book)
        return updated_books

    def process_user_data(self, user_input, action, book_id=None):
        if self.convert_input(user_input):
            if action == "add":
                self.books["books"].append(self.description_to_value)
                self.description_to_value = {"status": "success", "books": []}
            elif action == "update":
                self.books = self.update_book(book_id)

    @route("/books/<book_id>", methods=["PUT", "GET"])
    def update(self, book_id):
        if request.method == "PUT":
            self.process_user_data(request.get_json(), "update", book_id)
        elif request.method == "GET":
            return jsonify(self.books)
        return jsonify(self.books)

    @route("/books", methods=["GET", "POST"])
    def index(self):
        if request.method == "POST":
            self.process_user_data(request.get_json(), "add")
        elif request.method == "GET":
            return jsonify(self.books)
        return jsonify(self.books)


def main():
    app = Flask(__name__)
    app.config.from_object(__name__)
    CORS(app, resources={r"/*": {"origins": "*"}})
    UserInterface.register(
        app, route_base="/", init_argument=[{}, {"status": "success", "books": []}]
    )
    app.run()


if __name__ == "__main__":
    main()

Ich hoffe ich konnte einigermaßen verständlich erklären, was mein Problem ist. Im Prinzip erwarte ich, das `index` (dummer Name) das gleiche `self.books` zurück gibt, wie `update`.

Vielen Dank und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1185
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

ich bin noch auf der Suche nach einer Lösung und habe folgendes gefunden:
https://stackoverflow.com/a/56106835/19088872

Sieht das nach einem üblichen Weg aus?

In der Doku zu Flask, habe ich folgendes über Klassen mit einem `View`-Objekt gefunden:
https://flask.palletsprojects.com/en/2. ... e-and-self

Hier wird bei jedem Aufruf eine neue Instanz erstellt, es gibt aber auch eine Methode um das abzustellen. In der Doku zu `Flask-Classful` habe ich nichts vergleichbares gefunden.

Die Klasse mit Flask und dem `View`-Objekt zu machen, sieht irgendwie nicht so schön aus/einfach aus im Vergleich mit `Flask-Classful`.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
__blackjack__
User
Beiträge: 13168
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Dennis89: Das ist alles etwas komisch weil das WSGI zuwider läuft. Man kann normalerweise nicht davon ausgehen, dass jede Anfrage vom gleichen Thread beantwortet wird, im Grund nicht einmal vom gleichen Prozess. Web- und/oder WSGI-Anwendungsserver können und haben in der Regel mehrere Threads und/oder Prozesse die Anfragen beantworten, und darum kann man nicht einfach *ein* Objekt erstellen und da globalen Zustand drin aufheben, denn dieses Objekt kann es mehrfach geben ohne das man das weiss oder da dran kommt.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
Benutzeravatar
Dennis89
User
Beiträge: 1185
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für deine Antwort.
Web- und/oder WSGI-Anwendungsserver können und haben in der Regel mehrere Threads und/oder Prozesse die Anfragen beantworten
Das wusste ich nicht, erklärt aber natürlich einiges.

Ich will dem Benutzer ja ermöglichen beliebig viele Datensätze anzulegen (Ein Satz besteht aus 10 Variablen), diese nochmals zu bearbeiten oder auch wieder zu löschen und wenn er zufrieden ist, dann wird mit einem Button eine Berechnung eingeleitet.

Wie merke ich mir die Datensätze dann? Lasse ich das den Webserver machen? Also der Benutzer gibt Daten ein, Python validiert diese und gibt sie zurück und sie werden auf der Webseite angezeigt. Wenn die nächsten Daten eingegeben werden, dann müssen die Daten, die schon auf der Webseite stehen plus die neu eingetragenen Daten wieder an Python geschickt werden und so weiter?
Wäre dass die richtige Vorgehensweise?
Eine Datenbank wäre vermutlich etwas zu überdimensioniert?

Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
sparrow
User
Beiträge: 4221
Registriert: Freitag 17. April 2009, 10:28

Warum willst du die überhaupt zum Webserver schicken, bevor sie fertig eingegeben sind?

Ich kenne vue.js nicht sondern React - und da ist es kein Problem den State im Frontend zu halten und auf Knopfdruck an das Backend zu senden.
Sirius3
User
Beiträge: 17782
Registriert: Sonntag 21. Oktober 2012, 17:20

Ich verstehe nicht, wo das Problem liegt, das Editieren von den 10 Variablen komplett im Browser zu erledigen. Warum sollten unfertige Daten an den Server geschickt werden?
Vielleicht solltest Du etwas weiter ausholen, was Du eigentlich machen möchtest.
Benutzeravatar
Dennis89
User
Beiträge: 1185
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Morgen und Danke für die Antworten.

Letztendlich werden die Datensätze benötigt um Maschinenkomponenten zu berechnen, je nach Anzahl der eingegebenen Datensätze gibt es von diesen Komponenten entsprechend viele.
Mein Gedanke war folgender, ich drücke einen "Hinzufügen" - Button, es öffnet sich ein Eingabefenster, die Werte werden eingetragen und mit einem Bestätigen-Button wird kontrolliert ob alle Felder ausgefüllt werden, ob es Zahlen sind und ob diese im vorgegebenen Bereich liegen. Wenn das passt, werden die Daten in einer Tabellenform dargestellt, wenn nicht, soll eine Meldung kommen. Wenn man bemerkt, dass man sich vertippt hat, soll man die Möglichkeit haben, den Datensatz zu bearbeiten. Durch den "Edit"-Button öffnet sich wieder ein (bis jetzt noch) leeres Fenster und der Datensatz kann noch mal neu eingegeben werden und dann muss ich wieder prüfen, ob die Eingaben passen.

Das sieht wie in diesem Tutorial aus, nur mit mehr Werten. Wenn ihr den Link anklickt, einfach etwas nach unten Scrollen, nach den Codes ist eine kleine Animation, die das bearbeiten zeigt.

Das validieren der Daten, würde ich gerne in Python machen. Wenn das Projekt gut und erfolgreich wird, dann wird die Webseite vermutlich von jemanden aufgebaut, der das kann und für alle Sonderwünsche nicht erst eine Woche recherchieren muss und auch den Server betreut. Aber für die Berechnungslogik an sich bin ich verantwortlich und daher hätte ich auch die Überprüfung der Eingabedaten in Python. Die Webseite jetzt dient dazu, um das Projekt etwas visuell vorstellen zu können und ich hatte endlich mal einen Grund etwas mit Vue.js zu machen. Im Prinzip könnte ich eine rein statische Webseite für den Zweck machen, aber ich will ja immer etwas dazu lernen und das mit Vue.js gefällt mir. Falls ich aber eher oder einfacher oder besser mit React ans Ziel komme, dann kann ich auch noch ein mal umschwenken.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Sirius3
User
Beiträge: 17782
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn Du mit einem dieser Frameworks arbeitest (und da ist jedes so gut wie das andere) dann mußt Du auch Javascript-Code schreiben. Der Vorteil, das alles im Browser zu machen ist, dass Du kein Session-Management brauchst.
Üblich ist, sowohl die Validierung in Javascript zu schreiben, um dem Nutzer sofortiges Feedback zu geben, als auch später in Python, wenn Du dann Deine Berechnung startest.
Natürlich kannst Du auch alles in Python programmieren, dann halt mit einer Datenbank und so weiter.
Die Tutorials beschränken sich halt oft auf einen Aspekt, in Deinem Fall, wie man eine Vue-Oberfläche mit dem Backend verbindet. Dabei macht das Pythonprogramm an sich nicht sinnvolles.
Benutzeravatar
grubenfox
User
Beiträge: 435
Registriert: Freitag 2. Dezember 2022, 15:49

Dennis89 hat geschrieben: Mittwoch 1. Mai 2024, 07:58 Mein Gedanke war folgender, ich drücke einen "Hinzufügen" - Button, es öffnet sich ein Eingabefenster, die Werte werden eingetragen und mit einem Bestätigen-Button wird kontrolliert ob alle Felder ausgefüllt werden, ob es Zahlen sind und ob diese im vorgegebenen Bereich liegen. Wenn das passt, werden die Daten in einer Tabellenform dargestellt, wenn nicht, soll eine Meldung kommen. Wenn man bemerkt, dass man sich vertippt hat, soll man die Möglichkeit haben, den Datensatz zu bearbeiten. Durch den "Edit"-Button öffnet sich wieder ein (bis jetzt noch) leeres Fenster und der Datensatz kann noch mal neu eingegeben werden und dann muss ich wieder prüfen, ob die Eingaben passen.
in der Beschreibung ist mir irgendwie ein Fenster zuviel... Es gibt ein Eingabefenster, mit mehreren Eingabefelder die entweder leer sind (da werden dann die Werte eingetragen und der Bestätigen-Button kann gedrückt werden) oder die Felder enhalten schon die (fehlerhaften, weil vertippt,) Werte die zuvor eingegeben wurden und diese können dann bearbeitet werden.

Ein (bis jetzt noch) leeres Fenster und eine komplette Neueingabe des Datensatzes macht weder Spaß noch Sinn für mich.
Benutzeravatar
sparrow
User
Beiträge: 4221
Registriert: Freitag 17. April 2009, 10:28

@grubenfox: Häh?

@Dennis89: Wie gesagt, ich habe keine Ahnung von vue, sehe hier aber gar kein Problem. In Django mit dem Restframework würde man wohl einen Serializer im Backend verwenden. Die Komponente, in der du die Daten eingibst, validiert sich selbst. Beim Absenden validiert zusätzlich das Backend. Wenn etwas nicht passt, musst du entsprechend darauf reagieren und ggf. die Fehler ausgeben oder sogar den Feldern zuordnen.
Funktioniert das Speichern und das Backend meldet einen Status 2xx, dann kannst du entweder die komplette Tabellendaten erneut vom Server holen (da sollte der neue Datensatz dann ja enthalten sein) oder du nimmst den Datensatz und packst ihn in Javascript unter die Tabelle.

Das ist alles, wie Sirius3 schon schrieb, JavaScript-Kram. Keine Ahnung ob vue da irgendwelche Magie abbildet.
Und du _musst_ die Validierung sogar im Backend machen. Aber im Zweifelsfall musst du die _auch_ im Frontend machen.

Auch eine Möglichkeit: Jeweils eine nach dem Verlassen Feldes den aktuellen Stand ans Backend senden und dann die Antwort auswerten. Ist mehr Last für das Backend und macht eigentlich nur Sinn, wenn die Validierungsregeln so komplex sind, dass man sie nicht 2 Mal abbilden möchte (und dafür die Last in Kauf nimmt).
Und das erfordert auch im Frontend einen deutlichen Mehraufwand (man möchte ja keine Fehler dort anzeigen, wo man noch gar nicht versucht hat etwas einzugeben).
Benutzeravatar
Dennis89
User
Beiträge: 1185
Registriert: Freitag 11. Dezember 2020, 15:13

Vielen Dank für die schnellen Antworten.
Der Vorteil, das alles im Browser zu machen ist, dass Du kein Session-Management brauchst.
Das ist natürlich auch ein sehr guter Punkt.

Dann gehe ich jetzt so vor, das ich schaue wie ich das Hinzufügen und Editieren von Daten in JavaScript hinbekomme. Die Validierung überlasse ich dann demjenigen der die Webseite richtig umsetzt, falls es dazu kommt. Die Regeln kann er ja dann aus meiner Python-Validierung ableiten.
Und wer weis, über was ich dann so stolpere, vielleicht ist die Validierung in JavaScript gar nicht so schwer, dann versuche ich es natürlich mal. Habe zum Glück kein zu großer Zeitdruck.

@grubenfox Du hast recht, deswegen habe ich das "bis jetzt noch" geschrieben. Wenn man "bearbeiten" anwählt, dann sollen natürlich die Daten schon dastehen, so das man nur den falschen Wert ändern muss. Da bin ich deiner Meinung.


Gerade beim absenden, den Beitrag von @sparrow erst gesehen.
In Django mit dem Restframework würde man wohl einen Serializer im Backend verwenden
Werde ich mir auf jeden Fall auch mal anschauen.

Ob und was Vue.js da bietet, weis ich so auch nicht. Die haben auch ein eigenes Tutorial, das habe ich vor längerem mal gestartet, aber ohne wirklichen Anwendungszweck, war die Motivation leider nicht so hoch.
Deine zweite Möglichkeit wird wohl nicht sinnvoll sein, den die Validierung ist nicht komplex. Es müssen in erster Linie Zahlen sein und manche dürfen nicht negativ sein oder sich in einem bestimmten Bereich befinden. Zum Beispiel kann ich nicht mit einer Temperatur von -300°C rechnen.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Sirius3
User
Beiträge: 17782
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie schon geschrieben, ist eine Validierung trivial. Natürlich könnte man noch ein Framework einbinden, dass dann wieder mit neuer Syntax und einer ganz komplizierten Logik im Hintergrund ganze Formulare validiert.
Ich für meinen Teil habe gerne möglichst wenig Zwischenschichten und nur die Frameworks, die wirklich einen Mehrwert bieten.

Code: Alles auswählen

<html>
<head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<template v-if="is_edit_mode">
  Name: <input v-model="model_name"><br/>
  Temperatur: <input v-model="model_temperature"><span v-if="!is_temperature_valid" style="color:red">Temperature not between -20°C and 50°C</span><br/>
  <button @click="save" :disabled="!is_temperature_valid">save</button>
  <button @click="is_edit_mode = false">cancel</button>
</template>
<template v-else>
  <table>
    <thead><tr><th/><th>Name</th><th>Temperatur</th></tr></thead>
    <tbody>
      <tr v-for="(model, index) in models" :key="model.name">
        <td><button @click="edit(index)">edit</button></td><td>{{model.name}}</td><td>{{model.temperature}}°C</td>
      </tr>
    </tbody></table>
    <button @click="add">add model</button>
</template>
</div>

<script>
  const { createApp, ref, computed } = Vue

  createApp({
    setup() {
      const models = ref([])
      const is_edit_mode = ref(false)
      let model_index = undefined
      const model_name = ref("")
      const model_temperature = ref(0)
      function add() {
          model_index = undefined
          model_name.value = ""
          model_temperature.value = 0
          is_edit_mode.value = true
      }
      function save() {
          const item = {name: model_name.value, temperature: model_temperature.value};
          if(model_index === undefined) {
            models.value.push(item)
          } else {
            models.value.splice(model_index, 1, item)
          }
          is_edit_mode.value = false
      }
      function edit(index) {
          model_index = index
          model_name.value = models.value[index].name
          model_temperature.value = models.value[index].temperature
          is_edit_mode.value = true
      }
      const is_temperature_valid = computed(() => {
        return model_temperature.value >= -20 && model_temperature.value < 50
      })
      return {
        models, is_edit_mode, model_name, model_temperature,
        add, edit, save, is_temperature_valid
      }
    }
  }).mount('#app')
</script>
</body>
</html>
Benutzeravatar
Dennis89
User
Beiträge: 1185
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für die Vorlage, das sieht wirklich übersichtlich aus.

Ich setze mal ein neues Projekt auf und schaue, ob und wie ich das zum laufen bekomme und gehe es dann mal Schritt für Schritt durch.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1185
Registriert: Freitag 11. Dezember 2020, 15:13

Kurzer Nachtrag: Das ist ja geil 😎

Vielen Dank, darauf baue ich auf!

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1185
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Morgen,

ich habe etwas weiter gearbeitet und mir ist bei der Überprüfung etwas aufgefallen. Wenn ich wie im Beispiel so etwas schreibe:

Code: Alles auswählen

const is_temperature_valid = computed(() => {
        return model_temperature.value >= -20 && model_temperature.value < 50
      })
Dann vergleiche ich den String `model_temperature.value` mit einem Integer (?). Das funktioniert auch, erstaunlicherweise auch, wenn ich eine Kommazahl mit Punkt als Trennzeichen eingebe. Ist das doch kein String?

Code: Alles auswählen

console.log(Object.prototype.toString.call(model_temperature.value))
Sagt mir zumindest das es einer ist.

Ich habe jetzt sowas gemacht:

Code: Alles auswählen

const is_inside_diameter_valid = computed(() => {
        return inside_diameter.value < outside_diameter.value && inside_diameter.value > 0
      })
Und verwirrend für mich ist folgendes, wenn der Außendurchmesser "10" beträgt und der Innendurchmesser eine beliebige einstellige Zahl (ist ja jeder kleiner), dann schlägt die Prüfung fehl. Wenn beide Eingaben einstellig oder beide zweistellig sind, dann wird die Prüfung korrekt durchgeführt.
Woran liegt das denn?
Was wird da tatsächlich verglichen?

Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Sirius3
User
Beiträge: 17782
Registriert: Sonntag 21. Oktober 2012, 17:20

Javascript ist schwach typisiert. Wenn man mit einer Zahl vergleicht, wird ein String automatisch konvertiert. Du vergleichst aber anscheinend zwei Strings und da gilt der Lexikalische Vergleich. Deshalb sollte man Variablen explizit konvertieren:

Code: Alles auswählen

1 * inside_diameter.value < 1 * outside_diameter.value && inside_diameter.value > 0
Benutzeravatar
Dennis89
User
Beiträge: 1185
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für die schnelle Antwort und die Erklärung.

Hatte mir schon Gedanken über die Umwandlung gemacht und überlegt, ob ich dann noch ein `try` einbauen muss und sah schon einen großen Berg an Code vor mir. Schön dass das so einfach geht.


Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1185
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Morgen,

ich müsste schon wieder bei euch nachfragen.
Der Benutzer soll eine individuelle Gasmischung erstellen können. Es gibt eine Liste `validGases` in der sind die zur Verfügung stehenden Gase drin. Diese soll man in einem Dropdown-Menü auswählen können und daneben in ein Eingabefenster soll man die Volumenprozente des ausgewählten Gases eintragen können. Mit einem "+" Button soll ein weiteres Dopdown-Menü und Eingabefeld erzeugt werden und zwar so lange, bis die Gasmischung konfiguriert ist.

Meine Idee wie ich das umsetzen wollte hört sich für mich schon falsch an, ich hatte aber keine bessere. Ich erstelle eine Liste `gases` darin befindet sich schon ein leeres Wörterbuch.
Eine `v-for`-Schleife geht über die Liste und erstellt die Elemente. Beim betätigen von "+"-Button wird das ausgewählte Gas und der Prozentwert als Wörterbuch der Liste hinzugefügt. Dann hat meine Liste so viele Elemente, wie ich Dropdown-Menüs will. Das "funktioniert" soweit schon.
Aber ich erstelle keine neuen Menüs sondern die sind mit einander verknüpft, genau wie die Eingabefelder auch. Ändere ich in einem was, dann ändern sich alle.

Ein lauffähiges Minimalbeispiel:

Code: Alles auswählen

<html>
<head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">

  <h2>Gasmischung erstellen</h2>
  <li v-for="_ in gases" :key="_">
    <select v-model="gas_name">
      <option disabled value="">Bitte auswählen</option>
      <option v-for="name in validGases" :key="name">
        {{name}} </option>
    </select>
    <input v-model="percent">%
  </li>
  <button @click="add_gas">+</button>

</div>

<script>
  const { createApp, ref } = Vue

  createApp({
    setup() {
      const gases = ref([{gas_name: undefined, percent: undefined}])
      const gas_name = ref("")
      const percent = ref(0)

      function add_gas() {
        gases.value.push({gas_name: gas_name.value, percent: percent.value})
      }

      validGases = [
        "H2",
        "CO2",
        "CO"
      ]

      return {
        add_gas, 
        gas_name, 
        percent, 
        gases, 
        validGases
      }
    }
  }).mount('#app')
</script>
</body>
</html>

Ich kann mir vorstellen, das ich eventuell mit einer `id` oder einem `index` arbeiten muss. Aber ich verstehe gerade die "Verbindung" der Elemente unter einander noch nicht.

Vielen Dank und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Antworten