Регистрация  |  Вход

Создаем LWC приложение (часть 1)

В качестве саморазвития решил поставить перед собой такую задачу - написать LWC приложение и освятить этот процесс тут в виде своего рода туториала для будущих поколений.
Собственно первый шаг - мотивация. Последние пару лет немного отошел от темы Salesforce и пропустил целый пласт информации в виде LWC. И как показывает общение с коллегами, без данной темы в Salesforce сегодня уже не пройдешь. Поэтому буду в свободное время погружаться в LWC и применять знания на практике. Опытные коллеги, кто трудится в этой области, приглашаю делиться опытом по мере освещения отдельных моментов и поправлять понятия которые я буду брать из других направлений типа Angular. Новичкам будет полезно увидеть реальный практический пример.

Итак, буду писать SPA - универсальный конструктор для "карточек" привязанных к карте. У нас будет база данных карточек с навигаций, поиском, различным функционалом вокруг этих карточек. Карточка - такая динамическая сущность которая будет привязана к координатам, будет иметь набор базовых полей, набор динамических полей (задаваемых конфигурацией). Также прикрутим загрузку изображений с интеграцией на AWS S3. Можно будет привязать систему комментариев к карточке, чтобы мутить какое-то общение. Попробуем поиграться с отдельными от SPA компонентами для интеграции в разных частях SF, по типу "лучшая карточка", "5 последних карточек". Итогом будет созданный Managed Package готовый для установки на другие орги.

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

ЗЫ: Люблю начинать новые домашние проекты, но очень редко получается довести все до конца.
ЗЫЫ: Как раз завис сейчас в разработке интересный проект на .Net Core + Angular для бэкапа Salesforce (данных и кода), но что-то мотивация упала и проект стал вяло тянуться. Если кому интересно, буду рад обсудить!!!
В качестве саморазвития решил поставить перед собой такую задачу - написать LWC приложение и освятить этот процесс тут в виде своего рода туториала для будущих поколений. 
Собственно первый шаг - мотивация. Последние пару лет немного отошел от темы Salesforce и пропустил целый пласт информации в виде LWC. И как показывает общение с коллегами, без данной темы в Salesforce сегодня уже не пройдешь. Поэтому буду в свободное время погружаться в LWC и применять знания на практике. Опытные коллеги, кто трудится в этой области, приглашаю делиться опытом по мере освещения отдельных моментов и поправлять понятия которые я буду брать из других направлений типа Angular. Новичкам будет полезно увидеть реальный практический пример.

Итак, буду писать SPA - универсальный конструктор для "карточек" привязанных к карте. У нас будет база данных карточек с навигаций, поиском, различным функционалом вокруг этих карточек. Карточка - такая динамическая сущность которая  будет привязана к координатам, будет иметь набор базовых полей, набор динамических полей (задаваемых конфигурацией). Также прикрутим загрузку изображений с интеграцией на AWS S3. Можно будет привязать систему комментариев к карточке, чтобы мутить какое-то общение. Попробуем поиграться с отдельными от SPA компонентами для интеграции в разных частях SF, по типу "лучшая карточка", "5 последних карточек". Итогом будет созданный Managed Package готовый для установки на другие орги.

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

ЗЫ: Люблю начинать новые домашние проекты, но очень редко получается довести все до конца.
ЗЫЫ: Как раз завис сейчас в разработке интересный проект на .Net Core + Angular для бэкапа Salesforce (данных и кода), но что-то мотивация упала и проект стал вяло тянуться. Если кому интересно, буду рад обсудить!!!
Самое главное и 50% успеха любого дела это правильное название
Пусть наше приложение называется MagicCard.
Создаем дев орг и пробуем застолбить этот Namespage. Namespace свободен
Значит у нас будет пакет с этим именем.
Самое главное и 50% успеха любого дела это правильное название :smiley:
Пусть наше приложение называется [b]MagicCard[/b].
Создаем дев орг и пробуем застолбить этот Namespage. Namespace свободен :party:
Значит у нас будет пакет с этим именем. 
Для новичков вот тут волшебная страница, которая позволяет создать Developer Org абсолютно бесплатно
https://developer.salesforce.com/signup

Докментация по LWC
https://developer.salesforce.com/docs/co ... oduction

Trailhead
https://trailhead.salesforce.com/en/cont ... mponents
Для новичков вот тут волшебная страница, которая позволяет создать Developer Org абсолютно бесплатно
https://developer.salesforce.com/signup

Докментация по LWC 
https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.get_started_introduction

Trailhead
https://trailhead.salesforce.com/en/content/learn/trails/build-lightning-web-components
Восстанавливаю тему. Сразу после создания скрыл ее на следующий день. Что-то передумал вести ее так сказать в прямом эфире. И вот спустя 10 дней по календарю думаю можно уже писать опираясь на полученный опыт. В ходе разбирательств с LWC было куча вопросов которые обсуждал с коллегами и теперь буду здесь дублировать чтобы желающие могли присоединиться.
Восстанавливаю тему. Сразу после создания скрыл ее на следующий день. Что-то передумал вести ее так сказать в прямом эфире. И вот спустя 10 дней по календарю думаю можно уже писать опираясь на полученный опыт. В ходе разбирательств с LWC было куча вопросов которые обсуждал с коллегами и теперь буду здесь дублировать чтобы желающие могли присоединиться.
На сегодня получилось замутить вот такую штуку:

Делал LWC приложение по принципу Ангуляр - все в одном монолите. То есть у меня есть один большой LWC компонент - контейнер для все бизнес логики. Засунул его в Lightning App Builder в виде App Page. Кстати верхняя панель с названием "Magic Card App" именно из-за App Page, само приложение начинается ниже (это кстати можно обойти, позже расскажу как).
В плане бизнес логики пока есть только List View и New Card Modal. Ничего сверх естественного кроме карты справа и в модалке. С этим пришлось повоевать, но полученный опыт и база данных для копипаста в будущие проекты очень полезны.
В общем на List View у нас две секции:
- слева список объектов MagicCard
- справа LMC компонент с картой. Карта это не встроенный компонент lightning-map, полноценный Google Maps API JS либа.
На карте отображаются MagicCards в порядке удаления от центра. Для этого используется поле с типом geolocation и вот такая штука https://developer.salesforce.com/docs/at ... cate.htm#:~:text=SOQL%20supports%20using%20simple%20numeric,%2C%20calculations%2C%20and%20so%20on. Теоретически можно отображать курсоры в пределах окружности которая примерно вписывается в окно карты (искать в пределах перпендикуларя Salesforce я так понял не умеет).
А теперь магия - при двойном клике открывается окно создания нового MagicCard с координатами в месте клика. На модалке кроме обычных полей присутствует та же самая карта но с другой логикой - курсор всегда находится в центре и если подвигать карту то координаты меняются и автоматически записываются в создаваемую карточку. Такой интересный виджет для Location поля получился. Сохранив новую карточку модалка закрывается и список с картой перестраиваются (на карте появляется новый маркер). Также интересный эффект при наведении на Карточку с списке она подсвечивается и также подсвечивается маркер на карте.





На сегодня получилось замутить вот такую штуку:

Делал LWC приложение по принципу Ангуляр - все в одном монолите. То есть у меня есть один большой LWC компонент - контейнер для все бизнес логики. Засунул его в Lightning App Builder в виде App Page. Кстати верхняя панель с названием "Magic Card App" именно из-за App Page, само приложение начинается ниже (это кстати можно обойти, позже расскажу как). 
В плане бизнес логики пока есть только List View и New Card Modal. Ничего сверх естественного кроме карты справа и в модалке. С этим пришлось повоевать, но полученный опыт и база данных для копипаста в будущие проекты очень полезны. 
В общем на List View у нас две секции:
- слева список объектов MagicCard
- справа LMC компонент с картой. Карта это не встроенный компонент lightning-map, полноценный Google Maps API JS либа.
На карте отображаются MagicCards в порядке удаления от центра. Для этого используется поле с типом geolocation и вот такая штука https://developer.salesforce.com/docs/atlas.en-us.236.0.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_geolocate.htm#:~:text=SOQL%20supports%20using%20simple%20numeric,%2C%20calculations%2C%20and%20so%20on. Теоретически можно отображать курсоры в пределах окружности которая примерно вписывается в окно карты (искать в пределах перпендикуларя Salesforce я так понял не умеет). 
А теперь магия - при двойном клике открывается окно создания нового MagicCard с координатами в месте клика. На модалке кроме обычных полей присутствует та же самая карта но с другой логикой - курсор всегда находится в центре и если подвигать карту то координаты меняются и автоматически записываются в создаваемую карточку. Такой интересный виджет для Location поля получился. Сохранив новую карточку модалка закрывается и список с картой перестраиваются (на карте появляется новый маркер). Также интересный эффект при наведении на Карточку с списке она подсвечивается и также подсвечивается маркер на карте.

[img]https://images.salesforce-developer.ru/magiccard_1.png[/img]

[img]https://images.salesforce-developer.ru/magiccard_2.png[/img]

[img]https://images.salesforce-developer.ru/magiccard_3.png[/img]
Поговорим подробнее про карту и подводные камни при ее создании.

Так как LWC обвешаны разного рода ограничениями безопасности, то использовать сторонние JS либы крайне сложно. Надо во первых быть уверенным что Locker или LWS (Lightning Web Security) (пока еще до конца в них не разобрался) не порежут функционал либы и грузить саму либу надо в Static Resources (если что не так пишу, поправляйте). Использование либ которые грузятся с внешнего источника по типа Google Maps Javascript API (https://developers.google.com/maps/docum ... vascript) затруднено (может и можно как-то этот JS выгрузить и залить в Static Resources, но я не особо верю что такой подход работает). В общем следуя инструкции от Гугла их либы надо грузить из https://maps.googleapis.com/
Погуглив разные источники в интернет наткнулся на совет использовать Visualforce страницу и засунуть ее в LWC в виде ифрейма. Не вспомню где читал, может это давно так работало, но в советах упоминали что с таким подходом надо использовать Lightning Message Service (LMS). Но он нифига не заработал. И в документации к Salesforce написано что lightning фичи в Visualforce который в iframe не работают. У меня естественно LMS не заработал, хотя при некоторых условиях использования VF где-то в контексте Lightning он должен работать. Вроде если использовать VF внутри Lighting App Builder и положить страницу просто рядом с LWC компонентом. Но это не мой случай - у меня монолит. Нужно ковыряться в самом LWC.

После ряда изысканий решение было получено - подробнее здесь https://salesforce-developer.ru/lightnin ... 12616054
Поговорим подробнее про карту и подводные камни при ее создании. 

Так как LWC обвешаны разного рода ограничениями безопасности, то использовать сторонние JS либы крайне сложно. Надо во первых быть уверенным что Locker или LWS (Lightning Web Security) (пока еще до конца в них не разобрался) не порежут функционал либы и грузить саму либу надо в Static Resources (если что не так пишу, поправляйте). Использование либ которые грузятся с внешнего источника по типа Google Maps Javascript API (https://developers.google.com/maps/documentation/javascript) затруднено (может и можно как-то этот JS выгрузить и залить в Static Resources, но я не особо верю что такой подход работает). В общем следуя инструкции от Гугла их либы надо грузить из https://maps.googleapis.com/ 
Погуглив разные источники в интернет наткнулся на совет использовать Visualforce страницу и засунуть ее в LWC в виде ифрейма. Не вспомню где читал, может это давно так работало, но в советах упоминали что с таким подходом надо использовать Lightning Message Service (LMS). Но он нифига не заработал. И в документации к Salesforce написано что lightning фичи в Visualforce который в iframe не работают. У меня естественно LMS не заработал, хотя при некоторых условиях использования VF где-то в контексте Lightning он должен работать. Вроде если использовать VF внутри Lighting App Builder и положить страницу просто рядом с LWC компонентом. Но это не мой случай - у меня монолит. Нужно ковыряться в самом LWC.

После ряда изысканий решение было получено - подробнее здесь https://salesforce-developer.ru/lightning-message-service-v-visualforce-1649412616054


Итогом стала возможность слать сообщения через PostMessage как внутрь iframe, так и из iframe. Осталось только завернуть iframe с картой в красивый отдельный LWC компонент и обвязать обычными properties и event handlers чтобы скрыть всю эту магию вокруг iframe. Получилось очень даже интересно. Обозвал компонент extendedMap

Вот пример использования

<c-extended-map
                onmapready={mapReadyHandler}
                onmapdbclick={mapDBClickHandler}></c-extended-map>
В этом случае компонент сообщает что карта загрузилась и прорисовалась через событие onmapready, и можно отловить mapDBClickHandler, который содержит информацию о координатах клика

<c-extended-map
              map-height="200"
              lat={initialLocationLat}
              lng={initialLocationLng}
              mode="center"
              onmapcenter={mapCenterChangeHandler}></c-extended-map>
Этот вариант для модалки. Через атрибуты мы переключаем логику работы карты (mode), координаты цента (lat, lng). А также отлавливаем событие перемещения карты (изменение центра) получая новые координаты.

Еще один интересный вариант общения между компонентами который активно использую в extendedMap это непосредственный вызов методов внутри компонента из родителя. Не так явно как с properties и event handlers, но тоже очень полезный инструмент

this.template.querySelector('c-extended-map').reloadCards();
Этот код из компонента который содержит компонент карты и с помощью него мы просим карту перестроить маркеры. К примеру когда моздается новая карточка.
Итогом стала возможность слать сообщения через PostMessage как внутрь iframe, так и из iframe. Осталось только завернуть iframe с картой в красивый отдельный LWC компонент и обвязать обычными properties и event handlers чтобы скрыть всю эту магию вокруг iframe. Получилось очень даже интересно. Обозвал компонент extendedMap :smile:

Вот пример использования

[code]
<c-extended-map
                onmapready={mapReadyHandler}
                onmapdbclick={mapDBClickHandler}></c-extended-map>
[/code]
В этом случае компонент сообщает что карта загрузилась и прорисовалась через событие onmapready, и можно отловить mapDBClickHandler, который содержит информацию о координатах клика

[code]
<c-extended-map
              map-height="200"
              lat={initialLocationLat}
              lng={initialLocationLng}
              mode="center"
              onmapcenter={mapCenterChangeHandler}></c-extended-map>
[/code]
Этот вариант для модалки. Через атрибуты мы переключаем логику работы карты (mode), координаты цента (lat, lng). А также отлавливаем событие перемещения карты (изменение центра) получая новые координаты.

Еще один интересный вариант общения между компонентами который активно использую в extendedMap это непосредственный вызов методов внутри компонента из родителя. Не так явно как с properties и event handlers, но тоже очень полезный инструмент

[code]this.template.querySelector('c-extended-map').reloadCards();[/code]
Этот код из компонента который содержит компонент карты и с помощью него мы просим карту перестроить маркеры. К примеру когда моздается новая карточка.

Дале буду освещать более детальные вопросы с которыми столкнулся (@wire, Service Component, Forms, Navigation, ...)
Дале буду освещать более детальные вопросы с которыми столкнулся (@wire, Service Component, Forms, Navigation, ...) :smile: 
@Wire и Service Components.

В ангуляре я давно выработал простую но эффективную архитектуру приложения.

UI <-> Components <-> Services (Singletons) <-> Backend

Компоненты отвечают только за работу с UI, никакой бизнес логики. Вся бизнес логика в Сервисах, в том числе любая логика для работы с бэкендом. Сингелтоны для того чтобы тут же в сервисах хранить данные. Короче сервисы это такие Store с бизнес логикой. Не выношу сторы отдельно, не вижу в этом смысла. Такая модель прекрасно себя зарекомендовала во многих проектах и проста в понимании любого уровня разработчиками.

Попытался замутить похожее в LWC. Благо в LWC тоже есть такое понятие как Service Components (https://developer.salesforce.com/blogs/2 ... mponents) но мне не очень нравится как в статье это описано.

Я реализовал свой сервис компонент в таком виде

import RETRIEVE_CARDS_APEX from '@salesforce/apex/MagicCardController.RetrieveCards';
import SAVE_CARD_APEX from '@salesforce/apex/MagicCardController.SaveCard';
import { CommonService } from "c/commonService";

class RemoteService {

  static cards = null;

  static saveCards(card) {
   ...
  }

  static getCards() {
   ...
  }

}ето
export { RemoteService }

Сервис компонент это просто JS файл в отдельной папке. Кстати IC2 умеет генерить шаблон под Service Component с классом внутри, значит я на правильном пути. Но я использую static методы и проперти внутри. Таким образом получаем singleton со своим state который можем шарить между компонентами. К тому же такий подход с классом (а не просто набросанные функции и переменные) позволяет сделать красивый и понятный вызов метода. Сразу видно откуда метод растет, а не просто вызов какой-то непонятной функции болтается

let cards = await RemoteService.getCards();

Так как сервис у нас есть, то инкапсулируем бизнес логику и общение с бэкендом. И вот тут вспылвает подводный камень.

Если к Apex Method мы можем обратиться из Сервис компонента

import RETRIEVE_CARDS_APEX from '@salesforce/apex/MagicCardController.RetrieveCards';
import SAVE_CARD_APEX from '@salesforce/apex/MagicCardController.SaveCard';

то использовать @wire нет. @wire работает только внутри обычного компонента который наследует LightningElement и используется в DOM. Можно конечно сервис компонент тоже сделать LightningElement с пустым template и засунуть в root component. Но какой-же это костыль . Поэтому с моим подходом остается использовать только императивный вызов Apex (https://developer.salesforce.com/docs/co ... perative) и реализовывать получение данных вручную. НО ЕСЛИ ЧЕСТНО мне это даже больше нравится, больше контроля над процессом работы с данными. Ненавижу магию. И то что я увидел в @wire повергло меня в шок. Конечно в самых простых случаях @wire можно использовать, но в более серьезных задачах это бомба замедленного действия. Каждый @wire отрабатывается асинхронно и в том же конструкторе connectedCallback данных еще нет. Если надо произвести какие-то манипуляции с двумя и более @wire атрибутами, то я вижу только кучу костылей. Задумка хорошая, но реализация подвела - хотя может я еще не постиг дзен Salesforce .
Вот к примеру мне понравился этот метод
@wire(getObjectInfo, { objectApiName: CARD_OBJECT })
получаем все метаданные (объекта + полей) для любого объекта. Но опять же, могу использовать это только внутри UI компонента но не в сервисе, а также ХЗ когда оно у меня появится (надо делать handler и отлавливать событие). Бррр.
В моем с сервисами придется мутить костыли в LWC или доставать метаданные через Apex метод вручную.
@Wire и Service Components. 

В ангуляре я давно выработал простую но эффективную архитектуру приложения.

UI <-> Components <-> Services (Singletons) <-> Backend

Компоненты отвечают только за работу с UI, никакой бизнес логики. Вся бизнес логика в Сервисах, в том числе любая логика для работы с бэкендом. Сингелтоны для того чтобы тут же в сервисах хранить данные. Короче сервисы это такие Store с бизнес логикой. Не выношу сторы отдельно, не вижу в этом смысла. Такая модель прекрасно себя зарекомендовала во многих проектах и проста в понимании любого уровня разработчиками.

Попытался замутить похожее в LWC. Благо в LWC тоже есть такое понятие как Service Components (https://developer.salesforce.com/blogs/2019/05/lightning-web-components-service-components) но мне не очень нравится как в статье это описано.

Я реализовал свой сервис компонент в таком виде

[code]
import RETRIEVE_CARDS_APEX from '@salesforce/apex/MagicCardController.RetrieveCards';
import SAVE_CARD_APEX from '@salesforce/apex/MagicCardController.SaveCard';
import { CommonService } from "c/commonService";

class RemoteService {

  static cards = null;

  static saveCards(card) {
   ...
  }

  static getCards() {
   ...
  }

}ето
export { RemoteService }
[/code]

Сервис компонент это просто JS файл в отдельной папке. Кстати IC2 умеет генерить шаблон под Service Component с классом внутри, значит я на правильном пути. Но я использую static методы и проперти внутри. Таким образом получаем singleton со своим state который можем шарить между компонентами. К тому же такий подход с классом (а не просто набросанные функции и переменные) позволяет сделать красивый и понятный вызов метода. Сразу видно откуда метод растет, а не просто вызов какой-то непонятной функции болтается

[code]
let cards = await RemoteService.getCards();
[/code]

Так как сервис у нас есть, то инкапсулируем бизнес логику и общение с бэкендом. И вот тут вспылвает подводный камень.

Если к Apex Method мы можем обратиться из Сервис компонента

[code]
import RETRIEVE_CARDS_APEX from '@salesforce/apex/MagicCardController.RetrieveCards';
import SAVE_CARD_APEX from '@salesforce/apex/MagicCardController.SaveCard';
[/code]

то использовать @wire нет. @wire работает только внутри обычного компонента который наследует LightningElement и используется в DOM. Можно конечно сервис компонент тоже сделать LightningElement с пустым template и засунуть в root component. Но какой-же это костыль :so-sad:. Поэтому с моим подходом остается использовать только императивный вызов Apex (https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.apex_call_imperative) и реализовывать получение данных вручную. НО ЕСЛИ ЧЕСТНО мне это даже больше нравится, больше контроля над процессом работы с данными. Ненавижу магию. И то что я увидел в @wire повергло меня в шок. Конечно в самых простых случаях @wire можно использовать, но в более серьезных задачах это бомба замедленного действия.  Каждый @wire отрабатывается асинхронно и в том же конструкторе connectedCallback данных еще нет. Если надо произвести какие-то манипуляции с двумя и более @wire атрибутами, то я вижу только кучу костылей. Задумка хорошая, но реализация подвела - хотя может я еще не постиг дзен Salesforce :rolling:. 
Вот к примеру мне понравился этот метод
[code]
@wire(getObjectInfo, { objectApiName: CARD_OBJECT })
[/code]
получаем все метаданные (объекта + полей) для любого объекта. Но опять же, могу использовать это только внутри UI компонента но не в сервисе, а также ХЗ когда оно у меня появится (надо делать handler и отлавливать событие). Бррр.
В моем с сервисами придется мутить костыли в LWC или доставать метаданные через Apex метод вручную.


Поделитесь вашим подходом к архитектуре LWC приложения.
Поделитесь вашим подходом к архитектуре LWC приложения. 
По поводу форм.

Я привык работать с DTO объектами. Не люблю использовать на фронтенде SObjects.

Поэтому стандартные компоненты из области lightning-record-*-form (https://developer.salesforce.com/docs/co ... ut_intro) не подходят для использования.

Формы и обработчики надо пилить вручную. Собственно так и делается в Ангуляре.

Посему вопрос к вам, опытные коллеги. Как вы работаете с формами? Используете стандартные компоненты поверх SObjects?
Или полностью кастомно все реализовываете? Или может какой-то комбинированный подход?
По поводу форм. 

Я привык работать с DTO объектами. Не люблю использовать на фронтенде SObjects.

Поэтому стандартные компоненты из области lightning-record-*-form (https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.data_get_user_input_intro) не подходят для использования.

Формы и обработчики надо пилить вручную. Собственно так и делается в Ангуляре. 

Посему вопрос к вам, опытные коллеги. Как вы работаете с формами? Используете стандартные компоненты поверх SObjects?
Или полностью кастомно все реализовываете? Или может какой-то комбинированный подход?
По поводу навигации такой вопрос.

В Ангуляре мне очень нравилось использовать Routing + навигацию через адресную строку.

Скажем есть у меня Список каких-то объектов с поиском + страницы View / Edit.

чтобы со списка переейти на View или Edit мне достаточно было изменить URL текущей страницы (через #) чтобы Routing изменение отловил и открыл мне нужный компонент. Или скажем на списке поиск с кучей параметров - все параметры сразу мапятся на URL. Таким образом я могу попасть в нужное место моего SPA просто скопировав URL из адресной строки. Если открыть данный урл в пустой табе, то SPA откроет нужный компонент и передаст туда параметры.

Как дела с этим в LWC?
По поводу навигации такой вопрос.

В Ангуляре мне очень нравилось использовать Routing + навигацию через адресную строку.

Скажем есть у меня Список каких-то объектов с поиском + страницы View / Edit.

чтобы со списка переейти на View или Edit мне достаточно было изменить URL текущей страницы (через #) чтобы Routing изменение отловил и открыл мне нужный компонент. Или скажем на списке поиск с кучей параметров - все параметры сразу мапятся на URL. Таким образом я могу попасть в нужное место моего SPA просто скопировав URL из адресной строки. Если открыть данный урл в пустой табе, то SPA откроет нужный компонент и передаст туда параметры. 

Как дела с этим в LWC?
Кстати крайне удивился что в LWC до сих пор нет стандартного компонента для модалки.

Замутить конечно не проблема, но почему до такого простого решения до сих пор никто не додумался. С помощью slot такое организовать проще простого.

<template>
  <section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true" class="slds-modal slds-fade-in-open">
    <div class="slds-modal__container">
      <div class="slds-modal__header">
        <h1 id="modal-heading-01" class="slds-modal__title slds-hyphenate">{title}</h1>
      </div>
      <div class="slds-modal__content slds-p-around_medium">
        <slot></slot>
      </div>
      <div class="slds-modal__footer">
        <slot name="actions"></slot>
      </div>
    </div>
  </section>
  <div class="slds-backdrop slds-backdrop_open" role="presentation"></div>
</template>

А использовать

...
    <c-slds-modal title="Edit Card" if:true={showModal}>
        <div>
            ...
        </div>
        <div slot="actions">
            <lightning-button label="Cancel" title="Cancel" onclick={closeCardModal}></lightning-button>&nbsp;
            <lightning-button variant="brand" label="Save" title="Save Card" onclick={saveCard}></lightning-button>
        </div>
    </c-slds-modal>
...

При чем не нужны никакие хендлеры, проброс атрибутов.

Хотя наверное простота реализации и говорит о том что нет стандартной модалки, но я все-таки ожидал что такая базовая логика уже реализована.
Кстати крайне удивился что в LWC до сих пор нет стандартного компонента для модалки.

Замутить конечно не проблема, но почему до такого простого решения до сих пор никто не додумался. С помощью slot такое организовать проще простого.

[code]
<template>
  <section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true" class="slds-modal slds-fade-in-open">
    <div class="slds-modal__container">
      <div class="slds-modal__header">
        <h1 id="modal-heading-01" class="slds-modal__title slds-hyphenate">{title}</h1>
      </div>
      <div class="slds-modal__content slds-p-around_medium">
        <slot></slot>
      </div>
      <div class="slds-modal__footer">
        <slot name="actions"></slot>
      </div>
    </div>
  </section>
  <div class="slds-backdrop slds-backdrop_open" role="presentation"></div>
</template>
[/code]

А использовать

[code]
...
    <c-slds-modal title="Edit Card" if:true={showModal}>
        <div>
            ...
        </div>
        <div slot="actions">
            <lightning-button label="Cancel" title="Cancel" onclick={closeCardModal}></lightning-button>&nbsp;
            <lightning-button variant="brand" label="Save" title="Save Card" onclick={saveCard}></lightning-button>
        </div>
    </c-slds-modal>
...
[/code]

При чем не нужны никакие хендлеры, проброс атрибутов.

Хотя наверное простота реализации и говорит о том что нет стандартной модалки, но я все-таки ожидал что такая базовая логика уже реализована.
Dmitry Shnyrev
едленного действия. Каждый @wire отрабатываетс
А еще wire может отрабатывать более одного раза, и это видно в chrome developer console
[quote="Dmitry Shnyrev"]едленного действия. Каждый @wire отрабатываетс[/quote]
А еще wire может отрабатывать более одного раза, и это видно в chrome developer console
Dmitry Shnyrev
Посему вопрос к вам, опытные коллеги. Как вы работаете с формами? Используете стандартные компоненты поверх SObjects?
Или полностью кастомно все реализовываете? Или может какой-то комбинированный подход?

У меня написан код, который на основе JSON генерит форму потом возвращает все введенные данные
[quote="Dmitry Shnyrev"]Посему вопрос к вам, опытные коллеги. Как вы работаете с формами? Используете стандартные компоненты поверх SObjects?
Или полностью кастомно все реализовываете? Или может какой-то комбинированный подход?[/quote]

У меня написан код, который на основе JSON генерит форму  потом возвращает все введенные данные
Dmitry Shnyrev
Если открыть данный урл в пустой табе, то SPA откроет нужный компонент и передаст туда параметры.
На сколько я знаю, тоскливо. Если ошибаюсь поправьте
[quote="Dmitry Shnyrev"]Если открыть данный урл в пустой табе, то SPA откроет нужный компонент и передаст туда параметры.[/quote]
На сколько я знаю, тоскливо. Если ошибаюсь поправьте
wilder
У меня написан код, который на основе JSON генерит форму потом возвращает все введенные данные
У меня кстати тоже возникло устойчивое желание такое запилить!!! Значит в правильном направлении думаю. Често боялся что полетит куча хейта в плане "надо использовать стандартные решения /компоненты и только потом городить свой огород".
[quote="wilder"]
У меня написан код, который на основе JSON генерит форму  потом возвращает все введенные данные[/quote]
У меня кстати тоже возникло устойчивое желание такое запилить!!! Значит в правильном направлении думаю. Често боялся что полетит куча хейта в плане "надо использовать стандартные решения /компоненты и только потом городить свой огород".
wilder
А еще wire может отрабатывать более одного раза, и это видно в chrome developer console
На это внимания не обращая, но это очень не круто если такое происходит. А как же их хваленый кэш.
[quote="wilder"]А еще wire может отрабатывать более одного раза, и это видно в chrome developer console[/quote]
На это внимания не обращая, но это очень не круто если такое происходит. А как же их хваленый кэш.
Dmitry Shnyrev
wilder
У меня написан код, который на основе JSON генерит форму потом возвращает все введенные данные
У меня кстати тоже возникло устойчивое желание такое запилить!!! Значит в правильном направлении думаю. Често боялся что полетит куча хейта в плане "надо использовать стандартные решения /компоненты и только потом городить свой огород".

Wilder, Max можете провести code review в этой теме?
https://salesforce-developer.ru/lwc-supe ... 17974230
[quote="Dmitry Shnyrev"][quote="wilder"]
У меня написан код, который на основе JSON генерит форму  потом возвращает все введенные данные[/quote]
У меня кстати тоже возникло устойчивое желание такое запилить!!! Значит в правильном направлении думаю. Често боялся что полетит куча хейта в плане "надо использовать стандартные решения /компоненты и только потом городить свой огород".[/quote]

Wilder, Max можете провести code review в этой теме?
https://salesforce-developer.ru/lwc-superform-component-1650017974230
Блин, как не хватает строгой типизации как в Typescript

В ангуляре все сильно упрощается с типизацией. Никто не копал в этом направлении?
Блин, как не хватает строгой типизации как в Typescript :so-sad:

В ангуляре все сильно упрощается с типизацией. Никто не копал в этом направлении? 
Dmitry Shnyrev
wilder
У меня написан код, который на основе JSON генерит форму потом возвращает все введенные данные
У меня кстати тоже возникло устойчивое желание такое запилить!!! Значит в правильном направлении думаю. Често боялся что полетит куча хейта в плане "надо использовать стандартные решения /компоненты и только потом городить свой огород".

Не полетит. Так как динамически ты не можешь вызывать другие компоненты, а так нет проблем
[quote="Dmitry Shnyrev"][quote="wilder"]
У меня написан код, который на основе JSON генерит форму  потом возвращает все введенные данные[/quote]
У меня кстати тоже возникло устойчивое желание такое запилить!!! Значит в правильном направлении думаю. Често боялся что полетит куча хейта в плане "надо использовать стандартные решения /компоненты и только потом городить свой огород".[/quote]

Не полетит. Так как динамически ты не можешь вызывать другие компоненты, а так нет проблем
wilder
Так как динамически ты не можешь вызывать другие компоненты,
Кстати, никогда не использовал динамический рендеринг компонентов. Хотя такая возможность есть в том же Ангуляр (https://angular.io/guide/dynamic-component-loader). Всегда хватало switch case (https://angular.io/api/common/NgSwitch) или простого if. Возможно есть какой-то выигрыш в производительности рендеринга, если использовать динамику, но пока не сталкивался с этой проблемой.
Кстати очень не порадовало что в LWC if:true не принимает expression, особенно когда у нужна проверка внутри цикла. Приходится городить огороды
[quote="wilder"]Так как динамически ты не можешь вызывать другие компоненты,[/quote]
Кстати, никогда не использовал динамический рендеринг компонентов. Хотя такая возможность есть в том же Ангуляр (https://angular.io/guide/dynamic-component-loader). Всегда хватало switch case (https://angular.io/api/common/NgSwitch) или простого if. Возможно есть какой-то выигрыш в производительности рендеринга, если использовать динамику, но пока не сталкивался с этой проблемой. 
Кстати очень не порадовало что в LWC if:true не принимает expression, особенно когда у нужна проверка внутри цикла. Приходится городить огороды :so-sad:
Кстати с VF iframe внутри LWC компонента можно теперь пилить полноценное Angular приложение а общение с окружением сделать через события на уровень самого компонента враппера. Хотя наверное такое уже никому и в голову не придет делать
Кстати с VF iframe внутри LWC компонента можно теперь пилить полноценное Angular приложение а общение с окружением сделать через события на уровень самого компонента враппера. Хотя наверное такое уже никому и в голову не придет делать :smiley: