<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Pavel Osin</title><generator>teletype.in</generator><description><![CDATA[Павел Осин.  Frontend-разработчик.  Заметки о веб-разработке, программировании и не только.
https://t.me/httpresponse418]]></description><image><url>https://img3.teletype.in/files/66/bb/66bb79d5-5413-4dd3-8ab7-a8abff107bd8.png</url><title>Pavel Osin</title><link>https://osinpaul.ru/</link></image><link>https://osinpaul.ru/?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/osinpaul?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/osinpaul?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Thu, 09 Apr 2026 01:57:58 GMT</pubDate><lastBuildDate>Thu, 09 Apr 2026 01:57:58 GMT</lastBuildDate><item><guid isPermaLink="true">https://osinpaul.ru/_zOl28unbRz</guid><link>https://osinpaul.ru/_zOl28unbRz?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul</link><comments>https://osinpaul.ru/_zOl28unbRz?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul#comments</comments><dc:creator>osinpaul</dc:creator><title>Webstorm, как исключить из поиска папки</title><pubDate>Tue, 09 Dec 2025 09:05:12 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/a4/f4/a4f4666e-5b8e-487d-bab4-41e7dc8ca423.png"></media:content><description><![CDATA[<img src="https://img4.teletype.in/files/7a/fb/7afbce43-2dfb-4b03-b86b-ad837d14f48b.png"></img>На новом проекте WS активно искал по файлам, которые относятся к результатам билда nx-репозитория. Искал как решить, поэтому зафиксирую здесь.

Чтобы отключить поиск по файлам, которые Git игнорирует (например, в директории node_modules или dist), нужно настроить WebStorm, чтобы он не индексировал эти директории как часть проекта, а не отключать сам .gitignore (он нужен для Git), делая это через Settings/Preferences &gt; Directories, добавив папку как &quot;Excluded&quot; (Исключенная). Это предотвратит их участие в поиске, рефакторинге и других операциях IDE, сохраняя .gitignore для Git. 

Шаги для исключения папки в WebStorm:]]></description><content:encoded><![CDATA[
  <p id="rz74">На новом проекте WS активно искал по файлам, которые относятся к результатам билда nx-репозитория. Искал как решить, поэтому зафиксирую здесь.<br /><br />Чтобы отключить поиск по файлам, которые Git игнорирует (например, в директории node_modules или dist), нужно настроить WebStorm, чтобы он не индексировал эти директории как часть проекта, а не отключать сам .gitignore (он нужен для Git), делая это через Settings/Preferences &gt; Directories, добавив папку как &quot;Excluded&quot; (Исключенная). Это предотвратит их участие в поиске, рефакторинге и других операциях IDE, сохраняя .gitignore для Git. <br /><br />Шаги для исключения папки в WebStorm:</p>
  <pre id="Chdn">
File (Файл) -&gt; 
Settings (Настройки) -&gt; 
Preferences (Предпочтения)</pre>
  <p id="dnUr"><br />- В левой панели кликаем по Project Structure (Структура проекта).<br />- Находим папку, которую хотите игнорировать (например, node_modules, dist, build), клик по ней правой кнопкой мыши и пометить как Excluded (Исключенная).<br /></p>
  <figure id="IzBm" class="m_column">
    <img src="https://img4.teletype.in/files/7a/fb/7afbce43-2dfb-4b03-b86b-ad837d14f48b.png" width="2692" />
  </figure>

]]></content:encoded></item><item><guid isPermaLink="true">https://osinpaul.ru/hbKeVmZbl13</guid><link>https://osinpaul.ru/hbKeVmZbl13?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul</link><comments>https://osinpaul.ru/hbKeVmZbl13?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul#comments</comments><dc:creator>osinpaul</dc:creator><title>Centi Conf. Frontend Day</title><pubDate>Mon, 08 Dec 2025 20:11:17 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/52/0a/520a642c-c990-405a-bd89-8d30ccf2cefd.png"></media:content><description><![CDATA[<img src="https://img4.teletype.in/files/77/a6/77a6c19c-7665-40d4-9b3c-274ef9078147.jpeg"></img>Посетил конференцию по разработке на Angular от Centicore. Один поток, шесть докладов. Рассказывали о новом и хорошо забытом старом во фронтенде.]]></description><content:encoded><![CDATA[
  <p id="11VD">Посетил конференцию по разработке на Angular от Centicore. Один поток, шесть докладов. Рассказывали о новом и хорошо забытом старом во фронтенде.</p>
  <figure id="GNpI" class="m_column">
    <img src="https://img4.teletype.in/files/77/a6/77a6c19c-7665-40d4-9b3c-274ef9078147.jpeg" width="2268" />
  </figure>
  <p id="erOR">Отмечу, что запомнилось и сухой текст разбавлю парой фоточек. <br /><br /><strong>1) Computer Science во фронтенде</strong></p>
  <p id="AshM">Иван Черняков рассказал на практических примерах, что фронтенд требует не только владения фреймворком, но и понимания алгоритмов, структур данных, сложности операций. </p>
  <p id="Qg1b">Что интересно освежил:</p>
  <ul id="TMLY">
    <li id="vzPx">Битовые маски</li>
  </ul>
  <blockquote id="H45t">Используются для хранения множества булевых признаков <strong>в одном числе</strong>: <br />- экономия памяти <br />- быстрые проверки (<code>&amp;</code>, <code>|</code>, <code>^</code>)<br />- удобно для наборов флагов (права доступа, возможности, состояния)<br /><br />Примерно так же работают feature flags — только привычнее, когда флаги строковые, а не побитовые.</blockquote>
  <ul id="r3oe">
    <li id="pSIs">Двунаправленные связные списки</li>
  </ul>
  <blockquote id="5jt0">Актуальны там, где: <br />- много <strong>вставок/удалений в середине коллекции <br />- </strong>порядок элементов важен и часто меняется <br /><br />В JavaScript списки есть только абстрактно (<code>Array</code> ≠ linked list), поэтому, когда нужен реальный O(1) на вставку — без собственной структуры не обойтись. Полезно помнить, что <strong>не всё должно быть массивом</strong>, если требуется производительность.</blockquote>
  <hr />
  <ul id="ujPe">
    <li id="HC91">TypeScript Transformers</li>
  </ul>
  <blockquote id="oe0a">Это <strong>вмешательство в процесс компиляции</strong> TypeScript: <br />- переписывание типов и кода <br />- автоматическое добавление полей, проверок, метаданных<br />- снижение дублирования <br />Использование трансформеров позволяет: формализовать архитектурные правила генерировать код там, где он предсказуем и однообразен</blockquote>
  <p id="aMjR"></p>
  <p id="gt2s">Вывод простой: инженерный подход повышает оптимальность и масштабируемость приложений. Доклад полезен как напоминание: фронтенд — это не только фреймворки. Хорошая мотивация скинуть пыль с алгоритмических знаний.<br /><br /><strong>2) Сигналы в Angular</strong></p>
  <p id="0i8J">Олег Щеголев рассказал, что нового принесли сигналы в Angular. При сухом чтении документации не возникает желания их использовать, особенно когда давно пишешь на RxJS. На примерах же становится сильно понятнее, что сигналы позволяют экономить ресурсы, перерендеры и открывают дорогу в прекрасное будущее без Zone.js :)</p>
  <p id="MbYz">Заметки:</p>
  <ul id="iTnc">
    <li id="5AGr"><strong><code>Resource</code></strong> для асинхронных данных</li>
    <li id="5l8z">debounce для сигналов через <code>toSignal</code> → уже можно использовать</li>
  </ul>
  <p id="JHLs">и закончу докладом Глеба Михеева. </p>
  <figure id="48pI" class="m_column">
    <img src="https://img2.teletype.in/files/10/2a/102a3a55-e1d3-40ed-8ab0-b1fb6d861906.jpeg" width="2268" />
  </figure>
  <h3 id="8jK5">3) Генеративный пользовательский интерфейс</h3>
  <p id="0cls">Доклад о будущем, которое скоро наступит. А возможно уже наступило.<br />Пока мы по-старинке верстаем компоненты и пишем логику систем, все вокруг бежит так быстро, что ИИ делает просто нереальными попытки остающих зацепиться за реальность. Завернул. </p>
  <p id="nN1c">Доклад о формировании интерфейса на основе данных и контекста. Основная идея — UI может не быть жёстко заданным, а строится динамически. Технически звучит перспективно для систем с большим количеством вариативных экранов и пользовательских сценариев.</p>
  <p id="sgZP">Смысл подхода: UI не верстается вручную заранее, а формируется автоматически в момент использования — на основе данных, бизнес-правил и контекста.</p>
  <p id="WcJM">В основе лежат БЯМ (большие языковые модели), которые обучаются на описанной логике, моделях данных, сценариях, ограничениях и данных о пользователе. Все остальное генерируется автоматически. Разработчик становится по-сути оператором нейросети, чтобы задавать правила построения UI, а не расположение кнопок и форм. Тут риторический вопрос, нужны-ли разработчики в этой цепочке, когда такой интерфейс может построиться бизнес-аналитиком.</p>
  <p id="2iFr">Фактически для нас, как разработчиков смещается фокус.</p>
  <p id="HPCW">Было:</p>
  <pre id="2yn5">сверстать экран → внедрить бизнес-логику → править по запросу бизнеса</pre>
  <p id="kybh">Становится:</p>
  <pre id="dY7o">описать правила → настроить компоненты → обеспечить совместимость</pre>
  <p id="jMbw">Пока это выглядит как направление развития интерфейсов корпоративного уровня, где стоимость изменений высока. Но попробовать хочется уже на новогодних.</p>
  <p id="PD2D"><br />Немного жалею, что торопился и не остался на афтерпати)</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://osinpaul.ru/tsalLqGq6TM</guid><link>https://osinpaul.ru/tsalLqGq6TM?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul</link><comments>https://osinpaul.ru/tsalLqGq6TM?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul#comments</comments><dc:creator>osinpaul</dc:creator><title>performance() в браузере</title><pubDate>Fri, 05 Dec 2025 07:12:11 GMT</pubDate><description><![CDATA[Когда речь заходит о производительности веб-приложений, большинство разработчиков вспоминают Lighthouse, Web Vitals и профайлеры в DevTools. Но в современном JavaScript есть ещё один мощный инструмент, который часто недооценивают — Performance API. Это встроенный интерфейс браузера, который позволяет точно измерять время выполнения операций внутри веб-приложения: от загрузки страницы до конкретных участков кода. Он даёт доступ к высокоточным временным меткам (DOMHighResTimeStamp) и системам метрик.]]></description><content:encoded><![CDATA[
  <p id="Luxa">Когда речь заходит о производительности веб-приложений, большинство разработчиков вспоминают Lighthouse, Web Vitals и профайлеры в DevTools. Но в современном JavaScript есть ещё один мощный инструмент, который часто недооценивают — Performance API. Это встроенный интерфейс браузера, который позволяет точно измерять время выполнения операций внутри веб-приложения: от загрузки страницы до конкретных участков кода. Он даёт доступ к высокоточным временным меткам (DOMHighResTimeStamp) и системам метрик.</p>
  <p id="xHGq">Я использовал его для отладки интервалов и таймеров. Надеюсь, ни для кого не секрет, что в js interval(1000) - это далеко не 1 секунда. </p>
  <p id="gtxe"></p>
  <p id="Lvni"><strong>Например, задача</strong></p>
  <p id="Qzl5">Имеется временное отклонение t при работе таймеров в системе. Ожидаем, что таймер выполнится через 1000 мс, и проверяем реальное время:</p>
  <pre id="gJbS" data-lang="typescript">const interval = 1000;
let ticks = 0;
let last = performance.now();

const id = setInterval(() =&gt; {
  ticks++;
  const now = performance.now();
  const drift = now - last - interval; 

  console.log(
    &#x60;Тик #${ticks} — задержка: ${drift.toFixed(2)}ms, текущее время: ${(now - start).toFixed(2)}ms&#x60;
  );

  last = now;

  if (ticks &gt;= 5) {
    clearInterval(id);
    console.log(&#x27;Интервал остановлен&#x27;);
  }
}, interval);

const start = performance.now();
</pre>
  <p id="F2NI"><br />В реальности получил от 920мс до 1100. Иногда больше - иногда меньше. В зависимости от загруженности EventLoop или процессора ПК, активности вкладки и так далее.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://osinpaul.ru/2voohx8AhF4</guid><link>https://osinpaul.ru/2voohx8AhF4?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul</link><comments>https://osinpaul.ru/2voohx8AhF4?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul#comments</comments><dc:creator>osinpaul</dc:creator><title>Шпаргалка по Conventional Commits</title><pubDate>Sat, 29 Nov 2025 11:11:55 GMT</pubDate><description><![CDATA[Система Conventional Commits помогает писать единообразные коммиты, которые легко читать, анализировать и автоматически использовать в релизах.]]></description><content:encoded><![CDATA[
  <p id="FuDM">Система <strong>Conventional Commits</strong> помогает писать единообразные коммиты, которые легко читать, анализировать и автоматически использовать в релизах. <br /></p>
  <p id="3u7i">Собрал базовые примеры использования, чтобы документация была всегда под рукой.</p>
  <p id="kdoq"></p>
  <h2 id="Ezxc"><strong>Основная идея</strong></h2>
  <p id="ffjD">Каждый коммит должен отвечать на три простых вопроса:</p>
  <ol id="Eg8e">
    <li id="DLNT"><strong>Что изменили?</strong> — тип (<code>type</code>)</li>
    <li id="jW0y"><strong>Где?</strong> — область (<code>scope</code>) — необязательно, но очень рекомендуется</li>
    <li id="xBXb"><strong>Зачем и что именно?»</strong> — короткое и понятное описание (<code>subject</code>)</li>
  </ol>
  <pre id="cywb" data-lang="yaml">&lt;type&gt;[optional scope]: &lt;subject&gt;
[optional body]
[optional footer]</pre>
  <p id="pjc1"></p>
  <h3 id="lvaE">1. Типы коммитов</h3>
  <ul id="NGTY">
    <li id="3qL5">feat (новый функкционал).</li>
  </ul>
  <pre id="n3T6">feat(auth): добавить refresh token механизм</pre>
  <ul id="b6XL">
    <li id="zCq4">fix (исправление ошибки, дефекта)</li>
  </ul>
  <pre id="wOGF">fix(profile): корректно сохранять изменения аватара</pre>
  <ul id="hSlU">
    <li id="Omxn"><code>docs (</code>изменения в документации)</li>
  </ul>
  <pre id="f4ZL">docs(readme): обновить раздел по установке проекта</pre>
  <ul id="hWT1">
    <li id="V33C"><code>style</code>(изменения, не влияющие на логику — форматирование, пробелы, точки с запятой.</li>
  </ul>
  <pre id="aNOK">style(ui): выровнять отступы в модальном окне</pre>
  <ul id="pQ5l">
    <li id="xj3j"><code>refactor (</code>перереписывание кода без изменения поведения)</li>
  </ul>
  <pre id="LPJw">refactor(api): упростить структуру сервисов</pre>
  <ul id="3Sbe">
    <li id="eZgm"><code>perf</code>(Оптимизация производительности)</li>
  </ul>
  <pre id="Qcob">perf(table): ускорить рендер на 20%</pre>
  <ul id="4J7f">
    <li id="QxSm"><code>test (</code>добавление или исправление тестов)</li>
  </ul>
  <pre id="l7HR">test(cart): покрыть логику пересчёта сумм</pre>
  <ul id="yCu2">
    <li id="5wwD"><code>build</code>(изменения в сборке, CI, зависимостях)</li>
  </ul>
  <pre id="slv4">build: обновить Webpack до версии 5</pre>
  <ul id="60Kw">
    <li id="Zz0u"><code>chore</code>(Прочие изменения, не затрагивающие код приложения)</li>
  </ul>
  <pre id="GHSA">chore: почистить скрипты в package.json</pre>
  <p id="AbxO"><br /></p>
  <h3 id="4ssK">2. Scope</h3>
  <p id="Cden">Всегда, когда это помогает локализовать изменение. Хорошие примеры областей:</p>
  <ul id="32ja">
    <li id="ijY6"><code>auth</code></li>
    <li id="klHe"><code>live</code></li>
    <li id="WyH4"><code>ui</code></li>
    <li id="Hdph"><code>api</code></li>
  </ul>
  <p id="ydNz">Некорректные примеры:</p>
  <ul id="zofn">
    <li id="hap3"><code>misc</code></li>
    <li id="Ne6v"><code>stuff</code></li>
    <li id="RaG3"><code>temp</code></li>
  </ul>
  <p id="fAsr"></p>
  <h3 id="cjKG">3. Subject</h3>
  <p id="TxwY">Короткий принцип:</p>
  <ul id="zz4h">
    <li id="5cCw">Пишем в <strong>повелительном наклонении</strong>, как будто даём команду коду.</li>
    <li id="L0Qr">Не ставим точку в конце.</li>
    <li id="B8sA">Максимум ясности, минимум слов.</li>
    <li id="SGes">Если в команде принято соглашении о привязке задач к коммитам, можно указать номер задачи</li>
  </ul>
  <p id="qm2h">Примеры хороших subject:</p>
  <ul id="DGjO">
    <li id="A6oX"><code>перенести логику в сервис</code></li>
    <li id="i2SQ"><code>добавить debounce при вводе</code></li>
    <li id="I0fn"><code>исправить ошибку валидации номера</code></li>
  </ul>
  <p id="STK3"></p>
  <h3 id="Gedv">4. Body</h3>
  <p id="JFy1">Используем только если нужно объяснение:</p>
  <ul id="qCAH">
    <li id="5DCZ">почему так сделано;</li>
    <li id="gZIf">какие альтернативы рассматривались;</li>
    <li id="pFjL">есть ли побочные эффекты.</li>
  </ul>
  <pre id="zua5">refactor(order): вынести расчет скидки в отдельный модуль
Чтобы упростить дальнейшую поддержку и подготовить почву для многоуровневой системы скидок.</pre>
  <p id="46nx"><br /></p>
  <h3 id="1LjW">5. Footer</h3>
  <pre id="64GH">Closes #123</pre>
  <h3 id="dvmN"></h3>
  <p id="p6L9"><strong>BREAKING CHANGE:</strong></p>
  <p id="sGo2">Если коммит ломает обратную совместимость, обязательно указываем:</p>
  <pre id="DLkV">feat(api): изменить структуру ответа метода /users

BREAKING CHANGE: метод возвращает объект вместо массива</pre>

]]></content:encoded></item><item><guid isPermaLink="true">https://osinpaul.ru/A2Lxs5sghPx</guid><link>https://osinpaul.ru/A2Lxs5sghPx?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul</link><comments>https://osinpaul.ru/A2Lxs5sghPx?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul#comments</comments><dc:creator>osinpaul</dc:creator><title>Перенос файлов между ветками git</title><pubDate>Mon, 24 Nov 2025 08:57:53 GMT</pubDate><description><![CDATA[Иногда в Git нужно не переключаться полностью на другую ветку, а забрать из неё один или несколько конкретных файлов. Это существенно упрощает работу, особенно если нужно подтянуть изменения коллеги, а они разбиты по нескольким коммитам. Git позволяет сделать это в одну команду — git checkout (в новых версиях — git restore).]]></description><content:encoded><![CDATA[
  <p id="VI8q">Иногда в Git нужно не переключаться полностью на другую ветку, а забрать из неё <strong>один или несколько конкретных файлов. </strong>Git позволяет сделать это в одну команду — <strong>git checkout</strong> (в новых версиях — <strong>git restore</strong>). Это существенно упрощает работу, особенно если нужно подтянуть изменения коллеги, а они разбиты по нескольким коммитам</p>
  <p id="rZUo">Находясь в текущей ветке (например, develop):</p>
  <pre id="5SvV">git checkout &lt;имя-ветки&gt; -- &lt;путь/к/файлу&gt;</pre>
  <hr />
  <p id="DtGF">Пример</p>
  <p id="JaAg">Мы хотим взять файл <code>config.yml</code> из ветки <code>develop</code>:</p>
  <pre id="SAgn">git checkout origin/develop -- config.yml</pre>
  <p id="gkU7"></p>
  <p id="eQfR">После выполнения команда не переключит нас на ветку <code>develop</code>, а просто заменит файл <code>config.yml</code> в текущей ветке версией из <code>develop</code>.</p>
  <p id="78lS">Если файлов несколько, пути можно указать через пробел (или просто указать директорию)</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://osinpaul.ru/lgp0E9j8sgh</guid><link>https://osinpaul.ru/lgp0E9j8sgh?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul</link><comments>https://osinpaul.ru/lgp0E9j8sgh?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul#comments</comments><dc:creator>osinpaul</dc:creator><title>Signal forms в Angular</title><pubDate>Mon, 24 Nov 2025 06:52:07 GMT</pubDate><description><![CDATA[Angular продолжает движение в сторону чистой реактивности без RxJS, и сигналы стали первым большим шагом. Теперь к ним добавились сигнальные формы — экспериментальный API, появившийся в Angular 21.]]></description><content:encoded><![CDATA[
  <p id="YyWd">Angular продолжает движение в сторону <strong>чистой реактивности без RxJS</strong>, и сигналы стали первым большим шагом. Теперь к ним добавились <strong>сигнальные формы</strong> — экспериментальный API, появившийся в Angular 21.</p>
  <p id="WBSt">Это логичное продолжение идеи: если уже есть реактивное состояние на сигналах, то и формы тоже должны жить в этой модели — без подписок, без форм-групп, без FormControl, без грязных зон.</p>
  <p id="RgAh"></p>
  <p id="kZMY">Первое, что бросается в глаза — состояние формы мы объявляем через обычный сигнал без Observable и без подписок:</p>
  <pre id="QgUR" data-lang="typescript">formData = signal({
  username: &#x27;btomer&#x27;,
  firstName: &#x27;Tomer&#x27;,
  lastName: &#x27;Bill&#x27;,
  age: 35,
});</pre>
  <p id="1Ieg">Далее создаём саму форму:</p>
  <pre id="uKHy" data-lang="typescript">form = form(this.formData, (user) =&gt; {
  required(user.username);
  required(user.lastName);
  pattern(user.firstName, /^[A-Za-z]+$/, { message: &#x27;Проверьте ввод, допустимы только символы&#x27; });
  max(user.age, 40);
});</pre>
  <p id="DYbU">По сравнению со старым подходом все стало сильно проще: никаких FormControl, FormBuilder и подписок на изменения. </p>
  <p id="Fb6I"></p>
  <p id="zFPl">Связывание с шаблоном так же упростилось:</p>
  <pre id="AoqB" data-lang="typescript">&lt;input [control]=&quot;form.username&quot; /&gt;
&lt;input [control]=&quot;form.firstName&quot; /&gt;
&lt;input type=&quot;number&quot; [control]=&quot;form.age&quot; /&gt;

&lt;app-custom [control]=&quot;form.lastName&quot; /&gt;</pre>
  <p id="fT1v"></p>
  <p id="7iIb">Состояние формы так же выводится через сигналы:</p>
  <pre id="ZVjK" data-lang="typescript">&lt;pre&gt;{{ form().valid() }}&lt;/pre&gt;

@let errors = form().errorSummary();
@for (error of errors; track $index) {
  &lt;p&gt;{{ error | json }}&lt;/p&gt;
}

&lt;pre&gt;{{ form().value() | json }}&lt;/pre&gt;</pre>
  <p id="T2lh"></p>
  <p id="Btix">И самое главное, Angular уходит от использования CVA (Control Value Accessor)</p>
  <p id="sDC1">Чтобы создать новый контрол, нужно  имплементировать интерфейс FormValueControl. На первый взгляд он сильно проще, но надо еще попробовать на сложном контроле. В базе выглядит как-то так:<br /></p>
  <pre id="Ppn9" data-lang="typescript">@Component({
  selector: &#x27;app-custom&#x27;,
  imports: [Control],
  template: &#x60;
    &lt;div&gt;
      &lt;label&gt;Last name: &lt;/label&gt;
      &lt;input #i [value]=&quot;value()&quot; (input)=&quot;value.set(i.value)&quot; /&gt;
    &lt;/div&gt;
  &#x60;
})
export class Custom implements FormValueControl&lt;string&gt; {
  value = model&lt;string&gt;(&#x27;&#x27;);
}</pre>
  <p id="aZPA">Всё, что нужно — сигнал <code>model()</code>, представляющий значение.<br />Ни registerOnChange, ни writeValue, ни setDisabledState — просто реактивная модель.</p>
  <p id="2eq8">По ощущениям это действительно новая эпоха форм в Angular, похожая на то, как signals изменили работу с состоянием.</p>
  <p id="eMhO">Попробовать сигнальные формы можно самостоятельно, например на <a href="https://stackblitz.com/edit/stackblitz-starters-2tq7duke?file=src%252Fmain.ts" target="_blank">StackBlitz</a></p>

]]></content:encoded></item><item><guid isPermaLink="true">https://osinpaul.ru/el6NSH2UbIk</guid><link>https://osinpaul.ru/el6NSH2UbIk?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul</link><comments>https://osinpaul.ru/el6NSH2UbIk?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul#comments</comments><dc:creator>osinpaul</dc:creator><title>WeakMap</title><pubDate>Tue, 22 Jul 2025 16:57:22 GMT</pubDate><category>JavaScript 📜</category><description><![CDATA[WeakMap — это структура, которая не удерживает объект от удаления GC.]]></description><content:encoded><![CDATA[
  <p id="Qo0U"><code>WeakMap</code> — это структура, которая <strong>не удерживает объект от удаления GC</strong>.</p>
  <ul id="i7hu">
    <li id="72r1">Ключи — <strong>только объекты</strong>.</li>
    <li id="8hYC">Если на ключ больше <strong>нет других ссылок</strong> кроме <code>WeakMap</code> — объект считается мусором и будет удалён.</li>
    <li id="tdOG"><code>WeakMap</code> <strong>не итерируемый</strong>, ты не можешь получить <code>.keys()</code> или <code>.size()</code> — потому что эти объекты могут в любой момент &quot;исчезнуть&quot;.</li>
  </ul>
  <p id="eEoP"></p>
  <pre id="NXXR" data-lang="javascript">const privateData = new WeakMap();

class User {
  constructor(name) {
    privateData.set(this, { name });
  }

  get name() {
    return privateData.get(this).name;
  }
}
</pre>
  <p id="t8CU">📌 Когда экземпляр <code>User</code> больше нигде не используется — он удаляется, и все его &quot;приватные данные&quot; в <code>WeakMap</code> тоже.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://osinpaul.ru/61Qva_TMKnu</guid><link>https://osinpaul.ru/61Qva_TMKnu?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul</link><comments>https://osinpaul.ru/61Qva_TMKnu?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul#comments</comments><dc:creator>osinpaul</dc:creator><title>RxJS share() в Angular</title><pubDate>Sun, 20 Jul 2025 11:56:13 GMT</pubDate><description><![CDATA[Многие Observable в RxJS являются холодными. Это означает, что при каждой подписке создаётся новый поток, заново выполняются все операторы и возможные побочные эффекты — например, HTTP-запросы, таймеры, логирование и т.д.]]></description><content:encoded><![CDATA[
  <p id="RkgQ">Многие Observable в RxJS являются <strong>холодными</strong>. Это означает, что при каждой подписке создаётся <strong>новый поток</strong>, заново выполняются все операторы и возможные побочные эффекты — например, HTTP-запросы, таймеры, логирование и т.д.</p>
  <p id="HwEg">Чтобы избежать повторных вычислений и <strong>разделить один источник данных между несколькими подписчиками</strong>, используется оператор <code>share()</code>. Он превращает Observable в <strong>горячий</strong> и включает режим <strong>мультикастинга</strong> — подписчики получают данные из общего потока, а не инициируют новые.</p>
  <hr />
  <h2 id="zwu8">Пример холодного Observable</h2>
  <p id="fTvO">Допустим, у нас есть сервис <code>DataService</code>, который возвращает поток данных:</p>
  <pre id="ITcr">import { Injectable } from &#x27;@angular/core&#x27;;  
import { Observable, of, delay, tap } from &#x27;rxjs&#x27;;  
  
@Injectable({ providedIn: &#x27;root&#x27; })  
export class DataService {  
  private callCount = 0;

  // Холодный Observable — каждый вызов создаёт новый поток
  getData(): Observable&lt;string&gt; {  
    this.callCount++;  
    return of(&#x60;Ответ #${this.callCount}&#x60;).pipe(  
      delay(1000), // имитируем задержку  
      tap(value =&gt; console.log(&#x27;🔥 Выполнен запрос!&#x27;, value))  
    );  
  }
}
</pre>
  <p id="qDDr">И у нас есть компонент, который подписывается на этот Observable дважды:</p>
  <pre id="qnGX">import { Component, OnInit } from &#x27;@angular/core&#x27;;  
import { DataService } from &#x27;../../data.service&#x27;;  
  
@Component({  
  selector: &#x27;app-cold&#x27;,  
  template: &#x60;
    &lt;h2&gt;Cold Observable (без share)&lt;/h2&gt;
    &lt;div *ngIf=&quot;value1&quot;&gt;Подписка 1: {{ value1 }}&lt;/div&gt;
    &lt;div *ngIf=&quot;value2&quot;&gt;Подписка 2: {{ value2 }}&lt;/div&gt;
  &#x60;  
})  
export class ColdComponent implements OnInit {  
  value1?: string;  
  value2?: string;

  constructor(private dataService: DataService) {}

  ngOnInit() {
    const obs$ = this.dataService.getData();  
  
    obs$.subscribe(value =&gt; (this.value1 = value));  
    obs$.subscribe(value =&gt; (this.value2 = value));  
  }
}
</pre>
  <p id="MPVz"><strong>Результат:</strong> в консоли мы увидим:</p>
  <pre id="ARTE">🔥 Выполнен запрос! Ответ #1  
🔥 Выполнен запрос! Ответ #2  
</pre>
  <p id="IgzA">Каждая подписка вызывает Observable заново. Это может быть нежелательно, если Observable делает сетевой запрос или дорогостоящую операцию.</p>
  <hr />
  <h2 id="H7wN">🔥 Преобразуем в горячий Observable с <code>share()</code></h2>
  <p id="D4Q7">Теперь давайте применим <code>share()</code> и сделаем один общий поток:</p>
  <pre id="IUPV">import { Component, OnInit } from &#x27;@angular/core&#x27;;  
import { DataService } from &#x27;../../data.service&#x27;;  
import { share } from &#x27;rxjs/operators&#x27;;

@Component({  
  selector: &#x27;app-shared&#x27;,  
  template: &#x60;
    &lt;h2&gt;Shared Observable (с share)&lt;/h2&gt;
    &lt;div *ngIf=&quot;value1&quot;&gt;Подписка 1: {{ value1 }}&lt;/div&gt;
    &lt;div *ngIf=&quot;value2&quot;&gt;Подписка 2: {{ value2 }}&lt;/div&gt;
  &#x60;  
})  
export class SharedComponent implements OnInit {  
  value1?: string;  
  value2?: string;

  constructor(private dataService: DataService) {}

  ngOnInit() {
    const shared$ = this.dataService.getData().pipe(share());

    shared$.subscribe(value =&gt; (this.value1 = value));  
    shared$.subscribe(value =&gt; (this.value2 = value));  
  }
}
</pre>
  <p id="5ifC"><strong>Результат:</strong></p>
  <pre id="7yDc">🔥 Выполнен запрос! Ответ #3
</pre>
  <p id="DOcV">Теперь обе подписки получают <strong>одни и те же данные</strong> из одного источника. Это и есть эффект <strong>мультикастинга</strong>.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://osinpaul.ru/gjCt2Hd8EbD</guid><link>https://osinpaul.ru/gjCt2Hd8EbD?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul</link><comments>https://osinpaul.ru/gjCt2Hd8EbD?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul#comments</comments><dc:creator>osinpaul</dc:creator><title>Запуск Chrome в режиме бeз CORS</title><pubDate>Tue, 01 Jul 2025 10:29:20 GMT</pubDate><description><![CDATA[Во время локальной разработки часто возникает необходимость тестирования API-запросов к другим доменам. Браузер блокирует такие запросы из-за политики CORS, что затрудняет отладку. Для упрощения тестирования можно запустить Chrome в режиме без проверки CORS.]]></description><content:encoded><![CDATA[
  <p id="G7H3">Во время локальной разработки часто возникает необходимость тестирования API-запросов к другим доменам. Браузер блокирует такие запросы из-за политики CORS, что затрудняет отладку. Для упрощения тестирования можно запустить Chrome в режиме без проверки CORS.</p>
  <blockquote id="KkzH">⚠️ <strong>Внимание:</strong> запуск в таком режиме снижает безопасность. Используется <strong>только для разработки</strong>.</blockquote>
  <hr />
  <h2 id="8MLG">Инструкция по запуску</h2>
  <h3 id="UiBD">Windows</h3>
  <ol id="iWBj">
    <li id="k4aq">Закрыть все окна Chrome.</li>
    <li id="jSgx">Открыть <strong>Командную строку</strong> или <strong>PowerShell</strong>.</li>
    <li id="4PBS">Выполнить команду:</li>
  </ol>
  <pre id="CmuJ">start chrome --disable-web-security --user-data-dir=&quot;C:/chrome-dev-disable-cors&quot;
</pre>
  <p id="yiBQ"><strong>Пояснения:</strong></p>
  <ul id="T3ZU">
    <li id="sVyV"><code>--disable-web-security</code> отключает проверку политики безопасности, включая CORS.</li>
    <li id="81lW"><code>--user-data-dir=&quot;C:/chrome-dev-disable-cors&quot;</code> создаёт отдельный профиль для разработки, чтобы избежать конфликта с основным профилем.</li>
  </ul>
  <hr />
  <h3 id="RFeR">macOS</h3>
  <ol id="43gV">
    <li id="WlCa">Закрыть все окна Chrome.</li>
    <li id="qUBT">Открыть <strong>Terminal</strong>.</li>
    <li id="TW6b">Выполнить команду:</li>
  </ol>
  <pre id="38Rl">open -na &quot;Google Chrome&quot; --args --disable-web-security --user-data-dir=&quot;/tmp/chrome-dev-disable-cors&quot;
</pre>
  <hr />
  <h3 id="I7X3">Linux</h3>
  <ol id="rh09">
    <li id="YajM">Закрыть все окна Chrome.</li>
    <li id="DwAI">Выполнить в терминале:</li>
  </ol>
  <pre id="fxsE">google-chrome --disable-web-security --user-data-dir=&quot;/tmp/chrome-dev-disable-cors&quot;
</pre>
  <h2 id="oAkT"></h2>
  <h3 id="UAOU">А если без командной строки?<br /></h3>
  <p id="wp8n">Можно воспользоваться расширениями для браузера. Например, для <a href="https://chromewebstore.google.com/detail/cors-unblock/hadoojkfknbjgoppkecpgamiajljiief?hl=ru&utm_source=ext_sidebar" target="_blank">chrome</a>:  или <a href="https://mybrowseraddon.com/access-control-allow-origin.html" target="_blank">firefox. </a>Но, по моему опыту, поведение с ними иногда непредсказуемо. Лично ловил в хроме упавшие запросы по корсам, когда они вроде бы должны быть отключены.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://osinpaul.ru/MIpELP9Fj3v</guid><link>https://osinpaul.ru/MIpELP9Fj3v?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul</link><comments>https://osinpaul.ru/MIpELP9Fj3v?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=osinpaul#comments</comments><dc:creator>osinpaul</dc:creator><title>REST vs GraphQL</title><pubDate>Tue, 01 Apr 2025 10:27:19 GMT</pubDate><category>Теория 📔</category><description><![CDATA[В мире веб-разработки REST и GraphQL являются двумя наиболее популярными подходами к организации взаимодействия между клиентом и сервером. Несмотря на то, что оба подхода решают схожие задачи — передача данных через API — у них есть ключевые различия, которые могут повлиять на выбор в зависимости от потребностей проекта.]]></description><content:encoded><![CDATA[
  <p id="Qt7S">В мире веб-разработки REST и GraphQL являются двумя наиболее популярными подходами к организации взаимодействия между клиентом и сервером. Несмотря на то, что оба подхода решают схожие задачи — передача данных через API — у них есть ключевые различия, которые могут повлиять на выбор в зависимости от потребностей проекта.</p>
  <h3 id="S8ZQ"><strong>Что такое REST?</strong></h3>
  <p id="RSXX">REST (Representational State Transfer) — это архитектурный стиль, который описывает взаимодействие между клиентом и сервером с использованием стандартных HTTP-методов (GET, POST, PUT, DELETE) и структурированных URL. RESTful API обычно строится вокруг ресурсов (например, пользователей, постов, товаров), и каждое действие над ресурсом выполняется через соответствующий HTTP-метод.</p>
  <p id="4uHp"><strong>Пример запроса в REST:</strong></p>
  <ul id="Or5x">
    <li id="lJyS">Получение списка пользователей:<br /> <code>GET /users</code></li>
    <li id="KK0f">Получение одного пользователя:<br /> <code>GET /users/{id}</code></li>
    <li id="lGx0">Создание нового пользователя:<br /> <code>POST /users</code></li>
  </ul>
  <h3 id="ISYo"><strong>Что такое GraphQL?</strong></h3>
  <p id="t9kV">GraphQL — это язык запросов для API, который позволяет клиентам запрашивать только те данные, которые им действительно нужны. В отличие от REST, который подразумевает использование множества разных конечных точек, GraphQL имеет одну точку входа, через которую можно получить все необходимые данные.</p>
  <p id="NI8w"><strong>Пример запроса в GraphQL:</strong></p>
  <p id="FnfT">Запрос на получение информации о пользователе и его постах:</p>
  <pre id="WUq7" data-lang="graphql">{
  user(id: &quot;1&quot;) {
    name
    posts {
      title
      content
    }
  }
}</pre>
  <p id="u4Vd">Этот запрос возвращает только нужные данные, без излишней информации.</p>
  <h3 id="tnRQ"><strong>Основные различия:</strong></h3>
  <ol id="SIaY">
    <li id="Ta5X"><strong>Количество запросов:</strong></li>
    <ul id="7tYb">
      <li id="t1BV"><strong>REST:</strong> Для получения данных с нескольких ресурсов потребуется несколько запросов. Например, если нужно получить пользователя и его посты, потребуется два запроса: один для получения пользователя, второй — для получения постов этого пользователя.</li>
      <li id="UYbf"><strong>GraphQL:</strong> С помощью одного запроса можно получить все необходимые данные, независимо от количества связанных сущностей. В нашем примере достаточно одного запроса для получения и пользователя, и его постов.</li>
    </ul>
    <li id="NX8y"><strong>Гибкость запросов:</strong></li>
    <ul id="1zjx">
      <li id="LMKA"><strong>REST:</strong> Сервер определяет, какие данные и в каком формате клиент получает в ответ. Это может привести к избыточным данным или, наоборот, к необходимости делать несколько запросов для получения нужной информации.</li>
      <li id="bl3J"><strong>GraphQL:</strong> Клиент сам определяет, какие именно данные ему нужны. Это позволяет сократить объем передаваемых данных и повысить эффективность взаимодействия.</li>
    </ul>
    <li id="g7g4"><strong>Структура данных:</strong></li>
    <ul id="cKfc">
      <li id="N6so"><strong>REST:</strong> Каждый ресурс имеет свой собственный URL и обычно возвращает фиксированную структуру данных.</li>
      <li id="3K8y"><strong>GraphQL:</strong> Все запросы выполняются через один URL, а структура ответа зависит от запроса клиента. Это даёт более высокую степень кастомизации.</li>
    </ul>
    <li id="SF8m"><strong>Обработка ошибок:</strong></li>
    <ul id="2n3N">
      <li id="sfM5"><strong>REST:</strong> Ошибки обрабатываются через статус-коды HTTP (например, 404 для &quot;не найдено&quot;, 500 для &quot;ошибка сервера&quot;).</li>
      <li id="KGH5"><strong>GraphQL:</strong> Ошибки можно передавать в теле ответа. Они не блокируют успешное получение данных — даже если часть запроса не выполнена, остальные данные могут быть возвращены.</li>
    </ul>
    <li id="2XcC"><strong>Сложность реализации:</strong></li>
    <ul id="jYFR">
      <li id="tqEK"><strong>REST:</strong> Более прост в реализации, особенно для небольших приложений. В большинстве случаев используется стандартная структура URL и методы.</li>
      <li id="KCrA"><strong>GraphQL:</strong> Сложнее в настройке, но даёт более мощные инструменты для работы с данными. Требует более тщательного планирования схемы API.</li>
    </ul>
  </ol>
  <h3 id="BZDt"><strong>Сходства:</strong></h3>
  <ol id="5Tth">
    <li id="54GK"><strong>Обе технологии используют HTTP:</strong> Несмотря на разные подходы, REST и GraphQL работают через HTTP-протокол и могут использовать схожие механизмы аутентификации, например, с использованием токенов.</li>
    <li id="CGYt"><strong>Обе предлагают механизмы фильтрации данных:</strong> В REST это обычно происходит через параметры запроса (например, <code>?page=1&amp;limit=10</code>), а в GraphQL — через поля запроса.</li>
    <li id="sYm1"><strong>Обе позволяют работать с различными типами данных:</strong> и REST, и GraphQL могут работать с текстовыми данными, изображениями, файлами и т.д.</li>
  </ol>
  <h3 id="zs3l"><strong>Когда использовать REST, а когда GraphQL?</strong></h3>
  <ul id="hhN4">
    <li id="0h4V"><strong>REST:</strong> Подходит для простых приложений, где структура данных не меняется часто, и нет сложных зависимостей между сущностями. Это классический выбор для большинства CRUD-приложений.</li>
    <li id="0O1R"><strong>GraphQL:</strong> Идеален для сложных приложений с множеством взаимосвязанных данных, где важно минимизировать количество запросов и оптимизировать трафик. Особенно полезен в случае, когда структура данных может изменяться, и нужно обеспечить гибкость запросов.</li>
  </ul>
  <h3 id="M3Nf"></h3>
  <p id="aYwD">REST и GraphQL — это два подхода с разной философией. REST ориентирован на ресурсы и стандартные HTTP-методы, в то время как GraphQL даёт клиентам полную гибкость в запросах. Выбор между ними зависит от требований к проекту: если важна простота и стандартность, лучше использовать REST; если нужна гибкость и эффективность, GraphQL может быть лучшим вариантом.</p>

]]></content:encoded></item></channel></rss>