Angular: как добавить глобальный CSS (например, в тело), ​​но только для одной конкретной страницы?

Как я могу добавить отдельный CSS для одной страницы в Angular?

Это CSS, который мне нужен, согласно Как удалить URL-адрес со страницы печати?:

@media print{
    @page{
        margin:0;
    }
    body{
        margin:30px;
    }
}

Но включение CSS в компонент с ::ng-deep или ViewEncapsulation.None здесь не поможет, потому что при переходе со страницы CSS страницы не удаляется.

Я добавил Stackblitz , что ясно объясняет проблему.


Я нашел возможное решение, но оно не работает:

  encapsulation: ViewEncapsulation.None

   ...      

  constructor(private renderer: Renderer2) {
    this.renderer.addClass(document.body, 'special-print');
   }

  ngOnDestroy() {
    this.renderer.removeClass(document.body, 'special-print');
  }
  ....
  ....
  ....
  @media print{
    @page{
        margin:0;
    }
    body.special-print{
        margin:30px;
    }
  }

Почему не работает:

Хотя это поможет с <body> CSS, но не поможет с @page CSS. Возможно, вопрос лучше сформулировать как «Как добавить глобальный CSS, но удалить его, когда мы покидаем страницу?».


person Eliezer Berlin    schedule 11.11.2020    source источник
comment
Попробуйте использовать таблицу стилей компонентов для отдельного стиля конкретной страницы, которая не влияет на стили глобально.   -  person Awais    schedule 11.11.2020
comment
Затем следуйте методу привязки / инкапсуляции, упомянутому в этом stackoverflow.com/questions/34881401/   -  person Awais    schedule 11.11.2020
comment
@Awais Это решение будет работать для <body>, но не будет работать для других глобальных CSS, таких как @page. (Если вы посмотрите CSS, который я написал в своем посте, вы поймете, что я имею в виду.)   -  person Eliezer Berlin    schedule 11.11.2020
comment
Вы можете посмотреть этот ответ на аналогичный вопрос, который я задал stackoverflow.com/a/64672874/13680115. Я считаю, что ответ должен работать и в вашем случае, он сработал для меня   -  person Owen Kelvin    schedule 15.11.2020


Ответы (4)


Решено!

Мы печатаем блок <style> непосредственно в HTML компонента, и поэтому, когда компонент удаляется, наш блок <style> также удаляется. (Обычно это не сработает, но благодаря DomSanitizer.bypassSecurityTrustHtml Angular не нарушит наш код при выполнении оптимизаций.)

Вот StackBlitz.

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

component.ts: (This is all we need. We don't need an HTML or style.css file.)
//Inside  your local component, place this HTML 
//<app-local-css [style]="'body{background:green !important;}'"></app-local-css>
// OR
//<app-local-css [scriptURL]="'/path/to/file.css'"></app-local-css>

@Component({
  selector: "app-local-css",
  template: '<span style="display:none" [innerHTML]="this.safeString"></span>'
})
export class LocalCSSComponent implements OnInit {
  constructor(protected sanitizer: DomSanitizer) {}
  @Input() scriptURL?: string;
  @Input() style?: string;

  safeString: SafeHtml;
  ngOnInit() {
    if (this.scriptURL) {
      let string = '<link rel="stylesheet" type="text/css" href="' + this.scriptURL + '">';
      this.safeString = this.sanitizer.bypassSecurityTrustHtml(string);
    } else if (this.style) {
      let string = '<style type="text/css">' + this.style + "</style>";
      this.safeString = this.sanitizer.bypassSecurityTrustHtml(string);
    }
  }
}

А затем используйте это так:

mySample.component.html:

<app-local-css [style]="'body{background:green !important;}'"></app-local-css>
// OR
<app-local-css [scriptURL]="'/path/to/file.css'"></app-local-css>
person Eliezer Berlin    schedule 17.11.2020

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

1. Отдельная страница

Вы можете создать другую страницу с Angular или без нее, которая включает нужный вам CSS и загрузить эту страницу. При самом упрощенном подходе к этому другая страница будет иметь другой URL-адрес. Если вам не нравится другой URL-адрес, вы можете скрыть содержимое своей страницы и показать другую страницу внутри iframe. По общему признанию, это было бы хакерским решением, но это решение.

2. Отрисовка CSS на стороне клиента

Вместо простой загрузки CSS у вас может быть компонент, который будет управлять глобальными правилами CSS, соответствующими имени вашего представления. У вас будет значение шаблона, отображаемое для свойства, например:

@media print{
    @page{
        margin:0;
    }
    body{
        margin:30px;
    }
}

И когда вы посещаете страницу, где это необходимо активировать, вы просто инициализируете свойство с помощью style HTML-элемента, который был создан на основе шаблона и добавлен в head. Как только вы покинете данное представление, ваш компонент обнаружит это событие и remove() этот элемент. Если вы выберете это решение, было бы разумно убедиться, что вы поддерживаете его в более общих условиях, так что если некоторые новые представления будут иметь свой собственный глобальный CSS, то их будет легко интегрировать в ваш проект в будущем. .

3. классы тела

Вы можете добавить / удалить какой-нибудь custom-print или любой другой класс в / из body всякий раз, когда нужно изменить стиль. Таким образом, вы можете добавить CSS ровно один раз в свой HTML и соответствующим образом изменить правила, например:

body.custom-print {
    margin: 30px;
}

Это было бы изящным решением, но проблема в вашем случае заключается в том, что у вас также есть правило @page, и я не уверен, как вы можете сделать это зависимым от классов тела или некоторых других атрибутов HTML. На вашем месте я бы провел немало экспериментов по этому поводу.

4. Постановка iframe

Вы могли бы избежать этого CSS на своей главной странице, но у вас будет скрытый iframe, где у вас будет CSS, и вы просто скопируете содержимое в CSS, а после его загрузки распечатайте его.

person Lajos Arpad    schedule 17.11.2020
comment
1 и 4 в основном одинаковы. Поместите контент в iframe ... Это довольно радикально, но технически будет работать. # 3 Я уже написал часть вопроса и отклонил его по той же причине, что и вы. №2 кажется единственным разумным решением, и мы, вероятно, тоже можем написать какой-нибудь сервис для этого. (Вместо того, чтобы помещать CSS в файл HTML или CSS, просто поместите CSS в свой .ts файл и используйте JS, чтобы добавить его в ngInit и удалить в ngDestroy.) Это интересная идея. - person Eliezer Berlin; 18.11.2020

Не меняйте все тело с яблока. Вместо этого необходимо внести несколько изменений.

  1. В компоненте приложения удерживайте логическое значение, указывающее, используете ли вы яблоко, и используйте ngClass для класса, определенного в scss.

  2. Отслеживайте, по какому маршруту вы находитесь, в appComponent и соответствующим образом установите isApple.

  3. Добавьте div вокруг всего вашего html, чтобы контейнер принял полный размер

  4. Добавьте глобальный html, настройку высоты тела до 100%, чтобы вы видели цвет везде

  5. Убрать перекрытие тела в яблоке

Итак, appComponent.ts:

  isApple: Boolean;
  constructor(router: Router) {
    router.events.subscribe(v => {
      if (v instanceof NavigationEnd) {
        this.isApple = v.url === "/apple";
      }
    });
  }

appComponent.html:

<div [ngClass]="{'red':isApple}" class="container">
    <p>
        There are two components: Apple and Banana. Switching between them will show
        the problem.
    </p>

    <router-outlet></router-outlet>
</div>

appComponent.scss


.red {
  background-color: red;
}

.container {
  height: 100%;
}

apple.component.scss (удалить тело)

/*Sample "global" CSS, that affects something outside the current component.*/
::ng-deep {
  @media print {
    @page {
      margin: 0;
    }
  }
}

styles.scss (глобальный)

html, body {
  height: 100%;
}

Вы можете полностью увидеть это по этой ссылке Stackblitz < / а>

person PMO1948    schedule 17.11.2020
comment
.... Вся цель состоит в том, чтобы поместить CSS в тело. (В контексте того, почему нам может понадобиться глобальный CSS, реальный сценарий, который вдохновил меня на вопрос, скрывает <header> и <footer> на определенной странице во время печати .... без изменения кода app.component.html, поскольку у нас есть МНОГО страниц, и только один компонент имеет это требование.) - person Eliezer Berlin; 17.11.2020
comment
В любом случае, ваш код ничего не решает, когда дело касается печати @media. У нас осталась та же проблема, что и раньше. - person Eliezer Berlin; 17.11.2020

Вы можете добавлять в компонент разные css файлы (например, app-task.component.ts):

@Component({
  selector: 'app-task',
  templateUrl: './app-task.component.html',
  styleUrls: ['./app-task.component.scss', './styles2.scss', './styles3.scss']
})

В этом примере файлы стилей находятся в той же папке, что и компонент, но это не лучший вариант: например, вы должны поместить файлы в активы. Также будьте осторожны с нитью стилей, так как первый, который вы поместите, будет помещен перед вторым (очевидно).

person Carlos Bayarri Cebrecos    schedule 11.11.2020
comment
Вся проблема в том, что эти стили не удаляются при выгрузке страницы. Мне нужен способ добавления стилей, которые НЕ будут сохраняться от страницы к странице. - person Eliezer Berlin; 11.11.2020
comment
Angular ограничивает CSS конкретным компонентом. Например, если я работаю с app-mybutton, то angular автоматически добавит app-mybutton ко всему CSS, написанному внутри этого компонента. Это отлично подходит для поддержания чистоты кода, но не очень помогает, когда я пытаюсь изменить <body>. - person Eliezer Berlin; 11.11.2020
comment
(Ну, чтобы быть более ясным, он фактически добавляет некоторые [ng-content] вещи в CSS, но [ng-content] эффективно сохраняет весь CSS, который вы влияете только на этот один компонент.) - person Eliezer Berlin; 11.11.2020
comment
Я создал Stackblitz, чтобы улучшить показать проблему. - person Eliezer Berlin; 17.11.2020