Управление видимостью компонента на основе пользовательских утверждений с использованием директивы Angular Structural

В своей предыдущей статье я попытался показать один из способов реализации авторизации на основе утверждений в angular с помощью Router Guards. если вы не читали эту статью, я предлагаю вам пройти ее и вернуться сюда, так как большинство компонентов и сервисов, которые мы собираемся использовать, описаны в этой статье.

В предыдущей статье основное внимание уделялось защите пользователей от перехода на всю страницу, на которую они не имеют права. Это один из шагов реализации авторизации, мы также хотим скрыть элементы в определенном разделе нашего приложения, в котором у них нет разрешения на использование или просмотр.

Это то, чего мы собираемся достичь в этой статье, мы будем скрывать или показывать определенные элементы или разделы на нашей странице, используя пользовательскую директиву angular, даже если пользователь авторизован для просмотра страницы. например. в этом случае пользователь имеет право просматривать список всех записей о сотрудниках, но не имеет права редактировать записи о сотрудниках. в этом случае мы хотим полностью скрыть кнопку редактирования из представления, даже если у нас есть защита маршрута, не позволяющая ему/ей перейти к представлению редактирования; вы можете рассматривать это как дополнительный уровень безопасности.

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



Или посмотреть развернутую версию приложения здесь.

Примечание: при переключении между ролями вам необходимо обновить браузер, чтобы увидеть эффект.

Предпосылки

поэтому, прежде чем приступить к работе над этим проектом, убедитесь, что у вас есть все необходимые условия для вашей среды разработки. Чтобы выполнить то, что мы хотим, мы не будем полагаться на внешние зависимости, мы будем использовать только инструменты, предоставляемые angular:

  • Node.js ≥ 10.x
  • Угловой интерфейс командной строки ≥ 7.x

Теперь, когда у вас есть все предварительные условия в вашей среде разработки, клонируйте репозиторий, который мы создали в предыдущей статье, чтобы мы могли изменить и добавить к нему функцию видимости:

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

cd angular-authenntication-directive
npm i
ng serve -o

после выполнения следующих команд и завершения компиляции вы должны увидеть, что окно браузера открывается с видом, подобным этому:

Обзор проекта

У нас есть три типа пользователей и три раздела в приложении. раздел домашней страницы, раздел пользователя и раздел администратора соответственно.

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

Начало работы

Теперь мы можем приступить к реализации нашей пользовательской директивы для реализации функции видимости, которую мы хотим добавить. откройте приложение в своей любимой IDE и попытайтесь понять его базовую структуру и то, как каждый компонент взаимодействует друг с другом.

Директивы используются для управления DOM на основе определенного условия. они используются для расширения возможностей HTML, придавая ему новый синтаксис. У каждой директивы есть имя. Angular предоставляет предопределенные директивы из коробки, такие как ng-if, ng-for, ng-hide и т. д., или вы можете создать собственную директиву, которая может называться как угодно.

В Angular есть три вида директив:

  1. Компоненты — директивы с шаблоном.
  2. Структурные директивы — изменение макета DOM путем добавления и удаления элементов DOM.
  3. Директивы атрибутов — изменение внешнего вида или поведения элемента, компонента или другой директивы.

Подробное описание директивы см. в официальном официальном документе.

Для нашей цели мы собираемся использовать директиву Structural для отображения или скрытия элементов из представления на основе утверждения пользователя.

теперь давайте создадим пользовательскую директиву, которую мы назовем директивой HasClaim, поскольку ее цель — проверить, есть ли у пользователя претензия или нет:

ng generate directive HasClaim

мы можем видеть, что сгенерированный CLI код выглядит примерно так.

import { Directive } from '@angular/core';
@Directive({ 
      selector: '[appHasClaim]'
  })
export class HasClaimDirective {
   constructor() { }
}

Обратите внимание, что имя селектора имеет префикс app, который angular включает по умолчанию, чтобы не возникало конфликтов с HTML и другими атрибутами библиотеки, вы можете изменить его на любое значение, которое хотите.

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

Чтобы выполнить это требование, нам нужно использовать несколько сервисов, введенных в нашу директиву.

  • TemplateRef‹any› от @angular/core используется для создания экземпляра элемента в нашем представлении с помощью другого сервиса ViewContainerRef .
  • ViewContainerRefиспользуется для вставки ссылки на элемент с помощью TemplateRef‹any›внутри нашего представления.
  • SecurityService –это служба, которую мы создали в предыдущей статье, и которая будет использоваться для проверки того, есть ли у пользователя претензия или нет, используя метод hasClaim.

давайте добавим указанный выше сервис внутрь конструктора и добавим оператор импорта для каждого пакета.

import { TemplateRef, ViewContainerRef} from "@angular/core";
import { SecurityServiceService } from "./security-service.service";
---
constructor(
      private templateRef: TemplateRef<any>,
      private viewContainer: ViewContainerRef,
      private securityService: SecurityServiceService
) {}
---

затем нам нужен какой-то способ передать значения утверждений нашему компоненту, повезло с тем, что angular нас покрыл; мы можем использовать декоратор @Input для передачи данных вниз по дереву компонентов. идем дальше и определяем его в нашей пользовательской директиве следующим образом:

@Input("appHasClaim") set hasClaim(claimType: any){}

мы только что определили Input, но еще не определили все его функции. мы дали нашему вводу псевдоним; внутри директивы он известен как appHasClaim, но снаружи мы используем hasClaim, чтобы прикрепить его к любому элементу HTML.

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

@Input("appHasClaim") set hasClaim(claimType: any) {
    this.securityService.hasClaim(claimType).subscribe(e => {
         if (e) {
          this.viewContainer.createEmbeddedView(this.templateRef);
         } else {
          this.viewContainer.clear();
         }
   });
}

Первое, что мы сделали, это подписались на функцию hasClaim службы безопасности, которую мы создали ранее, и передали ей значение претензии. на основе результата, который мы получаем, если у пользователя установлено значение true, мы вводим/добавляем элемент внутри представления, иначе мы удаляем его из представления.

Вот и все!!!, мы создали нашу пользовательскую директиву, теперь осталось только начать ее использовать. убедитесь, что вы объявили его внутри AppModule, прежде чем начать его использовать.

@NgModule({
...     
  declarations: [ 
...
HasClaimDirective 
...
],
... 
})

Перейдите к src/app/app.component.html и обновите его, включив пользовательскую директиву, которую мы создали для скрытия или отображения элементов.

...
    <li class="nav-item active">
       <a class="nav-link"  routerLink="/admin"
          *appHasClaim="'canViewAdminSection'"
       >
   Admin
       </a>
   </li>
   <li class="nav-item active">
      <a class="nav-link" routerLink="/user"
         *appHasClaim="'canViewUserSection'"
       >
   User
     </a>
  </li>
....
  <button type="button" class="btn btn-primary m-2"
     *appHasClaim="'canViewAdminSection'"
      routerLink="/admin"
   >
       Go To Admin
  </button>
  <button  type="button"  class="btn btn-secondary m-2"
     *appHasClaim="'canViewUserSection'"
      routerLink="/user"
   >  
      Go to User
  </button>

Я удалил весь ненужный код и включил только ту часть, которую нам нужно изменить, чтобы наша директива вступила в силу. Обратите внимание на символ *, добавленный перед именем директивы, это не ошибка опечатки, убедитесь, что вы добавили его, чтобы angular мог определить его как директиву, иначе вы получите ошибку StaticInjector.

Мы закончили, убедитесь, что вы все сохранили, и выполните следующую команду, чтобы запустить приложение.

ng serve -o

Окончательный класс структурных директив должен выглядеть так

вы можете найти полный код в моем Github Repository

Справочник