Передача переменной из объявления миксина в прикрепленный блок контента?

В Ruby вы можете легко передать переменную из метода в прикрепленный блок кода:

def mymethod
  (1..10).each { |e| yield(e * 10) } # Passes a number to associated block
end

mymethod { |i| puts "Here comes #{i}" } # Outputs the number received from the method

Я хотел бы сделать то же самое в миксине SASS:

=my-mixin
  @for $i from 1 to 8
    .grid-#{$i}
      @content

+my-mixin
  color: nth("red green blue orange yellow brown black purple", $i)

Этот код не будет работать, потому что $i объявлен внутри объявления примеси и не виден снаружи, где используется примесь. :(

Итак... Как мне использовать переменные, объявленные внутри объявления миксина?

Когда я работаю с сеткой и медиа-запросами, мне очень нужна эта функциональность. В настоящее время я должен дублировать то, что находится внутри объявления миксина, каждый раз, когда мне это нужно, нарушая правило DRY.

Обновление от 24 января 2013 г.

Вот реальный пример.

У меня есть миксин, который циклически проходит через точки останова и применяет предоставленный код один раз для каждой точки останова:

=apply-to-each-bp
  @each $bp in $bp-list
    +at-breakpoint($bp) // This is from Susy gem
      @content

Когда я использую этот миксин, я должен использовать это значение $bp внутри @content. Это может быть так:

// Applies to all direct children of container
.container > *
  display: inline-block

// Applies to all direct children of container,
// if container does not have the .with-gutters class
.container:not(.with-gutters) > *
  +apply-to-each-bp
    width: 100% / $bp

// Applies to all direct children of container,
// if container has the .with-gutters class
.container.with-gutters  > *
  +apply-to-each-bp
    $block-to-margin-ratio: 0.2
    $width: 100% / ($bp * (1 + $block-to-margin-ratio) - $block-to-margin-ratio)
    width: $width
    margin-right: $width * $block-to-margin-ratio

    &:nth-child(#{$bp})
      margin-right: 0

Но это не сработает, потому что значение $bp недоступно внутри @content.

Объявление переменной перед вызовом миксина не поможет, потому что @content анализируется один раз и до того, как анализируется миксин.

Вместо этого КАЖДЫЙ раз, когда мне это нужно, мне приходится делать два уродливых бедра:

  1. объявить специальный миксин,
  2. напишите цикл, нарушая принцип DRY:
// Each of the following mixins is mentioned in the code only once.
=without-gutters($bp)
  width: 100% / $bp

=with-gutters($bp)
  $block-to-margin-ratio: 0.2
  $width: 100% / ($bp * (1 + $block-to-margin-ratio) - $block-to-margin-ratio)
  width: $width
  margin-right: $width * $block-to-margin-ratio

  &:nth-child(#{$bp})
    margin-right: 0

// Applies to all direct children of container
.container > *
  display: inline-block

// Applies to all direct children of container,
// if container does not have the .with-gutters class
.container:not(.with-gutters) > *
  @each $bp in $bp-list
    +at-breakpoint($bp) // This is from Susy gem
      +without-gutters($bp)

// Applies to all direct children of container,
// if container has the .with-gutters class
.container.with-gutters  > *
  @each $bp in $bp-list  // Duplicate code! :(
    +at-breakpoint($bp)  // Violates the DRY principle.
      +with-gutters($bp)

Итак, вопрос: есть ли способ сделать это в стиле Ruby?


person Andrey Mikhaylov - lolmaus    schedule 27.12.2012    source источник


Ответы (3)


Переменные в Sass имеют область действия. Они видны только в том блоке, в котором были созданы. Если вы хотите, чтобы переменная была доступна как внутри, так и вне миксина, она должна быть определена в глобальной области видимости:

$var: 0;

@mixin test {
    $var: $var + 1;
    color: red;
}

.test {
    $var: 5;
    @include test;
    @debug $var; // DEBUG: 6
}

Пока вы не заботитесь о состоянии $var очень долго, это должно работать нормально для ваших целей.

Для вашего примера это не сработает, потому что похоже, что сначала обрабатывается @content. Вам нужен миксин, который написан по-другому:

@mixin test($properties...) {
    @for $i from 1 to 8 {
        .grid-#{$i} {
            @each $p in $properties {
                $list: nth($p, 2);
                @if length($list) > 1 {
                    #{nth($p, 1)}: nth($list, $i);
                } @else {
                    #{nth($p, 1)}: $list;
                }
            }
            @content;
        }
    }
}

.test {
    @include test(color (red green blue orange yellow brown black purple));
}

Сгенерированный CSS:

.test .grid-1 {
  color: red;
}

.test .grid-2 {
  color: green;
}

.test .grid-3 {
  color: blue;
}

.test .grid-4 {
  color: orange;
}

.test .grid-5 {
  color: yellow;
}

.test .grid-6 {
  color: brown;
}

.test .grid-7 {
  color: black;
}

Подобный миксин может иметь любое количество аргументов и при этом позволяет вам использовать @content, если хотите.

person cimmanon    schedule 27.12.2012
comment
Уважаемый cimanon, предложенное вами решение не относится к ситуации. Я отредактировал свой первоначальный вопрос, добавив подробный и почти реальный пример того, что я ищу. Пожалуйста, посмотрите и напишите свои соображения. - person Andrey Mikhaylov - lolmaus; 24.01.2013

Я сам столкнулся с этой проблемой, и, насколько мне известно, это текущее ограничение в SASS.

person Phil Thomas    schedule 10.06.2013

Так что в настоящее время это недоступно в Sass.

В очереди задач Sass есть соответствующий тикет: https://github.com/nex3/sass/issues/871 Он находится в запланированном состоянии, но, вероятно, не будет выпущен по крайней мере до Sass 4.0.

person Andrey Mikhaylov - lolmaus    schedule 17.01.2014