Rails Plugin amount_field
Da ich nichts adäquates an Gems oder Plugins gefunden habe, habe ich mich selbst daran gemacht. Und das war anfangs gar nicht so einfach :)
Es ist die Eingabe (die Übertragung aus dem Formular an die Rails-Anwendung) und die korrekt formatierte Ausgabe (Anzeige im View/Formular) zu betrachten.
Eingabe
Vorweg folgende Definition:- Formatvalidierung: z.B. ist "1.234,56" ein gültiges Format?
- Wertvalidierung: z.B. ist 1.234,56 > 1250 ?
- Damit sichergestellt werden kann, dass der eingegebene Wert wirklich ein gültiges Format hat (z.B. 1.234,56), muss zunächst eine Formatvalidierung erfolgen und danach die (sichere) Konvertierung in eine Zahl 1234.56. Im Anschluss kann optional die Wertvalidierung erfolgen.
Ohne die Formatvalidierung würde Rails aus 1.234,56 den Wert 1.23 machen, ohne dass der Anwender das gegebenenfalls merkt. Und das ist schlecht.
-
Die Formatierung (Ein- und Ausgabe) hat eigentlich nichts mit dem Attribut oder dem ActiveRecord-Modell zu tun. Es handelt sich ja mehr um eine bestimmte Repräsentation eines Betrags und dafür ist das Modell nicht zuständig. Zur formatierten Anzeige werden u.a. die View-Helper von Rails genutzt.
Die Konvertierung von "1.234,56" in 1234.56 kann aber nicht auf Controller/View-Ebene erfolgen, da die Validierung der Attribute in ActiveRecord geschieht. Und die Formatvalidierung ist eben eine solche Validierung. Daher bleibt doch nur das Modell.
- Die Zuweisung als Ruby-Zahl an den Setter (an das Attribute) muss weiterhin wie gewohnt möglich sein:
p = Product.new p.price = '12.345,67' # schlecht p.price = 12345.67 # ok - Der Getter des Attributs muss den Wert als (Ruby-)Zahl liefern, damit Berechnungen weiterhin wie gewohnt möglich sind:
p = Product.new(:price => 1.23) p.price # => 1.23 damit Rechnen möglich tax = p.price * 19.0 / 100.0 - Eine clientseitige Formatvalidierung (und Formatierung) durch JavaScript ist hilfreich, verhindet aber nicht unbedingt die Übertragung falsch formatierter Beträge und daher ist eine serverseitige Formatvalidierung immer notwendig.
- Die Formatierung eines Betrags besteht immer aus den drei Werten:
- Separator (Tausendseparator)
- Delimiter (Trennung Ganzzahl und Nachkommastellen)
- Precision (Anzahl der Nachkommastellen)
Die drei Werte bilden die Formatkonfiguration:
deutsch:{ :precision => 2, :delimiter => '.', :separator => ',' }
amerikanisch:{ :precision => 2, :delimiter => ',', :separator => '.' } - Fehlermeldungen bei einen falschen Format sollten internationalisierbar sein
- Hinweis: Active Record speichert den übergebenen Wert “1.234,56” in dem Attribute "price". Erst beim Lesen des Wertes wird dieser konvertiert zurückgeliefert. Daher liefert "price_before_type_cast" den Wert “1.234,56” und "price" den Wert 1.234 zurück. Die Wertvalidierung erfolgt bei Active Record auf Basis von XXX_before_type_cast.
Als Lösung definiere ich einen eigenen View-Helper "amount_field" als Ersatz für "text_field". Das HTML sieht wie folgt aus:
<input name="product[amount_field_price]" class=" amount_field"... />
Der eingegebene Wert (z.B. "1.234,56") wird dadurch über den Parameter amount_field_price und nicht (wie normalerweise) über den Parameter price übertragen.
Zusätzlich existiert das Validierungsmakro "validates_amount_format_of", dass für jedes angegebene Attribut eine spezielle Setter-Methode (z.B. amount_field_price=(value) definiert, die den Parameter amount_field_price annimmt und Werte im Format "1.234,56" akzeptiert.
validates_amount_format_of :price
end
Nach der Validierung und Konvertierung (z.B. "1.234,56" in 1234.56) wird der Wert an das Originalattribut "price" zugewiesen und kann von weiteren Validierungsmakro geprüft werden (z.B: validates_numericality_of :price).
Ausgabe
Damit die Ausgabe des Wertes 1234.56 formatiert erfolgt (z.B. "1.234,56"), verwendet der View-Helper amount_field intern den View-Helper number_with_precision mit der global definierten Formatkonfiguration. Im Fehlerfall wird der Wert nicht formatiert, sondern wie eingegeben angezeigt.
Das Gem (oder Plugin) inklusive Dokumentation ist unter GitHub zu finden.
Freue mich über Feedback, Fragen oder Fehlermeldungen.
