Beim Arbeiten an komplexen Sass-Architekturen ist es nicht ungewöhnlich, Sass-Maps zur Verwaltung von Konfigurationen und Optionen zu verwenden. Von Zeit zu Zeit werden Sie Maps innerhalb von Maps (möglicherweise auf mehreren Ebenen) sehen, wie diese aus o-grid.
$o-grid-default-config: (
columns: 12,
gutter: 10px,
min-width: 240px,
max-width: 1330px,
layouts: (
S: 370px, // ≥20px columns
M: 610px, // ≥40px columns
L: 850px, // ≥60px columns
XL: 1090px // ≥80px columns
),
fluid: true,
debug: false,
fixed-layout: M,
enhanced-experience: true
);
Das Problem bei solchen Maps ist, dass es nicht einfach ist, Werte aus dem verschachtelten Baum zu lesen und zu schreiben. Dies ist definitiv etwas, das Sie in Funktionen verstecken möchten, um es nicht jedes Mal manuell tun zu müssen.
Deep Get
Tatsächlich ist das Erstellen einer Funktion zum Abrufen tief verschachtelter Werte aus einer Map sehr einfach.
/// Map deep get
/// @author Kitty Giraudel
/// @access public
/// @param {Map} $map - Map
/// @param {Arglist} $keys - Key chain
/// @return {*} - Desired value
@function map-deep-get($map, $keys...) {
@each $key in $keys {
$map: map-get($map, $key);
}
@return $map;
}
Wenn wir beispielsweise den Wert abrufen möchten, der der M-Layout zugeordnet ist, aus unserer Konfigurationsmap, ist es so einfach wie
$m-breakpoint: map-deep-get($o-grid-default-config, "layouts", "M");
// 610px
Beachten Sie, dass Anführungszeichen um Zeichenketten optional sind. Wir fügen sie nur aus Gründen der Lesbarkeit hinzu.
Deep Set
Auf der anderen Seite kann das Erstellen einer Funktion zum Setzen eines tief verschachtelten Schlüssels sehr mühsam sein.
/// Deep set function to set a value in nested maps
/// @author Kitty Giraudel
/// @access public
/// @param {Map} $map - Map
/// @param {List} $keys - Key chaine
/// @param {*} $value - Value to assign
/// @return {Map}
@function map-deep-set($map, $keys, $value) {
$maps: ($map,);
$result: null;
// If the last key is a map already
// Warn the user we will be overriding it with $value
@if type-of(nth($keys, -1)) == "map" {
@warn "The last key you specified is a map; it will be overrided with `#{$value}`.";
}
// If $keys is a single key
// Just merge and return
@if length($keys) == 1 {
@return map-merge($map, ($keys: $value));
}
// Loop from the first to the second to last key from $keys
// Store the associated map to this key in the $maps list
// If the key doesn't exist, throw an error
@for $i from 1 through length($keys) - 1 {
$current-key: nth($keys, $i);
$current-map: nth($maps, -1);
$current-get: map-get($current-map, $current-key);
@if $current-get == null {
@error "Key `#{$key}` doesn't exist at current level in map.";
}
$maps: append($maps, $current-get);
}
// Loop from the last map to the first one
// Merge it with the previous one
@for $i from length($maps) through 1 {
$current-map: nth($maps, $i);
$current-key: nth($keys, $i);
$current-val: if($i == length($maps), $value, $result);
$result: map-merge($current-map, ($current-key: $current-val));
}
// Return result
@return $result;
}
Wenn wir nun den Wert aktualisieren möchten, der dem M-Layout zugeordnet ist, aus unserer Konfigurationsmap, können wir dies tun:
$o-grid-default-config: map-deep-set($o-grid-default-config, "layouts" "M", 650px);
Zusätzliche Ressourcen
Die obige Funktion ist nicht die einzige Lösung für dieses Problem.
Die Bibliothek Sassy-Maps bietet ebenfalls die Funktionen map-deep-set und map-deep-get. In ähnlicher Weise hat Kitty Giraudel auch eine jQuery-ähnliche extend-Funktion geschrieben, um das integrierte map-merge rekursiv zu machen und mehr als zwei Maps auf einmal zusammenzuführen.
Tippfehler
sollte sein
Cooles Snippet aber!
-Von Zeit zu Zeit
Sollte eigentlich sein
Von Zeit zu Zeit....
Riesiger Zeitsparer, danke Hugo!
Wie immer nützlich, danke!
Es gibt sogar eine map-merge-deep() ... schau dir den PR im Sassy-Maps-Projekt an. Funktioniert in meinen Projekten wie ein Zauber. https://github.com/at-import/Sassy-Maps/issues/9
Das ist ein guter Anfang! Ich habe den Getter genommen und eine namespace-ähnliche Version erstellt.
Zuerst habe ich eine Funktion erstellt, die den String nach Punkten in Schlüssel aufteilt und dann den Wert aus der bereitgestellten Map holt.
Sie könnten dann Hilfsmethoden erstellen, um auf verschiedene Konfigurations-Maps zuzugreifen und die zugrunde liegende Map zu abstrahieren, wie die folgenden:
@Travis Das ist wirklich hilfreich, danke!
Entschuldigung für die Stile (ich weiß nicht, wie man sie schön macht).
Also meine Variante der „get“-Funktion zur Verwendung in allen Fällen.
@function get($map, $paths...){
$allParts: ();
@each $path in $paths {
$pathParts: str-split($path, '.');
@each $pathPart in $pathParts {
$allParts: append($allParts, $pathPart);
}
}
@each $key in $allParts {
$map: map-get($map, $key);
}
@return $map;
}
verwenden
get($buttons, 'foo.bar.baz');
get($buttons, 'foo', 'bar', 'baz');
get($buttons, foo, bar, baz);
get($buttons, 'foo.bar', 'baz');
get($buttons, 'foo.bar', baz);
get($buttons, 'foo', 'bar.baz');
Ich habe ein kleines Problem mit map-deep-get. In Ihrem Beispiel ruft die Funktion einen einzelnen spezifischen Wert ab. Aber wenn ich das in Codepen versuche, bekomme ich einen Fehler. Es scheint, als würden alle Schlüssel und Werte abgerufen. https://codepen.io/aalokt89/pen/NLzvpL
Nette Funktion, aber sie geht davon aus, dass alle Schlüssel existieren, was nicht immer der Fall sein muss. Ich habe sie leicht angepasst, um dies zu verhindern und "null" zurückzugeben, wenn ein Schlüssel in der Kette nicht gefunden wird.
@marc Niiice Danke! Ich brauche das auch als Bedingung, um die Existenz von Schlüsseln zu prüfen.