<?xml version="1.0" encoding="UTF-8"?>

<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" >

  <channel>
    <title><![CDATA[Все посты подряд / Angular / Хабр]]></title>
    <link>https://habr.com/ru/hubs/angular/posts/</link>
    <description><![CDATA[Angular – javaScript-фреймворк]]></description>
    <language>ru</language>
    <managingEditor>editor@habr.com</managingEditor>
    <generator>habr.com</generator>
    <pubDate>Fri, 24 Apr 2026 15:56:50 GMT</pubDate>
    
    
      <image>
        <link>https://habr.com/ru/</link>
        <url>https://habrastorage.org/webt/ym/el/wk/ymelwk3zy1gawz4nkejl_-ammtc.png</url>
        <title>Хабр</title>
      </image>
    

    
      
        
    

  

  
  <item>
    <title><![CDATA[Пост @MaxRokatansky — Блог компании OTUS (+3) — N/P]]></title>
    <guid isPermaLink="true">https://habr.com/ru/companies/otus/posts/1018662/</guid>
    <link>https://habr.com/ru/companies/otus/posts/1018662/?utm_campaign=1018662&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p><strong>5 демо-уроков апреля для тех, кто хочет роста в ИТ</strong></p><figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/653/c09/f0b/653c09f0ba3ab339fce26382b47007d7.png" width="1695" height="927"></figure><p>Привет, Хабр. Эти уроки проводят преподаватели курсов Отус в преддверии старта новых потоков. На них можно узнать о формате обучения, пообщаться с экспертами и заодно закрыть пробелы в знаниях по интересующей теме. Участие бесплатное. Присоединяйтесь.</p><ul><li><p>9 апреля, 20:00. <a href="https://otus.pw/OQXV/" rel="noopener noreferrer nofollow">Angular без RxJS? Пишем реактивные формы на сигналах</a><br><sup>Открытый урок&nbsp;курса </sup><a href="https://otus.pw/VRM3/" rel="noopener noreferrer nofollow"><sup>«Angular-разработчик»</sup></a></p></li><li><p>13 апреля, 20:00. <a href="https://otus.pw/JaUl/" rel="noopener noreferrer nofollow">Flutter GenUI: когда ИИ-агент собирает ваш интерфейс</a><br><sup>Открытый урок&nbsp;курса </sup><a href="https://otus.pw/Jllz/" rel="noopener noreferrer nofollow"><sup>«Flutter-разработчик»</sup></a></p></li><li><p>15 апреля, 18:00. <a href="https://otus.pw/xWsu/" rel="noopener noreferrer nofollow">Основы протокола HTTP</a><br><sup>Открытый урок&nbsp;курса </sup><a href="https://otus.pw/Nz1X8/" rel="noopener noreferrer nofollow"><sup>«Проектирование API»</sup></a></p></li><li><p>20 апреля, 20:00. <a href="https://otus.pw/IQF7L/" rel="noopener noreferrer nofollow">От интерфейса до корзины: создаём мини интернет-магазин на языке JavaScript</a><br><sup>Открытый урок&nbsp;курса </sup><a href="https://otus.pw/lbiBq/" rel="noopener noreferrer nofollow"><sup>«JavaScript-разработчик. Базовый уровень»</sup></a></p></li><li><p>21 апреля, 20:00. <a href="https://otus.pw/5fzz/" rel="noopener noreferrer nofollow">Архитектура Angular-приложения: как писать масштабируемый frontend</a><br><sup>Открытый урок&nbsp;курса </sup><a href="https://otus.pw/VRM3/" rel="noopener noreferrer nofollow"><sup>«Angular-разработчик»</sup></a></p></li></ul><blockquote><p>Полный список бесплатных уроков апреля смотрите <a href="https://otus.pw/yX73/" rel="noopener noreferrer nofollow">в дайджесте.</a></p></blockquote> <a href="https://habr.com/ru/posts/1018662/?utm_campaign=1018662&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Fri, 03 Apr 2026 11:50:26 GMT</pubDate>
    <dc:creator><![CDATA[MaxRokatansky (OTUS)]]></dc:creator>
      
      <category><![CDATA[подборка уроков]]></category><category><![CDATA[бесплатные уроки]]></category><category><![CDATA[карьера в ит]]></category><category><![CDATA[профессиональное развитие]]></category><category><![CDATA[frontend разработка]]></category><category><![CDATA[lifelong education]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @AlekseyVY — Angular (+3) — 25.01.2026 22:01]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/988838/</guid>
    <link>https://habr.com/ru/posts/988838/?utm_campaign=988838&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p>Коллеги привет, искал себе решение как реагировать на изменения в объекте и нашел отличный сервис, который используется внутри директив таких как NgClass и NgStyle.</p><p>KeyValueDiffers позволяет создать KeyValueDiffer для сравнения изменений текущих пар ключ-значение с новыми. Если вы используете иммутабельные объекты, то можно просто обернуть все в эффект, ну а если вы наследники крутого легаси, где все объекты мутируются по ссылке, тогда проверку нужно вешать в DoCheck, чтобы реагировать на  каждый тик change detection. </p><p>Накидал оба примера, чтобы поделиться с вами: </p><p>Иммутабельный с effect:</p><pre><code class="typescript">
@Component({
  selector: 'app-test',
  template: ''
})
export class TestComponent {
  public state = input.required&lt;Record&lt;string, string | number&gt;&gt;();
  
  private differs = inject(KeyValueDiffers);
  private differ: KeyValueDiffer&lt;string, string | number&gt; | undefined;

  constructor() {
    effect(() =&gt; {
      const currentState = this.state();
      
      // создаем диффер, если он еще не создан
      if (!this.differ) {
        this.differ = this.differs.find(currentState).create();
      }

      // Эффект будет перезапускаться при изменении инпут-сигнала.
      const changes = this.differ.diff(currentState);

      // только если есть изменения
      if (changes) {
        changes.forEachAddedItem((record) =&gt; {
          console.log(`В объект добавлена запись: Ключ: ${record.key} | Значение: ${record.currentValue}`)
        });

        changes.forEachChangedItem((record) =&gt; {
          console.log(`Изменено: ${record.key} | Новое значение: ${record.currentValue}`)
        });

        changes.forEachRemovedItem((record) =&gt; {
          console.log(`Удалено: ${record.key}`)
        });
        
        // Остальные методы forEachItem и forEachPreviousItem по необходимости
      }
    })
  }
}
</code></pre><p>Легаси подход, которого, надеюсь, ни у кого нет, но на всякий случай :) </p><pre><code class="typescript">@Component({
  selector: 'app-legacy',
  template: ''
})
export class LegacyComponent implements OnInit, DoCheck {
  @Input({ required: true }) state!: Record&lt;string, string | number&gt;;
  
  private differs = inject(KeyValueDiffers);
  private differ: KeyValueDiffer&lt;string, string | number&gt; | undefined;

  ngOnInit() {
    // Создаем диффер при инициализации
    this.differ = this.differs.find(this.state).create();
  }

  // Запускается на каждый тик change detection, так как мутации по-другому не отследим.
  ngDoCheck(): void {
    const changes = this.differ?.diff(this.state);

    if (changes) {
      changes.forEachAddedItem((record) =&gt; {
        console.log(`В объект добавлена запись: Ключ: ${record.key} | Значение: ${record.currentValue}`)
      });

      changes.forEachChangedItem((record) =&gt; {
        console.log(`Значение изменилось: ${record.key}`)
      });

      changes.forEachRemovedItem((record) =&gt; {
        console.log(`Запись удалена: ${record.key}`)
      });

      // Остальные методы forEachItem и forEachPreviousItem по необходимости
    }
  }
}
</code></pre> <a href="https://habr.com/ru/posts/988838/?utm_campaign=988838&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Sun, 25 Jan 2026 19:01:01 GMT</pubDate>
    <dc:creator><![CDATA[AlekseyVY]]></dc:creator>
      
      <category><![CDATA[angular]]></category><category><![CDATA[KeyValueDiffers]]></category><category><![CDATA[signals]]></category><category><![CDATA[change detection]]></category><category><![CDATA[ngDoCheck]]></category><category><![CDATA[perfomance]]></category><category><![CDATA[effect]]></category><category><![CDATA[mutability]]></category><category><![CDATA[typescript]]></category><category><![CDATA[frontend development]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @AlekseyVY — Angular (+2) — 11.01.2026 13:48]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/984154/</guid>
    <link>https://habr.com/ru/posts/984154/?utm_campaign=984154&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p>Решил сегодня почитать, что пишут в Ангуляр комьюнити Хабра, и увидел сильно популярный <a href="https://habr.com/ru/companies/ruvds/articles/967016/" rel="noopener noreferrer nofollow">пост </a>с аж 51 лайком и 71 закладкой.<br><br>Начал читать и был удивлен примерами. Автор с уверенностью говорит, как писать на Ангуляр грамотно, и при этом приводит плохие практики в качестве примеров. Я дошел до примера с RxJS, который меня немного триггернул.</p><p>Не буду разбирать все кейсы указанные в данном посте, покажу лишь самый плохой пример который меня немного тригернул:</p><p>Автор условно говорит, что у нас есть плохой пример использования:</p><pre><code class="typescript">this.http.get('/api/data').subscribe((data) =&gt;; {
  this.data = data; // Что если запрос не вернётся?
});</code></pre><p>и затем приводит хороший пример с сигналами и RxJs:</p><pre><code class="typescript">readonly data = signal([]);
readonly error = signal(null);

loadData() {
  this.http.get('/api/data').pipe(
    tap(() =&gt;; this.error.set(null)), // Сбрасываем предыдущую ошибку перед загрузкой
    catchError((err) =&gt;; {
      this.error.set('Не удалось загрузить данные');
      return of([]); // Возвращаем пустой массив, чтобы поток не прерывался
    })
  ).subscribe((result) =&gt;; {
    this.data.set(result);
  });
}</code></pre><p>я даже не буду указывать на количество антипатернов и плохих практик в данном примере, я просто покажу правильный пример с сигналами и RxJs:</p><pre><code class="typescript">interface State&lt;T = object&gt; {
  data: T[];
  error: string | null;
}

@Component({...})
export class BestRxJs {
  private http = inject(HttpClient);
  private loadDataAction$ = new Subject&lt;void&gt;();

  private state$ = this.loadDataAction$.pipe(
    switchMap(() =&gt; 
      this.http.get&lt;State[]&gt;('/api/data').pipe(
        map((result) =&gt; ({ data: result, error: null })),
        catchError(() =&gt; of({ data: [], error: 'Не удалось загрузить данные' })),
        startWith({ data: [], error: null })
      )
    ),
    shareReplay({ bufferSize: 1, refCount: true })
  );

  readonly private state = toSignal(this.state$, {
    initialValue: { data: [], error: null } 
  });

  readonly protected data = computed(() =&gt; this.state().data);
  readonly protected error = computed(() =&gt; this.state().error);

  protected loadData(): void {
    this.loadDataAction$.next();
  }
}</code></pre><p>Этот пост для новичков, которые, начитавшись популярных статей, могут начать применять вредные практики. </p> <a href="https://habr.com/ru/posts/984154/?utm_campaign=984154&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Sun, 11 Jan 2026 10:48:59 GMT</pubDate>
    <dc:creator><![CDATA[AlekseyVY]]></dc:creator>
      
      <category><![CDATA[angular]]></category><category><![CDATA[rxjs]]></category><category><![CDATA[typescript]]></category><category><![CDATA[javascript]]></category><category><![CDATA[best practices]]></category><category><![CDATA[clean code]]></category><category><![CDATA[рефакторинг]]></category><category><![CDATA[веб разработка]]></category><category><![CDATA[паттерны]]></category><category><![CDATA[архитектура]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @AlekseyVY — Angular (+2) — 08.01.2026 14:51]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/983604/</guid>
    <link>https://habr.com/ru/posts/983604/?utm_campaign=983604&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p>Почему стоит использовать&nbsp;protected&nbsp;в Angular компонентах?</p><p>Если вы используете в своих компонентах только&nbsp;public&nbsp;и&nbsp;private, вы упускаете возможность сделать архитектуру чище. Я предлагаю четко разделять ответственность членов класса.</p><p>Часто мы по инерции делаем&nbsp;public&nbsp;любые методы и свойства, которые нужны в шаблоне (HTML). Но&nbsp;public&nbsp;в TypeScript означает, что это публичный API&nbsp;компонента - к этим методам может получить доступ любой родительский компонент через&nbsp;@ViewChild.</p><p>Почему стоит использовать&nbsp;protected:</p><p>1. Явное намерение:&nbsp;protected&nbsp;сигнализирует, что метод предназначен для использования внутри класса или в его шаблоне, но&nbsp;не должен вызываться извне.</p><p>2. Защита от регрессии:&nbsp;Если другой разработчик попытается вызвать такой метод через&nbsp;@ViewChild, TypeScript выдаст ошибку. Это заставит его задуматься: «Действительно ли мне нужно делать этот метод публичным?» или «Может, стоит создать отдельный метод для API?».</p><p>3. Читаемость:&nbsp;Открывая код, вы сразу видите:&nbsp;public&nbsp;- для внешнего мира, protected&nbsp;- для шаблона,&nbsp;private&nbsp;- для внутренней логики сервисов и подписок.</p><p>Разделяйте Public API и внутреннюю логику шаблона - ваш код станет надежнее и понятнее.</p><pre><code class="typescript">@Component({
  selector: 'app-user-profile',
  template: `
    &lt;!-- В шаблоне мы без проблем обращаемся к protected свойствам --&gt;
    &lt;div class="card"&gt;
      &lt;h3&gt;{{ userName() }}&lt;/h3&gt;
      &lt;button (click)="onUpdateClick()"&gt;Обновить&lt;/button&gt;
        @if(isLoading()) {
          &lt;div&gt;Загрузка...&lt;/div&gt;
        }
    &lt;/div&gt;
  `
})
export class UserProfileComponent {
  // PRIVATE: Внутренняя логика. 
  // Не доступно ни в шаблоне, ни родительскому компоненту.
  private _userId = 123;

  // PROTECTED: Доступно только внутри класса и в ШАБЛОНЕ.
  // Идеально для переменных состояния UI и обработчиков событий.
  protected userName = signal('Алексей');
  protected isLoading = signal(false);

  protected onUpdateClick(): void {
    this.logAction();
    console.log('Кнопку нажали в шаблоне');
  }

  // PUBLIC: Публичный API компонента.
  // Только эти методы мы разрешаем вызывать родительскому компоненту.
  public resetState(): void {
    this.userName.set('Гость');
    this.isLoading.set(false);
  }

  private logAction(): void {
    console.log(`Action logged for userId: ${this._userId}`);
  }
}</code></pre> <a href="https://habr.com/ru/posts/983604/?utm_campaign=983604&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Thu, 08 Jan 2026 11:51:38 GMT</pubDate>
    <dc:creator><![CDATA[AlekseyVY]]></dc:creator>
      
      <category><![CDATA[angular]]></category><category><![CDATA[typescript]]></category><category><![CDATA[frontend]]></category><category><![CDATA[clean code]]></category><category><![CDATA[best practices]]></category><category><![CDATA[архитектура]]></category><category><![CDATA[инкапсуляция]]></category><category><![CDATA[рефакторинг]]></category><category><![CDATA[ооп]]></category><category><![CDATA[веб разработка]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @AlekseyVY — Angular (+4) — 19.12.2025 23:17]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/978672/</guid>
    <link>https://habr.com/ru/posts/978672/?utm_campaign=978672&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p>Коллеги, всем привет!<br><br>За годы менторства по Angular (в том числе в HTML Academy) я заметил одну системную проблему: студенты и даже миддлы часто знают синтаксис <code>RxJS</code>, но не понимают реактивного мышления. В итоге мы получаем subscribe внутри subscribe и императивную лапшу.<br><br>Я искал интерактивные курсы, но большинство бесплатных ресурсов ограничиваются основами.<br><br>Курс бесплатный. Делал для себя и студентов, но теперь делюсь со всеми.</p><p>Буду рад фидбеку и баг-репортам (проект активно допиливаю).<br><br>Ссылка на курс: <a href="https://rxjs-course-avy.web.app/" rel="noopener noreferrer nofollow">https://rxjs-course-avy.web.app/</a>  </p> <a href="https://habr.com/ru/posts/978672/?utm_campaign=978672&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Fri, 19 Dec 2025 20:17:33 GMT</pubDate>
    <dc:creator><![CDATA[AlekseyVY]]></dc:creator>
      
      <category><![CDATA[RxJS]]></category><category><![CDATA[Angular]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Reactive Programming]]></category><category><![CDATA[Frontend]]></category><category><![CDATA[Unit Testing]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[Architecture]]></category><category><![CDATA[Design Patterns]]></category><category><![CDATA[Best Practices]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @Surr1os — ReactJS (+2) — 12.08.2025 12:33]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/936282/</guid>
    <link>https://habr.com/ru/posts/936282/?utm_campaign=936282&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p>Реакт уже никогда в жизни не догонит angular по производительности и функциональности) Рано или поздно от реакта откажутся, потому что библиотека всегда будет уступать полноценному фреймворку. Жду коммы от джунов, которые в жизни не работали с крупными проектами</p> <a href="https://habr.com/ru/posts/936282/?utm_campaign=936282&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Tue, 12 Aug 2025 09:33:08 GMT</pubDate>
    <dc:creator><![CDATA[Surr1os]]></dc:creator>
      
      <category><![CDATA[react]]></category><category><![CDATA[angular]]></category><category><![CDATA[2025]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @IBS_habrablog — Блог компании IBS (+2) — 14.07.2025 12:23]]></title>
    <guid isPermaLink="true">https://habr.com/ru/companies/ibs/posts/927490/</guid>
    <link>https://habr.com/ru/companies/ibs/posts/927490/?utm_campaign=927490&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/7d7/10d/d64/7d710dd64df3655515c24dff278d6d1f.jpg" width="1280" height="820"></figure><p><strong>Приглашаем на бесплатный вебинар «<a href="https://ibs-training.ru/training/seminar/151725/" rel="noopener noreferrer nofollow">Эффективное использование RxJS в Angular: шаблоны, подходы, лучшие практики</a>».</strong></p><p>📅 <strong>Дата:</strong> 15.07.2025</p><p><strong>⏰ Время:</strong> 17:00-18:00 (Мск)</p><p>Этот семинар углубит ваши знания в RxJS и Angular. Вы изучите, как организовать потоки данных, управлять подписками, избегать утечек памяти и эффективно настроить change detection. Мы рассмотрим полезные RxJS-операторы, частые ошибки и лучшие практики. Обсудим, как выстраивать архитектуру на основе RxJS, использовать AsyncPipe и делить компоненты на умные и глупые.</p><p><strong>На вебинаре:</strong></p><p>✔️ Как устроен RxJS под капотом.</p><p>✔️ Проблемы RxJS в реальных приложениях.</p><p>✔️ Tестирование потоков.</p><p>✔️ Лучшие практики и архитектурные паттерны.</p><p>✔️ Работа с операторами.</p><p>✔️ Работа с данными в реальном времени.</p><p>✔️ Кастомные операторы.</p><p>✍️<strong>З<a href="https://ibs-training.ru/training/seminar/151725/" rel="noopener noreferrer nofollow">аписаться</a></strong></p> <a href="https://habr.com/ru/posts/927490/?utm_campaign=927490&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Mon, 14 Jul 2025 09:23:20 GMT</pubDate>
    <dc:creator><![CDATA[IBS_habrablog (IBS)]]></dc:creator>
      
      <category><![CDATA[angular]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @andreishpileuski — Angular (+2) — 01.07.2025 15:42]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/923924/</guid>
    <link>https://habr.com/ru/posts/923924/?utm_campaign=923924&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p><strong>Сила RxJS. scan + mergeScan = 'Загрузить еще'</strong></p><p>Кнопка 'Загрузить еще' (либо автоматическая подгрузка данных при скролле) довольно часто встречается в проектах и обычно решение связано с большим количеством подписок и переменных. </p><p>Как всегда, для оптимизации чего либо нам на помощь приходит великий и могучий RxJS, а в данной ситуации конкретно операторы <strong>scan &amp; mergeScan</strong>.</p><figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/2b2/54d/d6f/2b254dd6f3f6e4ebda4f52a04f328060.png" width="3840" height="3840"></figure><p><strong>Код:</strong></p><pre><code class="typescript">  readonly loadTrigger$ = new Subject&lt;void&gt;();
  private readonly batchSize = 5;

  private readonly posts$ = this.loadTrigger$.pipe(
    startWith(void 0),
    scan((offset) =&gt; offset + this.batchSize, -this.batchSize),
    mergeScan(
      (accPosts: Post[], offset: number) =&gt;
        getPosts(offset, this.batchSize).pipe(
          map((newPosts) =&gt; [...accPosts, ...newPosts]),
        ),
      [] as Post[],
    ),
  );
</code></pre><ol><li><p><strong><code>scan</code>&nbsp;– калькулятор + хранитель состояния для offset:</strong></p><ul><li><p>Управляет состоянием загрузки (текущее смещение)</p></li><li><p>Начинается с&nbsp;<code>-batchSize</code>, чтобы первая загрузка была с&nbsp;<code>0</code></p></li><li><p>Увеличивает смещение на&nbsp;<code>batchSize</code>&nbsp;при каждом срабатывании</p></li></ul></li><li><p><strong><code>mergeScan</code>&nbsp;– волшебный оператор для инкрементальной загрузки:</strong></p><ul><li><p>Сохраняет массив накопленных постов</p></li><li><p>Объединяет новые данные с существующими</p></li><li><p>Корректно обрабатывает параллельные запросы (в отличие от обычного&nbsp;<code>scan</code>)</p></li></ul></li></ol><p>Где полезен этот паттерн?</p><ul><li><p>Постраничные API (пагинация)</p></li><li><p>Бесконечная прокрутка</p></li><li><p>Порционная загрузка данных</p></li><li><p>Любые сценарии накопления асинхронных данных</p></li></ul><p>scan - <a href="https://rxjs.dev/api/operators/scan" rel="noopener noreferrer nofollow">https://rxjs.dev/api/operators/scan</a><br>mergeScan - <a href="https://rxjs.dev/api/operators/mergeScan" rel="noopener noreferrer nofollow">https://rxjs.dev/api/operators/mergeScan</a><br>Больше об Angular - <a href="https://t.me/grandgular" rel="noopener noreferrer nofollow">https://t.me/grandgular</a></p> <a href="https://habr.com/ru/posts/923924/?utm_campaign=923924&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Tue, 01 Jul 2025 12:42:51 GMT</pubDate>
    <dc:creator><![CDATA[andreishpileuski]]></dc:creator>
      
      <category><![CDATA[angular]]></category><category><![CDATA[rxjs]]></category><category><![CDATA[ts]]></category><category><![CDATA[scan]]></category><category><![CDATA[mergescan]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @andreishpileuski — Angular (+2) — 19.06.2025 17:32]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/919992/</guid>
    <link>https://habr.com/ru/posts/919992/?utm_campaign=919992&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p><code>afterEveryRender и afterNextRender</code></p><figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/468/534/378/4685343781eed6d0337bcb0ab04b92b8.png" width="3840" height="3840"></figure><p>В Angular 20 <code>afterRender</code> был переименован в <code>afterEveryRender</code>, и это очень логично, так как теперь он более четко отражает суть (нейминг решает). Сам <code>afterRender</code> (далее <code>afterEveryRender</code>) и его брат <code>afterNextRender</code> появились в версии 17. Рассмотрим, почему эти два мощных инструмента управления рендерингом — не просто альтернативы <code>ngAfterViewInit</code>, а полноценные хуки жизненного цикла с бесшовной поддержкой SSR!</p><p>Это хуки?<br>Да! Это хуки нового типа, которые выполняются после рендеринга компонента:</p><ul><li><p>Они не заменяют ngAfterViewInit/ngAfterContentInit, а дополняют их</p></li><li><p>Включают гранулярные реакции на рендеры, включая обновления</p></li></ul><p><strong>Почему идеально подходит для SSR?</strong><br>Главное преимущество: обратные вызовы выполняются только на клиенте!<br> ✅ После гидратации (в SSR)<br> ✅ После первоначального рендеринга (в CSR)<br> ✅ Больше никаких ошибок «документ не определен»</p><p><strong>Использование:</strong><br> <code>constructor() {<br> // 🚫 Не запускается на сервере<br> // ✅ Запускается только один раз после загрузки браузера!<br> // 📊 Идеально подходит для однократной инициализации<br> afterNextRender(() =&gt; {<br> console.log('Next');<br> });</code></p><p><code>// 🚫 Не запускается на сервере<br> // 🔄 Запускается после каждого цикла обнаружения изменений<br> // ✨ Отлично подходит для обновлений, зависящих от DOM<br> afterEveryRender(() =&gt; {<br> console.log('Every');<br> });<br> }</code></p><p><br><strong>Когда использовать?</strong></p><p><code>afterNextRender</code></p><ul><li><p>Одноразовые операции (инициализация библиотеки, загрузка данных)</p></li><li><p>Безопасная замена ngAfterViewInit для SSR</p></li></ul><p><code>afterEveryRender</code></p><ul><li><p>Отслеживание изменений DOM (измерения элементов, позиции) <br><br>⚠️ Внимание: может повлиять на производительность</p></li></ul><p><strong>Основные выводы</strong></p><ul><li><p>Интегрировано в систему жизненного цикла Angular</p></li><li><p>Автоматический пропуск на стороне сервера - больше никаких хаков isPlatformBrowser!</p></li><li><p>afterNextRender - "один раз после рендеринга"</p></li><li><p>afterEveryRender - "после каждого обновления"</p></li></ul><p>"Я пока не использовал <code>afterEveryRender</code> в своих проектах - есть ли у вас практические примеры использования? Поделитесь в комментариях!"</p><blockquote><p>Больше об 🅰️ngular в моём&nbsp;<a href="https://t.me/grandgular" rel="noopener noreferrer nofollow">Telegram-канале</a></p></blockquote> <a href="https://habr.com/ru/posts/919992/?utm_campaign=919992&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Thu, 19 Jun 2025 14:32:28 GMT</pubDate>
    <dc:creator><![CDATA[andreishpileuski]]></dc:creator>
      
      <category><![CDATA[angular]]></category><category><![CDATA[afterRender]]></category><category><![CDATA[afterNextRender]]></category><category><![CDATA[afterEveryRender]]></category><category><![CDATA[typescript]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @andreishpileuski — Angular (+2) — 10.06.2025 17:29]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/917306/</guid>
    <link>https://habr.com/ru/posts/917306/?utm_campaign=917306&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p><code>withComponentInputBinding()</code><br> Упрощение работы с параметрами маршрутизатора в Angular.</p><figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/f82/fc8/b95/f82fc8b95ff00f065c654346dd5bbeeb.png" width="3840" height="3840"></figure><p>Как было раньше?</p><ol><li><p>Создаем переменную/свойство (Signal, BehaivorSubject, Observable, неважно)</p></li><li><p>Инжектим и подписываемся на ActivatedRoute</p></li><li><p>Получаем параметры маршрута</p></li><li><p>Записываем в BS/Signal</p></li></ol><p>😵‍💫😵‍💫😵‍💫</p><p>Манипуляций довольно много, но мы все к этому привыкли и это кажется нормальным. </p><p>Но с <code>withComponentInputBinding()</code> все стало намного проще:<br>   1.  Создаем сигнальный инпут... и...<br>Вот и все!<br><br>Никаких дополнительных манипуляций, и значение «у вас в кармане». Все, что вам нужно, чтобы это работало, — это передать <code>withComponentInputBinding()</code> в качестве аргумента в provideRouter().</p><p>Функция не новая (кажется, появилась в Angular 16), но я редко ее видел в проектах.</p><p><strong>Немного технической информации из документации:</strong></p><p>🔍Маршрутизатор передает данные в input() из:</p><ol><li><p>Параметров запроса (?page=1&amp;sort=asc)</p></li><li><p>Параметров пути и матрицы (/users/123;details=true)</p></li><li><p>Статических данных маршрута (data: { role: 'admin' })</p></li><li><p>Результатов резолвера (resolve: { user: userResolver })</p></li></ol><p>🔍 Приоритеты:<br>Если есть дублирующиеся ключи, данные переопределяются в порядке выше — резолверы имеют наивысший приоритет и перезапишут остальные.</p><p>🚩 Важный нюанс<br>Если в маршруте нет данных для input(), он получит undefined (например, если параметр запроса удален из URL).</p><p>ℹ️ Как задать значения по умолчанию?</p><p>✔ Через resolver (чтобы данные всегда были в маршруте)<br>✔ Через transform в input() (если нужно обрабатывать undefined)</p><p>Спасибо разработчикам Angular за эту функциональность 🙏.</p><blockquote><p>Больше об 🅰️ngular в моём&nbsp;<a href="https://t.me/grandgular" rel="noopener noreferrer nofollow">Telegram-канале</a></p></blockquote> <a href="https://habr.com/ru/posts/917306/?utm_campaign=917306&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Tue, 10 Jun 2025 14:29:08 GMT</pubDate>
    <dc:creator><![CDATA[andreishpileuski]]></dc:creator>
      
      <category><![CDATA[angular]]></category><category><![CDATA[typescript]]></category><category><![CDATA[javascript]]></category><category><![CDATA[frontend]]></category><category><![CDATA[withComponentInputBinding]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @andreishpileuski — Angular (+1) — 04.06.2025 13:09]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/915540/</guid>
    <link>https://habr.com/ru/posts/915540/?utm_campaign=915540&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p><strong>🦥 RxJS defer — ленивая инициализация Observable</strong></p><figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/b80/598/8ec/b805988eca3a2034bc53b4f11dddb42e.jpg" width="1133" height="1134"></figure><p>defer — это фабрика, которая создает Observable только при подписке, а не во время объявления. Идеально подходит для:</p><ul><li><p>HTTP-запросов (чтобы избежать преждевременного выполнения)</p></li><li><p>динамических данных (которые должны быть свежими при каждой подписке)</p></li><li><p>условных потоков (когда Observable зависит от состояния времени выполнения)</p></li></ul><p><strong>📌 Основные варианты использования</strong></p><ol><li><p>Свежие данные при каждой подписке </p><p><code>const freshData$ = defer(() =&gt; of(Date.now())); </code></p><p><code>// Новая временная метка при каждой подписке()</code></p></li><li><p>Работа с изменяемым состоянием </p><p><code>const token$ = defer(() =&gt; of(localStorage.getItem('token'))); </code></p><p><code>// Всегда получает текущий токен, даже если обновлен</code></p></li><li><p>Условные наблюдаемые </p><p><code>const api$ = defer(() =&gt; isLoggedIn ? http.get('/user') : http.get('/guest') );</code></p></li><li><p>Генерация случайного значения</p><p><code>const random$ = defer(() =&gt; of(Math.random())); </code></p><p><code>// Новое случайное число на подписку </code></p></li></ol><p><strong>🚫 Ограничения defer</strong></p><ul><li><p>нет кэширования → используйте shareReplay, если вам нужно повторно использовать результаты.</p></li><li><p>нет отмены запроса → объедините с switchMap/takeUntil для управления отменой</p></li></ul><p><strong>⚡Когда следует выбирать defer вместо обычных наблюдаемых?</strong></p><ul><li><p>данные должны быть свежими при каждом subscribe()</p></li><li><p>cоздание наблюдаемого стоит дорого и должно быть отложено</p></li><li><p>поток зависит от изменяемых условий (флаги функций, статус аутентификации и т. д.)</p></li></ul><p>Больше об 🅰️ngular в моём&nbsp;<a href="https://t.me/grandgular" rel="noopener noreferrer nofollow">Telegram-канале</a></p> <a href="https://habr.com/ru/posts/915540/?utm_campaign=915540&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Wed, 04 Jun 2025 10:09:03 GMT</pubDate>
    <dc:creator><![CDATA[andreishpileuski]]></dc:creator>
      
      <category><![CDATA[angular]]></category><category><![CDATA[typescript]]></category><category><![CDATA[rxjs]]></category><category><![CDATA[defer]]></category><category><![CDATA[frontend]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @andreishpileuski — Angular — 22.05.2025 13:28]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/911812/</guid>
    <link>https://habr.com/ru/posts/911812/?utm_campaign=911812&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p>😎 <strong>Кастомный signal в Angular</strong></p><p>Мы, как разработчики, использующие в работе самый крутой фреймворк (по моему мнению🙂), немного избалованы обилием его возможностей, особенно в последнее время. Но иногда хочется либо побаловаться, либо возникает реальная потребность в функционале, которого нет, но очень хотелось бы.&nbsp;</p><p><br>И вот, как-то я в очередной раз писал подобные строки:</p><p><code>isOpened = signal(false);</code></p><p><code>toggle() {</code></p><p><code>&nbsp; this.isOpened.update(value =&gt; !value);</code></p><p><code>}</code></p><p><code><br>#isOpenedEffect = effect(() =&gt; {</code></p><p><code>&nbsp; console.log('New state:', this.isOpened())</code></p><p><code>})</code></p><p>- и подумал: 'Было бы удобно, если бы был булевый signal с методом toggle'</p><p><br>В этом не было прям сильной необходимости, однако было бы немного удобнее (процентов на 10 😅). И появилась идея написать свой сигнал (реализация на фото)</p><figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/b86/2d2/654/b862d26549390bc75a8e7a5b6a233d64.jpg" width="1130" height="1132"></figure><p><br>Пример, согласен, так себе.&nbsp;</p><p>👍🏼 меньше кода, более аккуратно</p><p>👎🏼 всей команде придется подстроится</p><p><br>Но этим постом я просто хотел показать, что есть такая возможность создания своего переиспользуемого сигнала под нужды вашего проекта, где это будет выглядеть уместно и целесообразно.&nbsp;<br></p><p>💬<strong> </strong>А какого сигнала не хватает вам?</p><p>Больше об 🅰️ngular в моём&nbsp;<a href="https://t.me/grandgular" rel="noopener noreferrer nofollow">Telegram-канале</a></p> <a href="https://habr.com/ru/posts/911812/?utm_campaign=911812&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Thu, 22 May 2025 10:28:45 GMT</pubDate>
    <dc:creator><![CDATA[andreishpileuski]]></dc:creator>
      
      <category><![CDATA[angular]]></category><category><![CDATA[typescript]]></category><category><![CDATA[signal]]></category><category><![CDATA[signals]]></category><category><![CDATA[сигналы]]></category><category><![CDATA[ангуляр]]></category><category><![CDATA[effect]]></category><category><![CDATA[утечка памяти]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @andreishpileuski — Angular — 21.05.2025 10:47]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/911328/</guid>
    <link>https://habr.com/ru/posts/911328/?utm_campaign=911328&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p>RxJS: Почему shareReplay(1) может вызывать утечки памяти</p><p>Если вы используете shareReplay(1) в RxJS, будьте осторожны — в некоторых случаях это может привести к утечкам памяти! Давайте разберёмся, почему так происходит и как это исправить.</p><p>❌ Проблема с shareReplay(1). По умолчанию:</p><ul><li><p>Сохраняет последнее значение в бесконечном буфере, даже если подписчиков больше нет</p></li><li><p>Не отписывается от источника, когда никто не слушает (например, interval, Subject, HTTP-запросы)<br>Это означает, что данные могут накапливаться, а ненужные Observable продолжают работать в фоне</p></li></ul><p>✅ Решение: shareReplay({ bufferSize: 1, refCount: true })</p><ul><li><p>Добавление refCount: true решает проблему:</p></li><li><p>Автоматически отписывается, когда не остаётся подписчиков</p></li><li><p>Очищает буфер, предотвращая утечки памяти</p></li></ul><figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/1ca/149/510/1ca149510ae6c8d9fde3bfdd07810046.jpg" width="1102" height="1102"></figure><p>💡 Ключевые выводы:</p><ol><li><p>shareReplay(1) - Может оставлять "висящие" подписки</p></li><li><p>shareReplay({ bufferSize: 1, refCount: true }) - Безопасная альтернатива</p></li></ol><p>Я обнаружил эту проблему во время code review вчера, а впервые узнал о ней из <a href="https://www.youtube.com/watch?v=mVKAzhlqTx8" rel="noopener noreferrer nofollow">видео</a> Dmytro Mezhenskyi.</p><p>🔗 P.S. Самое время быстро исправить это в наших проектах!)</p><p>Больше об 🅰️ngular в моём <a href="https://t.me/grandgular" rel="noopener noreferrer nofollow">Telegram-канале</a></p> <a href="https://habr.com/ru/posts/911328/?utm_campaign=911328&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Wed, 21 May 2025 07:47:57 GMT</pubDate>
    <dc:creator><![CDATA[andreishpileuski]]></dc:creator>
      
      <category><![CDATA[angular]]></category><category><![CDATA[rxjs]]></category><category><![CDATA[frontend]]></category><category><![CDATA[shareReplay]]></category><category><![CDATA[memory leaks]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @andreishpileuski — Angular — 20.05.2025 15:33]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/911128/</guid>
    <link>https://habr.com/ru/posts/911128/?utm_campaign=911128&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p><strong>"mergeMap: Секрет управления параллелизмом в RxJS"</strong></p><figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/e4f/501/da8/e4f501da8c9d73865c6449aa6775645f.jpg" width="1082" height="1083"></figure><p>Вы знаете&nbsp;<code>mergeMap</code>,&nbsp;<code>concatMap</code>,&nbsp;<code>exhaustMap</code>&nbsp;и&nbsp;<code>switchMap</code>&nbsp;— но знали ли вы, что&nbsp;<strong>только один из них принимает второй аргумент</strong>?</p><p>👉&nbsp;<code>mergeMap</code>&nbsp;уникален благодаря параметру&nbsp;<strong><code>concurrent</code></strong>, который ограничивает параллельные подписки. Это как "регулятор скорости" для ваших Observable.</p><p>_____</p><p><strong>Пример использования (контролируемая загрузка файлов):</strong></p><p>Представьте, что вам нужно:<br>1️⃣ Обработать несколько файлов<br>2️⃣ Избежать перегрузки браузера/API<br>3️⃣ Сохранить порядок загрузки</p><p>В этом случае второй аргумент mergeMap становится незаменимым:</p><pre><code class="typescript">from(fileList).pipe(
  mergeMap(
    file =&gt; uploadFile(file), // функция загрузки
    3                        // одновременно только 3 файла
  )
).subscribe();</code></pre><p>_____</p><p><strong>Преимущества mergeMap с concurrent:</strong><br>✅ Предотвращает перегрузку API<br>✅ Оптимизирует нагрузку на сеть<br>✅ Сохраняет отзывчивость интерфейса<br>_____</p><p>Знали ли об этой возможности? 😉</p><p><em>Больше об Angular в </em><a href="https://t.me/grandgular" rel="noopener noreferrer nofollow"><em>телеграмм-канале</em></a></p> <a href="https://habr.com/ru/posts/911128/?utm_campaign=911128&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Tue, 20 May 2025 12:33:13 GMT</pubDate>
    <dc:creator><![CDATA[andreishpileuski]]></dc:creator>
      
      <category><![CDATA[angular]]></category><category><![CDATA[rxjs]]></category><category><![CDATA[frontend]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @andreishpileuski — Angular — 19.05.2025 15:13]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/910738/</guid>
    <link>https://habr.com/ru/posts/910738/?utm_campaign=910738&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p><strong>Управление методами в зависимости от окружения в 🅰️ngular</strong></p><figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/a51/032/b81/a51032b8125c58f480afe8aa169ac879.jpg" width="1040" height="1041"></figure><p>Бывают случаи, когда нужно, чтобы определенные методы работали только:</p><ul><li><p>🛠️ В&nbsp;<strong>dev-режиме</strong>&nbsp;(например, фича-тогглы, дебаг-логи, экспериментальные функции)</p></li><li><p>🚀 В&nbsp;<strong>prod-режиме</strong>&nbsp;(аналитика, мониторинг, продакшен-логика)</p></li></ul><p>Вместо того чтобы писать&nbsp;<code>if (isDevMode()) {...}</code>&nbsp;везде, можно использовать&nbsp;<strong>декораторы Angular</strong>&nbsp;для более чистого и декларативного подхода.</p><p>_____</p><p><strong>Одно из решений:</strong></p><p><code>export function EnvMode(mode: 'dev' | 'prod') {
  return function (_: unknown, __: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: unknown[]) {
      const shouldExecute = mode === 'dev' ? isDevMode() : !isDevMode();
      if (!shouldExecute) return;
      return originalMethod.apply(this, args);
    };

    return descriptor;
  };
}</code></p><p>_____</p><p><strong>Как использовать:</strong></p><p><code>@EnvMode('dev')
public setFeatureToggle(): void {
  // Сработает только в dev-режиме
}

@EnvMode('prod')
public sendAnalytics(): void {
  // Сработает только в prod-режиме
}</code></p><p>_____</p><p><strong>🤔 А вы создавали кастомные декораторы? Зачем?</strong></p><p><em>Больше про Angular в </em><a href="https://t.me/grandgular" rel="noopener noreferrer nofollow"><em>тг канале</em></a></p> <a href="https://habr.com/ru/posts/910738/?utm_campaign=910738&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Mon, 19 May 2025 12:13:29 GMT</pubDate>
    <dc:creator><![CDATA[andreishpileuski]]></dc:creator>
      
      <category><![CDATA[angular]]></category><category><![CDATA[web]]></category><category><![CDATA[javascript]]></category><category><![CDATA[typescript]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @andreishpileuski — Angular (+3) — 15.05.2025 21:54]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/909810/</guid>
    <link>https://habr.com/ru/posts/909810/?utm_campaign=909810&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p><strong>Angular Hack: Цикл без данных (в тэмплейте)</strong></p><figure class="full-width "><img src="https://habrastorage.org/getpro/habr/upload_files/6bb/311/3c6/6bb3113c6261c6f9091c5b0fc7ab85e9.jpg" width="1324" height="1318"></figure><p>Иногда нужно отобразить несколько одинаковых элементов чисто для ui-целей: скелетоны загрузки, звёзды рейтинга, пустые таблицы и т.д., но без реальных данных для итерации.</p><p>Вот мой способ (по крайней мере, я нигде такого не видел):</p><p><code>@for (_ of [].constructor(10); track $index) {<br>  &lt;div class="item"&gt;&lt;/div&gt;<br>}</code></p><p>Используется&nbsp;<code>Array.constructor</code>, чтобы создать пустую массив фиксированной длины, который&nbsp;<code>@for</code>&nbsp;может перебрать по индексам.</p><p><strong>Плюсы ✅</strong></p><ul><li><p><strong>Чудо-код</strong>&nbsp;(удивит коллег)</p></li><li><p><strong>Минимум кода</strong>&nbsp;(не нужно объявлять массив в компоненте)</p></li></ul><p><strong>Минусы ⚠️</strong></p><ul><li><p><strong>Чудо-код</strong>&nbsp;(может ненадолго ввести в ступор чающего код человека)</p></li></ul><p>Конечно, можно просто использовать&nbsp;<code>Array.from({length: 10})</code>... но так все делают, не интересно)</p><p>Норм тема? Как считаете?</p> <a href="https://habr.com/ru/posts/909810/?utm_campaign=909810&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Thu, 15 May 2025 18:54:19 GMT</pubDate>
    <dc:creator><![CDATA[andreishpileuski]]></dc:creator>
      
      <category><![CDATA[angular]]></category><category><![CDATA[typescript]]></category><category><![CDATA[hack]]></category><category><![CDATA[лайфхак]]></category><category><![CDATA[фронтенд]]></category><category><![CDATA[frontend]]></category><category><![CDATA[html]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @nin-jin — $mol (+3) — N/P]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/866818/</guid>
    <link>https://habr.com/ru/posts/866818/?utm_campaign=866818&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p><em>Доклад с SPb IT Club Meetup №3</em><br><strong>Фреймворк, который нельзя называть</strong></p><iframe id="675f45fdca01a46b4cc7328a" src="https://embedd.srv.habr.com/iframe/675f45fdca01a46b4cc7328a" class="embed_video embed__content" allowfullscreen="true"></iframe><p></p> <a href="https://habr.com/ru/posts/866818/?utm_campaign=866818&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Mon, 16 Dec 2024 06:09:51 GMT</pubDate>
    <dc:creator><![CDATA[nin-jin]]></dc:creator>
      
      <category><![CDATA[о]]></category><category><![CDATA[господи]]></category><category><![CDATA[опять реклама мола]]></category><category><![CDATA[помогите]]></category><category><![CDATA[спасите]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @lonya_547 — Angular (+2) — 03.12.2024 13:15]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/863344/</guid>
    <link>https://habr.com/ru/posts/863344/?utm_campaign=863344&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p><strong>Немного о резолверах в Angular 19 (теперь в них есть редиректы).</strong></p><p>В логике использования гвардов применяется подход: </p><blockquote><p>проверить что-то, если все ок то вернуть <strong>true</strong> или кинуть редирект на другую страницу</p></blockquote><p>Выглядит достаточно удобно. Но если мне не изменяет память в резолверах такого нет, вместо этого приходилось натягивать <strong>Router </strong>и рулить<strong> navigate </strong>или <strong>navigateByUrl </strong>и т.д.</p><p>В 19 же версии нам немного упростили жизнь и резолвер научили в <strong>RedirectCommand.</strong></p><p>Пример с <a href="https://angular.dev/api/router/ResolveFn" rel="noopener noreferrer nofollow">angular.dev</a></p><pre><code class="typescript">export const heroResolver: ResolveFn&lt;Hero&gt;= async (
  route: ActivatedRouteSnapshot,
  state: RouterStateSnapshot,
) =&gt; {
  const router = inject(Router);
  const heroService = inject(HeroService);
  
  try {
    return await heroService.getHero(route.paramMap.get('id')!);
  } catch {
    return new RedirectCommand(router.parseUrl('/404'));
  }
  
};</code></pre><p>Ну просто сказка какая-то, а не только сигналы =)</p> <a href="https://habr.com/ru/posts/863344/?utm_campaign=863344&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Tue, 03 Dec 2024 10:15:02 GMT</pubDate>
    <dc:creator><![CDATA[lonya_547]]></dc:creator>
      
      <category><![CDATA[angular19]]></category><category><![CDATA[angular]]></category><category><![CDATA[resolver]]></category><category><![CDATA[redirectcommand]]></category>
  </item>
  

	
  

  

  

    

  

  
  <item>
    <title><![CDATA[Пост @isumix — ReactJS (+4) — 05.10.2024 10:28]]></title>
    <guid isPermaLink="true">https://habr.com/ru/posts/848394/</guid>
    <link>https://habr.com/ru/posts/848394/?utm_campaign=848394&amp;utm_source=habrahabr&amp;utm_medium=rss</link>
    <description><![CDATA[<p>Кнопка со счётчиком: React vs Fusor</p><figure class=""><img src="https://habrastorage.org/getpro/habr/upload_files/8ed/abd/064/8edabd064c179017c0610d7a1b94ccef.png" alt="Кнопка со счётчиком: React vs Fusor" title="Кнопка со счётчиком: React vs Fusor" width="496" height="552"><div><figcaption>Кнопка со счётчиком: React vs Fusor</figcaption></div></figure><p>Fusor это новый способ разработки вэб приложений <a href="https://github.com/fusorjs/dom" rel="noopener noreferrer nofollow">https://github.com/fusorjs/dom</a></p><p></p> <a href="https://habr.com/ru/posts/848394/?utm_campaign=848394&amp;utm_source=habrahabr&amp;utm_medium=rss">Читать дальше &rarr;</a>]]></description>
      
    <pubDate>Sat, 05 Oct 2024 07:28:17 GMT</pubDate>
    <dc:creator><![CDATA[isumix]]></dc:creator>
      
      <category><![CDATA[javascript]]></category><category><![CDATA[fusor]]></category><category><![CDATA[fusorjs]]></category><category><![CDATA[vanilla]]></category><category><![CDATA[typescript]]></category><category><![CDATA[react]]></category><category><![CDATA[solid]]></category><category><![CDATA[web]]></category><category><![CDATA[frontend]]></category><category><![CDATA[front-end]]></category>
  </item>
  

	
  

  

  

      

      

      

    
  </channel>
</rss>
