Less.js: Permitir que los guardias trabajen con variables indefinidas

Creado en 4 jul. 2013  ·  46Comentarios  ·  Fuente: less/less.js

.guard () when (@variable) {
  a {
    color: red;
  }
}

La variable no está definida, por lo tanto, no hay salida.

<strong i="8">@variable</strong>: true;

.guard () when (@variable) {
  a {
    color: red;
  }
}

La variable está definida, por lo tanto, la salida:

a {
  color: red;
}
feature request low priority support as plugin

Comentario más útil

¿Ha habido algún movimiento en esta solicitud? He estado ansioso por una verificación de variable indefinida desde hace algún tiempo. Por el momento, tengo un trabajo que está lejos de ser ideal...

p {
  & when not (@body-text-color = null) {
    color: @body-text-color;
  }
}

pero, para que esto funcione, la variable debe existir y estar definida como null por defecto. Esto sería mucho más efectivo/flexible si pudiera verificar si la variable existe en primer lugar.

Todos 46 comentarios

+1
Esto también podría usarse para establecer colores base en las bibliotecas.
Imagina el siguiente caso:
Tienes un archivo menos para una aplicación específica, por ejemplo, el menú principal de un sitio web.
Importa este archivo de menos en el archivo de menos principal, donde sus colores base se definen como variables.
Ahora, si usa esos colores base en el archivo menos del menú principal, dependen del archivo principal.
Si pudiera verificar la existencia de las variables, podría recurrir a los colores definidos por el módulo.
Aquí hay un ejemplo de cómo me imagino el menú sin archivo:

@menu-base-color: white;
@menu-base-color: @base-color when (@base-color);
...or...
@menu-base-color: @base-color when isset(@base-color);

+1, ya tenemos istext , isnumber con isdefined y isundefined ayudarán mucho en la tematización con menos.

Es decir, use value o userValue si isdefined

Me encuentro mucho con este problema cuando trato de configurar convenciones temáticas para la interfaz de usuario semántica con LESS.

+1 en esto - sería realmente genial.

+1 en esto para mí también

No es gran cosa agregar la función is-defined (espero que aparezca en uno de los primeros complementos tan pronto como se publique v2). Aunque, para ser honesto, lo triste es que aquellos que quieren esta característica realmente se pierden el hecho de que los dos casos de uso anteriores son solucionables (en muchos casos, incluso con un código más compacto) sin ninguna característica como esa.

Caso de uso de @InitArt :

// library.less
<strong i="9">@variable</strong>: false;

a when (@variable) {
    color: red;
}

// ......................................
// user.less
<strong i="10">@import</strong> "library.less"
<strong i="11">@variable</strong>: true;

Caso de uso de @nemesis13 :

// library.less
@base-color: green;
@menu-base-color: @base-color;

.menu {
    background-color: @menu-base-color;
}

// ......................................
// user.less
<strong i="16">@import</strong> "library.less"
@base-color: white;
// or:
@menu-base-color: black;
// or whatever...

Es decir, en resumen: en lugar de probar si una variable está definida, solo proporcione un valor predeterminado para esa variable, ya que los valores de las variables se pueden anular libremente.

¿Alguien puede responder por qué el ejemplo de Max no es factible para ellos? yo solo puedo
suponga que es porque no sabe si la variable está definida arriba o
por debajo de la importación? ¿O es otra cosa?
No vamos a considerar algo que no tenga casos de uso.
La desventaja de hacer esto es que las personas que escriben mal una variable no verán una
error. Podríamos solucionar esto con una advertencia, pero luego, si lo hizo en
propósito, no querrías una advertencia ... ese es mi único problema con esto
sugerencia.

Esto es especialmente útil cuando se definen temas para reducir el tamaño del CSS resultante. Al crear temas en general, a uno le gustaría habilitar la configuración de muchas propiedades. Sin embargo, es posible que los usuarios deseen configurar solo algunos de ellos dejando el resto de las propiedades sin configurar. Por supuesto, establecerlos en los valores predeterminados funcionaría en general, sin embargo, esto infla el CSS final.

Considere el siguiente tema base (plantilla):

.my-class {
    color: @text-color;
    background-color: @background-color;
    border-color: @border-color;
}

El usuario del tema puede querer configurar solo el color del texto dejando el resto de las propiedades sin configurar. Por supuesto, es posible establecer los valores en "inicial", "transparente", "heredar", etc., sin embargo, esto aumenta el tamaño del CSS final. Los temas tienden a tener cientos de estas propiedades, por lo que el tamaño puede aumentar considerablemente.

@JechoJekov

Creo que se está perdiendo el punto de esta solicitud de función, tenga en cuenta que para su caso de uso aún deberá proteger cada una de estas propiedades (por lo tanto, la lógica que sugerí en las dos publicaciones anteriores aún se aplica). Entonces, no creo que su caso de uso agregue nada nuevo a # 1400 (básicamente parece que está sugiriendo una característica diferente como "omitir propiedad si todos sus valores están configurados con variables indefinidas" o algo así pero es otro gran historia).

@siete-fases-max

Quizás sería mejor si es posible establecer una variable en un valor "indefinido" o "no establecido". Establecer una propiedad en tal valor significaría que la propiedad no se representa en el CSS final. Esto simplifica la sintaxis y permite validar que la variable está declarada.

@JechoJekov

Quizás sería mejor si es posible establecer una variable en un valor "indefinido" o "no establecido".

Como mencioné, esta es otra característica que necesita su propio boleto (con su propia discusión).

Solo una aclaración para la hoja de ruta: Esto está marcado como "ReadyForImplementation", pero no parece haber consenso en este hilo, y parece que @seven-phases-max sugirió una alternativa decente. @lukeapage @seven-phases-max, ¿debería eliminarse listo para la implementación?

@ matthew-dean Ni idea. Supongo que aquí "ReadyForImplementation" todavía es válido, lo que significa algo así como "esta característica parece no ser realmente necesaria, pero si alguien presenta un PR implementando tal cosa, no habrá resistencia" o algo así :)

¿Como es que? (Bajo consideración)

Para mí, "Listo para la implementación" significa que existe un consenso general (pero no necesariamente unánime) de que _debería_ abordarse o implementarse. Creo que "Bajo consideración" cumple con su definición.

Todavía me encuentro con este problema al usar el alcance

<strong i="6">@foo</strong>: 'bar';
& { 
  // not sure if <strong i="7">@foo</strong> is defined
}

Me encantaría poder usar isDefined

Si bien algo como is-defined tiene sentido (como azúcar sintáctico), en realidad no es necesario. Simplemente _siempre_ defina @foo (con el valor predeterminado que necesite), después de todo, si está usando una variable dentro de sus estilos, _puede_ predecir que necesita definirla, por ejemplo:

// in a far far away galaxy of your library default variables:
<strong i="8">@foo</strong>: undefined;

// the code where you need it
& when not(<strong i="9">@foo</strong> = undefined) { 
    // <strong i="10">@foo</strong> is defined by user    
}

// some user code
<strong i="11">@foo</strong>: bar;

Aunque supongo que ya escribí esto en los ejemplos anteriores (solo estoy tratando de asegurarme de expresar el hecho de que la falta de esta función no puede ser un impedimento para nada. Si conoce algún caso de uso donde está el uno, por favor compartir).

Estoy tratando de configurar vistas previas de temas en el navegador.

Esto significa que tengo que usar less.js y sobrescribir la variable predeterminada @theme con la configuración de tiempo de ejecución.

ala

window.less.modifyVars({
  theme: 'someOtherValue'
})

En mi LESS estoy importando un archivo global theme.less que incluye

<strong i="14">@theme</strong>: undefined;

.setTheme {
  <strong i="15">@theme</strong> : 'default';
}
.setTheme;

También lo he intentado solo

<strong i="19">@theme</strong>: 'default';

En el primer caso, el valor de salida es undefined ; en el segundo caso, siempre es default independientemente de lo que esté configurado en modifyVars

Haz clic en el menú desplegable de temas aquí para ver un ejemplo.

El trabajo actual (que está en el sitio en vivo) es tomar manualmente la ruta de importación de variables y analizarla usando expresiones regulares y luego importar todas esas variables con modifyVars , como puede adivinar, esto es bastante complicado

@seven-phases-max Lo reduje a un caso de prueba muy específico con @import causando problemas.

En ambos casos suponga:

JavaScript

window.less.modifyVars({
  theme: 'changedValue'
});

En el primer caso en theme.less, outer está configurado correctamente en changedValue

componente.menos

<strong i="17">@import</strong> 'theme.less';

tema.menos

<strong i="22">@theme</strong>: 'default';
.outer {
  value: @theme; // value is set to changedValue
}

En este caso fallido, en theme.less @theme se establece en default y no changedValue

componente.menos

<strong i="32">@import</strong> 'theme.less';

tema.menos

<strong i="37">@theme</strong>: 'default';
<strong i="38">@import</strong> "@{theme}"; // theme is set to default not changedValue

En el caso de "var in mixin", el fragmento se convierte en:

// defaults:
.setTheme {
   <strong i="6">@theme</strong>: undefined;
}

// custom:
.setTheme {
  <strong i="7">@theme</strong>: 'default';
}
.setTheme;

Aunque para modifyVars siempre debería dar como resultado 'someOtherValue' para cualquiera de los fragmentos que haya probado (porque modifyVars funciona simplemente agregando una línea simple <strong i="14">@theme</strong>: 'someOtherValue'; justo al final de la compilación código fuente). Entonces, si modifyVars no funciona, lo más probable es que el problema esté en otra parte.
Miré el código de la página button pero es demasiado complejo para decir rápidamente qué podría estar mal. De un vistazo rápido, no puedo ver (o tal vez simplemente no puedo entender) cómo se vuelve a compilar el código Less y cómo se vuelve a aplicar el CSS resultante después modifyVars (en fragmentos simples se hace a través less.refreshStyles después modifyVars ).


Por cierto, por si acaso, ¿estás seguro de que no planeas usar is-default como algo como:

.setTheme when not(is-defined(@theme)) {
  <strong i="26">@theme</strong>: 'default';
}
.setTheme;

? Si es así, el problema es que dicho código violará directamente el principio de evaluación perezosa ( is-defined debería devolver falso si la variable no está definida, pero dado que _será_ definida en ese ámbito inmediatamente , is-defined también tiene para devolver verdadero -> recursividad irresoluble (para obtener más detalles sobre por qué las redefiniciones de tipo imperativo condicional no se pueden permitir de manera segura en Less, consulte el n.° 2072).
Es decir, en realidad podría funcionar como se esperaba (a menos que is-defined esté codificado para detectar y rescatar explícitamente dicha recurrencia), pero solo como un tipo de comportamiento no especificado/indefinido, sin coherencia con las reglas normales de visibilidad de variables, creando así incluso más desorden

@jlukic

<strong i="8">@theme</strong>: 'default';
<strong i="9">@import</strong> "@{theme}"; // theme is set to default not changedValue

Sí, esta también era una de mis sospechas (lamentablemente no puedo probar esto en este momento, ni puedo adivinar cómo podría suceder esto, pero sí, es posible porque el uso de variables en las importaciones es magia negra real. Crearé un emitir un informe si este es el problema).

Estoy bastante seguro de que tiene algo que ver con esa maravillosa magia negra.

He estado trabajando con menos archivos muy simples para depurar, sin embargo, aún no he tenido la oportunidad de crear un repositorio de casos de prueba.

En mis pruebas, la falla es específica del uso de valores {@variable} en la importación, pero no en las propiedades css.

Realmente disfruto de la tematización con menos, me encantaría superar estos problemas. Aquí hay un ejemplo de tema divertido con LESS que acabo de mostrar en Meteor DevShop la semana pasada (haga clic en el ícono del cubo de pintura en la parte superior derecha)

@jlukic Lo probé y modifyVars funciona para vars en las importaciones como se esperaba tanto con lessc como con less.js (vea la demostración de codepen , es un código un poco complicado allí ya que tenía que importar desde la url aterradora esencial , pero supongo que es igual a lo que se supone que deben realizar sus páginas ... Así que en realidad parece que el problema está en otra parte).

Gracias por tomarse el tiempo para producir el caso de prueba para mí. Esto es bastante claro. No estoy seguro de lo que me está pasando... Tendré que investigar.

¿Ha habido algún movimiento en esta solicitud? He estado ansioso por una verificación de variable indefinida desde hace algún tiempo. Por el momento, tengo un trabajo que está lejos de ser ideal...

p {
  & when not (@body-text-color = null) {
    color: @body-text-color;
  }
}

pero, para que esto funcione, la variable debe existir y estar definida como null por defecto. Esto sería mucho más efectivo/flexible si pudiera verificar si la variable existe en primer lugar.

+1

Entonces, en resumen: la implementación de una función is-defined(name) (y/o get-value-of(var-name, fallback-value-if-not-defined) ) dentro de un complemento necesita menos de 15 líneas de código. Así que solo haz eso si eres lo suficientemente valiente.

+1 a esto. ¿Hay algún progreso en esto como complemento? Quiero decir, @seven-phases-max conoces mejor el código que la mayoría de los colaboradores, supongo. Si requiere alrededor de 15 líneas de código, ¿por qué no se ha hecho todavía?

Si requiere alrededor de 15 líneas de código, ¿por qué no se ha hecho todavía?

¿Porque nadie está realmente interesado? Cada característica es genial y "extremadamente útil" cuando alguien más hace esto por ti... Pero mágicamente deja de ser tan importante cuando se trata de tu propio tiempo;)

conoces mejor el código que la mayoría de los colaboradores

Esto no significa que tenga que poner algo de mi propio interés después de algo de interés de otra persona, lo siento (especialmente si eso es algo alentador que yo personalmente trato como uso indebido/estilo de código incorrecto).

Para no sonar vacío, para el caso de uso de @ashenden : esta es la forma correcta de hacer que cualquiera de sus reglas sea personalizable por "desconocidos" (obviamente, asumiendo un caso en el que la anulación normal de CSS no es aplicable por alguna razón) sin el defecto idea de "mover a ciegas todos los valores de CSS a una variable más usar algo que no se use" (no entraré en detalles sobre por qué está roto, ya que es una historia para una larga entrada de blog).

Hay un concepto generalmente llamado "ganchos", por lo que el siguiente "no puedo decidirme por el festival":

// .............................................
// base code:

p {
    & when not (is-defined(@body-text-color)) {
        color: @body-text-color;
    }
    & when not (is-defined(@body-text-color)) {
        background-color: @body-back-color;
    }
}

span {
    & when not (is-defined(@body-text-color)) {
        color: @body-text-color;
    }
    & when not (is-defined(@body-text-color)) {
        background-color: @body-back-color;
    }
}

// .............................................
// customization:

@body-back-color: blue; // how about gradient?

debe ser reemplazado con:

// .............................................
// base code:

p {
    .body-text();
    .body-back();
    // ^ it's actually better to group all this into a singe entity, e.g. .p()
    // so that as you don't know WHAT or HOW to be customized
    // you don't pre-enforce any limitations and/or hardcoded approach
    // for something YOU do not even write
}

span {
    .body-text();
    .body-back();
}

.body-text() {}
.body-back() {}

// .............................................
// customization:

.body-back() {background-color: blue}

Cuenta las líneas, cuenta las posibilidades, cuenta las limitaciones.
Es decir, si no utiliza algunas propiedades CSS, déjelas en paz para aquellos que sí lo hagan .

Chicos, aquí es donde me encontré con esto, miren este ejemplo y díganme si es un caso válido y no se puede arreglar sin is-undefined.

Quiero cargar temas en una biblioteca como un módulo así:

En mi aplicación principal, defino la variable @theme : 'amarillo' y luego importo la biblioteca menos.

Y así es como se vería biblioteca menos en este caso:

@import 'temas/@{tema}';

cuerpo {
fondo: @primaryColor;
}

De esta manera podría tener yellow.less y default.less con @primaryColor : amarillo y @primaryColor : rojo.

Al final, puedo escribir mi biblioteca usando nombres de variables semánticas como color primario y proporcionar un conjunto de temas con esas variables definidas. Y el usuario de la biblioteca simplemente definiría el nombre del tema en su aplicación e importaría los estilos de la biblioteca.

Ok, no importa, parece que lo tengo, solo necesito una mezcla de importación como esta:

<strong i="6">@theme</strong>: 'default';

.imports(@theme) when (<strong i="7">@theme</strong> = 'yellow') {
    <strong i="8">@import</strong> "themes/yellow";
}
.imports(@theme) when (<strong i="9">@theme</strong> = 'default') {
    <strong i="10">@import</strong> "themes/default";
}
.imports(@theme);

@waterplea Puede simplificar su código para:

<strong i="7">@theme</strong>: default;

.imports(yellow) {<strong i="8">@import</strong> "themes/yellow";}
.imports(default) {<strong i="9">@import</strong> "themes/default";}
.imports(@theme);

también tenga en cuenta las comillas redundantes eliminadas.

Aunque para ser honesto, no puedo ver cómo <strong i="13">@theme</strong>: yellow; (+ todo el código de mixins) es mejor que solo la línea explícita <strong i="15">@import</strong> "themes/yellow"; . Es decir, en primer lugar, ¿eres consciente de la anulación ? Es decir, no necesita ocultar <strong i="18">@import</strong> "themes/default"; predeterminado, si el usuario de su biblioteca necesita aplicar cosas yellow , ella solo importa su tema amarillo (en cualquier lugar después de su archivo de biblioteca principal, y una vez más es el misma línea) y voilà - su biblioteca usa valores variables especificados en el archivo amarillo.

Terminé con una importación opcional de un archivo que anula el tema o cambia a un tema preestablecido. En un proyecto que usa la biblioteca, luego configuramos el paquete web para usar un alias para ese archivo como un módulo de nodo si el proyecto necesita un tema diferente. Lo que escribí antes o lo que escribiste tú no funcionaría en nuestro caso, ya que estamos construyendo un kit de interfaz de usuario para Angular y es altamente modular. Podemos importar solo un botón o seleccionar el control y tiene que ser consciente del tema que estamos usando con todos sus estilos encapsulados. La solución es fácil de configurar, así que estoy contento con ella.

lo que escribió no funcionaría en nuestro caso, ya que estamos creando un kit de interfaz de usuario para Angular y es altamente modular.

He visto esto infinitas veces antes, es uno de los problemas más tristes de Menos (malentendidos) - la gente simplemente pierde el principio fundamental de evaluación Menos perezoso y, en lugar de usar el Less anulado natural, construye bibliotecas con una inyección pesada de archivos- patrones de personalización basados ​​en hábitos de lenguajes con semántica variable directamente opuesta (como PHP, Sass, etc.). Debe ser algo más que un tipo que grita continuamente "¡Chicos, lo hacéis mal!"

Me encantaría hacerlo bien, no estoy afirmando que entiendo todo :) ¿Te importaría explicar tu sugerencia en mi ejemplo? Esto es lo que tenemos:

Aquí hay un ejemplo estructural mínimo:

Biblioteca:

  • sin importación
    <strong i="10">@import</strong> 'theme-default';
    <strong i="13">@import</strong> 'mixins';
  • mixins.less
    some mixins here like resetting default browser button styles
  • tema-default.less
    <strong i="20">@primary</strong>: red;
    <strong i="23">@secondary</strong>: blue;
  • tema-secundario.menos
    <strong i="27">@primary</strong>: green;
    <strong i="30">@secondary</strong>: yellow;
  • botónComponente
    <strong i="34">@import</strong> 'import';
    .button { color: @primary; }

Proyecto 1 (debe usar el tema predeterminado):

  • componente
    <strong i="42">@import</strong> 'import';
    body { background: @secondary; }

Proyecto 2 (debe usar tema secundario):

  • componente
    <strong i="50">@import</strong> 'import';
    body { background: @secondary; }

Nuestra biblioteca se agrega como un módulo de nodo y dentro de los proyectos importamos solo el componente de botón de la biblioteca. Dado que es un módulo Angular para ese botón con su propio archivo menos, se compila por separado del resto del Proyecto 1 o Proyecto 2.

He estado pensando en esto por un tiempo y no pude encontrar una manera de organizar menos archivos para permitir que el Proyecto 1 o el Proyecto 2 establezcan qué archivo de tema usar al compilar los componentes y el propio código del Proyecto. No veo una forma de anular los estilos para el componente de botón desde el código de Project.

Entonces, básicamente, así es como escribí import.less:
<strong i="58">@import</strong> 'theme-default';
<strong i="61">@import</strong> 'mixins';
<strong i="64">@import</strong> (optional) '~custom-theme';

Y dentro de Proyectos, si quiero anular las variables de tema tanto para el código de Proyectos como para los componentes de la biblioteca, aliaso este archivo como módulo usando el paquete web. Este archivo podría tener una anulación explícita de, por ejemplo, @primary o simplemente un cambio al tema secundario:
<strong i="69">@import</strong> '~myLibrary/styles/theme-secondary';

Entonces, sé sobre la anulación, el problema es que el usuario de la biblioteca no tiene forma de interponerse entre "en cualquier lugar después de su archivo de biblioteca principal" y el código del componente de la biblioteca. Entiendo que lo que dices funcionaría para marcos sin encapsulación de estilos, pero para Angular no funcionaría o no entendí algo en tu solución.

Veo. Entonces en realidad estamos hablando de lo mismo (solo que con diferentes palabras). Tan pronto como buttonComponent (o cualquier otro componente) se compila por separado, "el maestro de personalización del proyecto" sigue siendo el archivo import.less e hizo exactamente lo que sugerí. (mientras que Project 1 y Project 2 se convierten en otros componentes "cualquier otra cosa" (¿o "todo en uno"?) del mismo nivel que buttonComponent y no todo un "proyectos").

Es decir, de esta manera diría que "lo hiciste bien" a mi gusto.

Este problema se ha marcado automáticamente como obsoleto porque no ha tenido actividad reciente. Se cerrará si no se produce más actividad. Gracias por sus aportaciones.

@stale ping.

Técnicamente, un complemento que implementa la función está disponible desde 2015 . Pero personalmente nunca lo probé, así que no me culpes si algo sale mal (es solo para tu información).

Este problema se ha marcado automáticamente como obsoleto porque no ha tenido actividad reciente. Se cerrará si no se produce más actividad. Gracias por sus aportaciones.

Entonces... Revisé este hilo y realmente no puedo determinar si se trata principalmente de casos de personas que malinterpretan el alcance de Less o no.

Realmente no veo la necesidad de verificar si los vars están definidos. Vars debe definirse si se van a consumir.

<strong i="7">@import</strong> "library";  // contains @library-color: blue;

@library-color: red;

.box {
  color: @library-color;
}

No entiendo la necesidad de establecer condicionalmente una propiedad basada en el valor _cambio_. Si la propiedad se establece en una variable, simplemente cambie el valor de la variable.

Es decir, si library.less tiene esto:

@library-color: blue;
.box {
  color: @library-color;
}

Todo lo que alguien tendría que hacer para establecer su salida:

<strong i="16">@import</strong> "library";
@library-color: red;

Esto daría como resultado:

.box {
  color: red;
}

¿El OP y otros en ese hilo entienden ese comportamiento?

Creo que el caso de uso de "existe var" es una buena manera de hacer banderas. Es decir, no creo que haya una "falsedad" conceptualmente directa. O más bien, es inusual que true sea el único valor "veraz". En otras palabras, creo que es una cuestión de educación, más que un problema de comportamiento.

Creo que deberíamos considerar cerrar este problema, ya que esto generalmente se soluciona con una línea que declara el valor predeterminado de la variable. Aunque con Less v3.5 avanzando hacia un trato más permisivo, ¿quizás las variables dentro de los guardias se evalúen de manera más permisiva?

Aunque mi voto es no. @the-variable: false; parece ser todo lo que OP necesita agregar.


Para cualquier persona que tenga preguntas sobre cómo resolver un problema específico que crea que está relacionado con esto, siéntase libre de publicar en less gitter y @ me.

Ok cerrando, gracias @calvinjuarez.

Lo que también sucedió desde que se abrió es la función if() . Entonces puedes hacer color: if((@variable), green, red);

@matthew-dean Muchas gracias por escribirnos con este consejo. Vi la adición de if() en las notas de la versión 3.0, pero no hice la conexión con este problema, para el cual tenía un caso único (pero no el momento para hacer una versión reducida). De nuevo, muy apreciado. Bien hecho, equipo LESS.

@kbav ¡No hay problema! Otro consejo además de ese consejo: cuando if() se introdujo por primera vez, requería paréntesis en torno a las condiciones porque básicamente estaba "incrustando" una protección cuando (como en la parte después when en when (@variable) ). Sin embargo, eso se solucionó a partir de Less 3.6, por lo que puede escribir el ejemplo anterior sin paréntesis (si compila con la última versión):

color: if(<strong i="10">@variable</strong>, green, red);
¿Fue útil esta página
0 / 5 - 0 calificaciones