AngularTool: onkey down event handler

In C# oder VB gibt ein Eventhandler um Keyboard Eingaben zu filtern und direkt zu unterdrücken:

'#### EINGABEFILTER
    Private Sub EingabeFilter(absender As Object, keyArgs As KeyPressEventArgs)
        Dim asc As Integer = Convert.ToInt32(keyArgs.KeyChar)  ' Asci-Code ermitteln
        Select Case asc
            Case 8, 44, 46, 48 To 57
            Case Else
                keyArgs.Handled = True
        End Select
    End Sub


--- bzw ---
 If (e.KeyCode = Keys.Enter) And (sender.Text <> String.Empty) Then    ' Enter-Taste
            EventDone = True 'Vermeiden der Ausführung

In JavaScript könnte so etwas implementiert werden, aber beim Tastenprellen funktioniert es nicht, auch bei schnellen Eingaben kann etwas "unter dem Tisch" fallen.

window.addEventListener('keyup', (event:KeyboardEvent )=>
{this.keyValue = event.key;
  if (parseInt( this.keyValue) >=  0 && parseInt( this.keyValue) <= 9 )
      { /*Eingabe OK*/ }
  else 
      {this.A = this.A.replace( event.key,"");}
}

Welche Lösung könnte es geben um nur Zahlen und Komma zuzulasen oben erst "ENTER Taste" zu drücken.

  1. Hallo AngularTool,

    was für ein Gerät und was für ein Betriebssystem verwendest Du, dass da die Tasten prellen und das nicht vom Betriebssystem abgefangen wird? Vermutlich meinst Du eher Autorepeat.

    in HTML - nicht JavaScript - gibt es <input type="number">

    Wenn Dir das zu viel zulässt, kannst Du noch das pattern-Attribut an den Start bringen (wobei ich grad erstmal ausprobieren müsste, ob das bei type="number" greift)

    Wenn Dir das auch nicht passt (weil das Pattern keine Eingabe unterdrückt), dann nimm JavaScript, aber registriere Dich nicht auf keyup, sondern auf keydown (nicht keypress, das soll aussterben). Zum einen wird das ausgelöst, bevor das input das Zeichen sieht, zum anderen wird es bei Autorepeat nicht nur ein einziges Mal, sondern einmal pro Repeat ausgelöst.

    Im Standard gibt's auch noch das beforeinit-Event, aber das ist noch zu rot.

    Im keydown-Event kannst Du das Event unterdrücken, indem Du auf dem Event-Objekt, dass der Eventhandler übergeben bekommt, die Methode preventDefault aufrufst. Denke auch daran, shiftKey, ctrlKey, metaKey und altKey auf false zu prüfen.

    Weitere Lektüre

    Rolf

    --
    sumpsi - posui - clusi
    1. Hallo Rolf B, danke für die schnelle Antwort. (Windows 10, Firefox 70.0.1 (64-Bit) )

      Ja, Du hast recht: Ich meine Autorepeat bei ständig gedrückter Taste.

      <input type="number">, hier wird das Input Element mit "Enter" zur Validierung abgeschickt.

      Ich habe "keydown, Keypress, und Keyup versucht, leider war über preventDefault nicht die Eingabe zu "stoppen". Da ist VB und C# absolut zuverlässig, es wird nichts übernommen, egal wie lang der4 String wird.

      Leider habe das Ganze mit Angular am Start, und hier ist eine Doppel-Bindung zwischen dem Input und der Varíablen möglich und vorhanden.

      Übrigen dieser Code klappt, bis auf das Autorepeat:

      window.addEventListener('keyup', (event:KeyboardEvent )=>
      {this.keyValue = event.key;
        if (parseInt( this.keyValue) >=  0 && parseInt( this.keyValue) <= 9 )
            { /*Eingabe OK*/ }
        else 
            {this.A = this.A.replace( event.key,"");} // Löscht das letzte Eingeabezeichen.
      }
      

      DANKE für die Links, da scheint eine Lösung dabei zu sein, über Funktionen ersteinmal alles abfangen.

      1. Hallo AngularTool,

        ja ok, an Angular habe ich nicht gedacht. Damit habe ich keine Erfahrung.

        Meine Gehversuche mit MVVM habe ich mit knockout.js gemacht.

        Hast Du es in Angular nicht in der Hand, wie das Input an das Modell gebunden wird? Normalerweise gehen MVVM Tools doch her und übertragen den Feldinhalt erst ins Modell, wenn das Eingabefeld verlassen wird. Das wäre eine Überlegung wert.

        Wenn das nicht hilft, und Angular sich auf keydown oder keypress registriert, bevor dein Code anläuft, bist Du ausgezählt. Es wird sicherlich nicht klappen, vor Angular zum Zug zu kommen.

        Es bleibt dann wohl nur ein Hack, oder eine gute Idee der Kollegen hier ;).

        Der Hack: Du müsstest dich auf einen Container des input registrieren, und zwar mit true als 3. Parameter von addEventListener (capturing handler). Damit kannst Du erreichen, dass das Event gar nicht erst beim Input ankommt.

        Hier bietet sich das Label an, das ein Input sowieso haben sollte. Man kann das for-Attribut von <label> vermeiden, indem man das input als Kindelement des Label setzt:

        <label class="digitOnly">Zahlenfolge<br>
        <input type="text">
        </label>
        

        Wenn Du auf alle Elemente mit class="digitOnly" einen capturing-Handler für keydown legst, könnte der Coup gelingen.

        Rolf

        --
        sumpsi - posui - clusi
      2. document.getElementById('name').addEventListener('click', handler, true);

        Der Dritte Parameter true ist offensichtlich vergleichbar mit dem event.done im C# oder VB. Leider nirgendswo in den Büchern gesehen.

        Ja, mit dem Input INNERHALB des Labels komme ich weiter... Werde über das Ergebinis berichten.

        1. Hallo AngularTool,

          Der Dritte Parameter true ist offensichtlich vergleichbar mit dem event.done im C# oder VB.

          Nein, wenn ich das eventdone richtig verstehe, entspricht das in etwa dem preventDefault Aufruf.

          Ein Capturing-Eventhandler (addEventListener("...", handler, true)) ist etwas Spezielles im DOM: Das Event sucht sich zunächst seinen Weg vom Root des DOM (also das Dokument) nach unten zum Event-Auslöser (capturing-Phase), wird dort geworfen (trigger) und blubbert dann zurück nach oben (bubbling-Phase). Das gibt's meines Wissens in .net so nicht, zumindest nicht die Capturing-Phase.

          Wenn der Eventhandler auf dem Element registriert ist, auf dem das Event ausgelöst wird, ändert der 3. Parameter nichts. Trigger ist Trigger. Der Unterschied entsteht bei Registrierung auf Elternelementen im DOM.

          Ist der 3. Parameter false, greift der Handler beim Bubbling. Das ist das vertraute Verhalten: Elternelemente können Events ihrer Kinder auch behandeln, aber die Kinder sind zuerst dran.

          Ist der 3. Parameter true, greift der Handler vor dem Trigger. Das ist das Verhalten von überfürsorglichen Eltern: Alles erstmal begutachten bevor das Kind dran kommt.

          Dabei haben die Eltern zwei Optionen: preventDefault - das sorgt dafür, dass der Browser das Event nicht mehr anfasst wenn es durch's JavaScript durchgereicht wurde. Und stopPropagation - das ist etwas anderes, aber genauso gemein: Das Weiterreichen des Events durch's JavaScript wird gestoppt. Das hindert den Browser aber nicht an seiner Default-Aktion.

          D.h. ein preventDefault könnte - wenn Angular auf dem Element selbst lauscht, ggf. wirkungslos sein. Und zwar dann, wenn Angular die Tastendrücke aus irgendeinem schrägen Grund selbst verarbeitet. In dem Fall hilft dann nur noch das Fernhalten der leckeren Keydown-Kekse vom plärrenden Angular-Kleinkind, durch stopPropagation in der Capturing-Phase.

          Rolf

          --
          sumpsi - posui - clusi
      3. Tach!

        Leider habe das Ganze mit Angular am Start, und hier ist eine Doppel-Bindung zwischen dem Input und der Varíablen möglich und vorhanden.

        Übrigen dieser Code klappt, bis auf das Autorepeat:

        window.addEventListener('keyup', (event:KeyboardEvent )=>
        {this.keyValue = event.key;
          if (parseInt( this.keyValue) >=  0 && parseInt( this.keyValue) <= 9 )
              { /*Eingabe OK*/ }
          else 
              {this.A = this.A.replace( event.key,"");} // Löscht das letzte Eingeabezeichen.
        }
        

        Sich an das window-Objekt zu hängen ist eigentlich nicht der Angular-Weg. Sollen wirklich nach Aufruf dieses Codes in der gesamten Anwendung Tastendrucke gefiltert werden? Falls das nur auf bestimmte Input-Elemente angewendet werden soll, ist der Einsatz einer Directive besser.

        @Directive({
            selector: '[input-restriction]'
        })
        export class InputRestriction {
            constructor(private element: ElementRef) {}
        
            @HostListener('keydown', ['$event'])
            onKeyDown(event: KeyboardEvent) {
                if (event.key == 'a') {
                    event.preventDefault();
                }
            }
        }
        

        Anwendungsbeispiel:

        @Component({
            selector: 'test',
            templateUrl: './test.component.html'
        })
        export class TestComponent {
            formControl: FormControl;
        
            constructor() {
                this.formControl = new FormControl('');
                this.formControl.valueChanges.subscribe(console.log);
            }
        }
        

        Ausschnitt aus test.component.html:

            <label>No-a's Arc
                <input [formControl]="formControl" input-restriction>
            </label>
        

        Die InputDirective filtert derzeit alle a aus. Sie ist ausbaufähig, zum Beispiel, dass man Parameter übergeben kann, was gefiltert werden soll.

        dedlfix.

        1. Hallo Rolf,

          erst einmal vielen, vielen Dank für Deine Antwort!

          Leider geht das Ganze ja noch etwas weiter, Angular wird in TypeScript programmiert. Das ist ja erst einmal nicht schlimm gesehen werden, da es in JS später umgesetzt wird. Dennoch ist im TypeScript die Klasse "noch mehr" Klasse als im JS und die "this." ist der Funktionsauftrufe ist durch JS vom Aufrufungsort(-objekt) (und nicht von der Definition). D.h. mit meinem Klassen .this habe ich nun ein Problem. Das Klassen .this benötige ich zur Dopplelbindung zwischen dem HTML Code <input ng{(A)} und dem JS (Typescript) A.this … Da habe ich mit gestern erst einmal die Karten gelegt. Hier werde ich mit etwas mit Delegaten ausdenken müssen.

          Was als Resultat dabei herauskommen soll kann auf der Seite angulartool...de gesehen werden. Bei der Eingabe der Zahlen werden sofort die Ergebnisse geliefert, auch später einmal, "kreuzweise". D.h. es ist egal, welcher Parameter geändert wird von A+B=C, es wird entsprechend (nach meiner Vorgabe) geändert. Hier bei C-Änderung wird das A oder selektiert das B geändert.

          Grüße Jürgen

          1. Hallo dedlfix,

            ja, das ist genau die Baustelle die habe!

            @HostListener('keydown', ['$event'])...

            Denn nach der Lösung möchte ich ja nicht für jedes <input> Element die konkrete this.A auf B.This ect. erweitern.

            Leider bin ich auf der Arbeit, und kann den Code nicht laufen lassen... Melde mich aber wieder.

            DANKE! Jürgen

            1. Tach!

              ja, das ist genau die Baustelle die habe!

              @HostListener('keydown', ['$event'])...

              Denn nach der Lösung möchte ich ja nicht für jedes <input> Element die konkrete this.A auf B.This ect. erweitern.

              Beschreib doch mal konkret, was du für ein Problem hast. Vermutlich löst sich das mit den this in Luft auf, wenn du es Angular-like löst.

              Meine Beispiel-Direktive kommt in ihrer jetzigen Form ohne this aus, weil sie die notwendigen Dinge (hier das Event-Objekt) als benannten Parameter reingereicht bekommt. Ein this würde sie nur für ihre eigenen Zwecke benötigen, beispielsweise wenn sie mit Parametern initialisiert werden soll, die in einer Eigenschaft abgelegt werden müssen, um zum Event darauf zugreifen zu können.

              Das Input-Element muss sie ja auch gar nicht befummeln, und wenn doch, steht es über event.target zur Verfügung. Da spielt also auch kein this eine Rolle, das für das aktuelle Element steht.

              dedlfix.

              1. Moin Moin,

                Leider komme ich nun erst jetzt zur Antwort der Fragen.

                Ich kenne Angular erst seit 2 Monaten. Dank guter Tutorials und dem Buch Angular (Höller, sowie Pro... JavaScript von Ackermann, Entwurfmuster von Geirhos) im Rheinwerk Verlag kann ich nun mein Anliegen hoffentlich sinnvoll und modular verwirklichen. Es geht mit um eine Formelsammlung für Mechanik, Physik und Mathe. Das gibt es ja schon im Web, aber mit Werbung und oft ohne der Eingabe der Einheiten. Das Ganze soll dann per Kopfdruck in Form mit gegeben, Formel mit Werten und dem Ergebnis in eine Datei geschrieben werden. Hierbei ist es mir Wichtig, dass klar nachvollzogen werden kann, wie das Ergebnis entstanden ist, natürlich ohne Formelumstellung etc. Das Ganze kann dann händisch zu einer Berechnung einer Konstruktion oder Sonstiges zusammengebastelt werden. Modular deswegen, damit Stück für Stück die Formelsammlung erweitert werden kann.

                Mein Hauptprobem hierzu sind:

                Wie kann ich eine modulare Formelsammung über Function aufbauen. Macht es Sinn, alle Formeln als Function Zentral anzulegen, oder doch in „Klassen“ und pro „Seite“. Wie kann Elegant die Variablen von Seite zur Seite übergeben werden. Und da ich kein Fan der Entertaste bin, soll bei der Eingabe schon auf sinnvolle Zeichen geprüft und ggf. die falschen „unterdrückt“ werden.

                1. Tach!

                  Es geht mit um eine Formelsammlung für Mechanik, Physik und Mathe. Das gibt es ja schon im Web, aber mit Werbung und oft ohne der Eingabe der Einheiten. Das Ganze soll dann per Kopfdruck in Form mit gegeben, Formel mit Werten und dem Ergebnis in eine Datei geschrieben werden. Hierbei ist es mir Wichtig, dass klar nachvollzogen werden kann, wie das Ergebnis entstanden ist, natürlich ohne Formelumstellung etc.

                  Das sind inhaltliche Aspekte und vor allem eine Frage der Gestaltung der Benutzeroberfläche. Das hat erstmal nichts mit der Technik darunter zu tun. Die kommt erst im zweiten Schritt hinzu, wenn man sich Gedanken macht, wie man die geplante Oberfläche implementiert.

                  Wie kann ich eine modulare Formelsammung über Function aufbauen. Macht es Sinn, alle Formeln als Function Zentral anzulegen, oder doch in „Klassen“ und pro „Seite“.

                  Du wirst die Formeln nur einmalig notieren wollen. Sie werden aber mehrfach verwendet werden, weil sie zum Beispiel allgemeiner Teil von diversen Berechnungen sind. Das Ausführen der Formeln ist nicht von einer Bedienoberfläche abhängig. Für solche im Hintergrund stattfinden Aufgaben sind Services vorgesehen.

                  Wie kann Elegant die Variablen von Seite zur Seite übergeben werden.

                  Wenn es ein mehrstufiger Bedienprozess ist, würde ich dafür eine Komponente aus einer der Komponentensammlungen nehmen, die den jeweils benötigten Bedienschritt einblendet. Das Verteilen auf mehrere Komponenten, zwischen denen hin und her geroutet wird, ist mehr umständlich als sinnvoll. Komponenten für die Bedienoberfläche, die bereits anderenorts existieren, selber zu entwickeln, kann man machen, ist aber meist Zeitverschwendung mit schlechterem Resultat.

                  dedlfix.

                  1. Du kannst einem auf meine Website "Nickname".de sehen, wie ich das Ganze aufbauen möchte. Im Grunde sind die Formeln (Biegefall) für jede Anwendung unterschiedlich, vor allem die Eingabeparameteranzahl kann sich ändern, mal habe ich 2 Lastangriffe oder halt 3... Was ich jedoch durch optionale Parameter steuern werde.

                    Für solche im Hintergrund stattfinden Aufgaben sind Services vorgesehen. Ist ein hilfreicher Tipp.

                    Wenn es ein mehrstufiger Bedienprozess ist, würde ich dafür eine Komponente aus einer der Komponentensammlungen nehmen,

                    Das isat mir naoch nich klar, wie diese aufgebaut und implementiert werden.

                    Eigentlich habe ich eine einstufe Eingabe geplant. D.h. die einzelnen Fälle (von einfacher Kreisformel-Fläche, Biegefall, ect.) werden einzeln selektiert und dann die Werte eingegeben und berechnet und auch einzeln in einer Datei oder separiert innerhalb einer Datai ausgegeben.

                    Die Separierung hat den Vorteil des Modularen, siehe meinen Grundaufbau: zur Zeiut kann ich nichts hochladen?!

                    1. Ja, Du hast Recht, Services als DI mit Provider sind die Lösung. D.h. weg von der "Componentenklassen" zur modularem Service. Hier muß ich mich aber noch etwas mehr einlesen, da ich die Eingabewerte; Kraft (F)´, Länge (L) ... einmalig eingeben will, und dann bei den verschiedenen Biegefällen (Einseitig, beidseitig Fest und einseitig Lose/Fest durch "anklicken" in der "Navbar" berechnen und auswerten möchte.

                      1. Tach!

                        [...] modularem Service. Hier muß ich mich aber noch etwas mehr einlesen, da ich die Eingabewerte; Kraft (F)´, Länge (L) ... einmalig eingeben will, und dann bei den verschiedenen Biegefällen (Einseitig, beidseitig Fest und einseitig Lose/Fest durch "anklicken" in der "Navbar" berechnen und auswerten möchte.

                        Services sind üblicherweise globale Singletons. Damit eignen sie sich auch als Zwischenspeicher von Daten, sie man zwischen Komponenten austauschen möchte.

                        Vielleicht möchtest du aber auch zwei Komponenten gleichzeitig darstellen, in der einen die oben genannten Werte abfragen und in der anderen eine der konkreten Verwendungen darstellen. Das wäre dann eine Kommunikation zwischen zwei Komponenten. Ein Service als Mittler ist eine Variante, weitere sind unter Component Interaction aufgeführt.

                        dedlfix.

                    2. Tach!

                      Wenn es ein mehrstufiger Bedienprozess ist, würde ich dafür eine Komponente aus einer der Komponentensammlungen nehmen,

                      Das isat mir naoch nich klar, wie diese aufgebaut und implementiert werden.

                      Beispielsweise ist PrimeNG eine sehr umfangreiche Sammlung an Komponenten für diverse Ein- und Ausgaben. Die KeyFilter-Komponente würde schonmal dein Problem vom Erstposting lösen.

                      Bei mehrstufig dachte ich an sowas wie Steps. Wenn du keine derartig komplexe Eingabe brauchst ... auch gut, war ja nur ein Beispiel. Könnte aber vielleicht bei Lektionen interessant sein, falls du sowas planst. Damit kann man sie in kleine Häppchen teilen und anzeigen, wie weit man ist, und wieviel noch kommt.

                      dedlfix.

                      1. Moin Moin, danke für Deine Tipps! Ich bin etwas überrascht, wie viele ng Tools und Hilfsmittel - auch von diversen Gruppen- es so gibt. Da war VB (2012) C# doch etwas übersichtlicher.

                        Mit den rudimentären Kenntnissen werde ich wohl erstmal ein Konzept erstellen. Ich bin eigentlich ein Freund einer soliden Basisimplementierung. Mag sein, dass das dann etwas "oversized" ist, aber ich hoffe dann nicht so schnell an die Grenzen zu stoßen. Meine Idee der Formelsammlung ist es, sehr unterschiedliche Bereiche abzudecken die dann wieder innerhalb ihrer Gruppen in Einzelteile Zusammengefasst werden.

                        Beispiel: Kreis und Kreisring:

                        1. „Beidseitiges“ Bestimmen des Aussen-Durchmessers // Innen-Durchmesser <-> Fläche
                        2. „Beidseitiges“ Bestimmen des Aussen-Durchmessers // Innen-Durchmesser <-> Flächenträgheitsmoment
                        3. „Beidseitiges“ Bestimmen des Aussen-Durchmessers // Innen-Durchmesser <-> Flächenwiderstandsmoment

                        Das kann dann natürlich auf das Quadrat und Rechteck sinngemäß umgesetzt werden.

                        Und so weiter und so weiter... . Ich benötige sozusagen jeweils ein Template als Gerüst für die Verbindung/Erstellung der Formeln und der Kopplung der Eingabe und der Eingabemaske, so dass die Sammlung sich immer weiter entwickeln kann.

                        Ich bin mir sicher, dass das Framework Angular (und Javascript) hierzu gute Konzepte "bereitstellen". Jedenfalls ist es schon gut, dass auch Funktionen (Formeln) als Objekte in der Parameterliste übergeben werden können. Auch dass Variablen über Service global aber gekapselt bereitgestellt werden können.

                        Grüße Jürgen