Содержание
- Как сверстать вайрфрейм в Фигме
- Общее понятие о вайрфреймах и их особенности
- Что дает wireframe разработчику?
- Как использовать вайрфреймы на практике
- Грамотное создание структуры
- Заключение
- Правило внутреннего и внешнего в веб дизайне. Принцип близости
- Как изменение расстояний влияет на восприятие текста
- Пример 1 — Слова и буквы
- Пример 2 — Текстовый блок
- Пример 3 — Текст + картинка
- Пример 4 — Работа с заголовком
- An error has occurred this application may no longer respond until reloaded reload перевод
- Принцип работы проекта Blazor Server
- Класс Startup
- Страница _Host.cshtml
- Компонент App
- MainLayout
- Выбор компонентов
Как сверстать вайрфрейм в Фигме
Wireframes — особая концепция, которая используется для создания структуры и прототипа web-сайта и часто применяется опытными разработчиками. В переводе с английского слово означает «каркас», однако более точным определением является «прототип». Вайрфреймы выполняют важную роль в определении общей концепции сайта, поэтому каждый специалист в обязательном порядке должен знать их особенности и специфику. Лучше всего использовать данный инструмент в Фигме – программе, которая часто применяется при создании web-страничек.
Общее понятие о вайрфреймах и их особенности
На практике Wireframes помогает разработчику лучше понять, где и какие элементы размещать на макете. Неудивительно, почему создание вайрфреймов являются важнейшим этапом в любом дизайн-процессе. Ведь здесь требуется грамотно структурировать текстовые и графические данные, а также задать определенную иерархию. При помощи таких элементов можно визуализировать макет и облегчить задачу по правильному размещению объектов.
Для лучшего осознания того, какую роль wireframe играет в разработке дизайна сайта, можно представить его как главный архитектурный план здания. С его помощью можно также визуализировать концепцию или задумку сайта для того, чтобы показать и согласовать его с клиентом еще на этапе разработки. Каждый специалист согласится с тем, как важно с самого начала донести свои предложения до заказчика сайта, после чего предложить ему лучшее решение.
Ведь это снизит риск неточностей из-за недопонимания до минимума и позволит избежать необходимости менять концепцию, переделывать работу на поздних сроках сдачи проекта. Приложение Figma подходит для этой цели наилучшим образом, поскольку имеет не только понятный пользовательский интерфейс, но также широкий функционал, с помощью которого web-дизайнер может создавать разные Вайрфреймы.
Что дает wireframe разработчику?
Получается, что вайрфреймы по сути представляют собой основу цифрового проекта в грамотном и структурированном виде. Это важнейший этап, который обязательно выполняется на стадии проектирования сайта и позволяет получить своего рода «скелет» сайта. Такой инструмент дает следующие преимущества:
Простейший вайрфрейм можно набросать на листе бумаги А4, однако сегодня вместо этого лучше использовать Фигму. Это специализированная платформа, которая открывает широкие возможности для визуализации структуры сайта.
Как использовать вайрфреймы на практике
В своем классическом представлении Wireframes – это схема, которая состоит из черных, серых и белых блоков, где разработчик может также использовать дополнительные элементы в виде фигур и символов. В Фигме вначале создается «каркас», после чего он плавно превращается в конкретный интерфейс.
Таким образом, любой варфрейм — это не просто минималистичный шаблон, а определенный скелет, позволяющий сэкономить время и увидеть, каким образом будут распределены основные компоненты сайта.
Все опытные и дальновидные дизайнеры стараются в обязательном порядке использовать варфрейм, поскольку это значительно экономит время. С его помощью на разработку не нужно тратить несколько дней, что позволят справиться с поставленной задачей намного быстрее. При помощи Фигмы можно четко структурировать макет, показать границы и наладить требуемое взаимодействие элементов.
Грамотное создание структуры
На первом этапе web-разработчик всегда должен внимательно выслушать клиента, чтобы понять его задумку. Далее следует этап сбора информации и разработки макета, где особенное внимание уделяется схеме. При разработке важно учитывать фирменный стиль и бренд клиента, желаемую цветовую схему, шрифты и прочие компоненты, от чего напрямую будет зависеть визуализация сайта.
Важно помнить, что wireframe представляет собой «бесцветную» конструкцию, которая применяется для описания структуры сетевой странички в целом. Единственное, важно помнить, что вайрфрейм создается без красок, а поэтому у дизайнера должна быть хорошо развита фантазия и образное мышление. В целом правильная цепочка при создании UI интерфейса выглядит таким образом: визуальный набросок — wireframe — макет — верстка. В конце выполняется практическое тестирование.
Заключение
Вайфреймы в Фигме можно создавать очень удобно, формируя образы дизайна низкой точности в максимально короткие сроки. При правильном использовании они являются очень эффективными, позволяя статично отображать основные элементы интерфейса. С их помощью разработчик может изначально запланировать места конечного расположения элементов, чтобы не отвлекаться на текст, шрифт и цветовую гамму при разработке сайта.
Источник
Правило внутреннего и внешнего в веб дизайне. Принцип близости
Неряшливость веб-дизайнеров часто оправдывается фразой: “Я так вижу». Но художник и дизайнер — понятия немного разные.
Веб-дизайнер — специалист, работающий над внешним видом сайта. Он выбирает элементы, блоки, их размеры, порядок отображения на странице, цвета, композиции. Самое главное для веб-дизайнера — сделать макет сайта максимально удобным для пользователя.
В отличии от картин художника, результат работы веб-дизайнера нельзя потрогать. И в первую очередь важна не просто красота, а донесение информации до посетителей сайта.
Вы знаете, что основные правила веб-дизайна опираются на знание психологии? Человек воспринимает предметы, расположенные близко друг к другу как единое целое — так работает теория близости.
Хотя общее количество элементов осталось одинаковым. Так работает принцип близости в дизайне.
Поэтому внутренние расстояния между элементами должны быть меньше, чем внешние. Этот закон актуален как для букв и слов, так абзацев, картинок и модулей.
Рассмотрим еще один пример.
При несоблюдении правила внешнего и внутреннего в дизайне разные элементы часто образовывают случайные связи и вызывают в лучшем случае улыбку.
Как изменение расстояний влияет на восприятие текста
Интерлиньяж — это расстояние между строками текста. Для букв внутренним считается расстояние между ними, а внешним — межстрочный интервал.
А при добавлении заголовка к тексту улучшилась наглядность. При накладывании текста на серую подложку ее внутренние поля шире, чем расстояние между строками текста. Информация теперь больше привлекает внимание. Вы согласны?
Теперь рассмотрим принцип работы правила на примерах.
Пример 1 — Слова и буквы
В этом примере буквам очень тесно, а увеличенное внутреннее пространство символа «О» как-бы разрывает слово. | А так слово приобрело значимость и лучше читается. |
В этой фразе сливаются слова. | При увеличении расстояний между словами фраза обрела читабельность. |
А тут мы вытянули шрифт вверх. | Теперь логично уменьшить межбуквенное расстояние. |
Какая фраза выглядит самой компактной и лучше всего воспринимается? Та, которая написана по правилам внешнего и внутреннего расстояния — самая последняя.
Пример 2 — Текстовый блок
Абзац и заголовок — это самостоятельные объекты. Поэтому внимательно следите за расстояниями.
Пример 3 — Текст + картинка
Рассмотрим такой блок:
Пример 4 — Работа с заголовком
Заголовок не должен «зависать в воздухе». Он относится именно к тому блоку, о котором пойдет речь.
И напоследок несколько закономерностей из копилки правил веб-дизайнера.
При сборке макета сначала определите внешние и внутренние расстояния. И помните про принцип схожести — используйте одинаковые шрифты и стили, иначе результат будет смешным.
Запомнить все рекомендации очень затруднительно. И даже если вы выучите их наизусть, то найдутся новые. Ведь копилка веб-дизайнера бесконечна. Самое важное — применять правило ко всем частям макета — внутреннее расстояние должно быть меньше внешнего.
Источник
An error has occurred this application may no longer respond until reloaded reload перевод
В Blazor Server основная логика приложения располагается на стороне сервера. Если на стороне клиента присходят какие-то события, то посредством SignalR клиент посылает серверу информацию о произведенных действиях. Сервер получает эту информацию, обрабатывает ее и посылает клиенту ответ. Обновление элементов пользовательского интерфейса, обработка событий, вызовы JavaScript на клиентской стороне осуществляются посредством взаимодействия сервера и клиента через SignalR.
Создадим первое приложение с использованию Blazor Server. Для этого откроем среду Visual Studio 2019 или выше. Данная среда разработки имеет прекрасную поддержку фреймворка Blazor, в частности, уже по умолчанию шаблон для создания проекта приложения Blazor.
Итак, в Visual Studio при создании проекта выберем шаблон Blazor App :
После выбора данного шаблона дадим проекту какое-нибудь имя, например, HelloBlazorServerApp:
Далее нам откроется окно для выборка шаблона проекта Blazor. На момент написания статьи был доступен только один шаблон — Blazor Server App , который собствено и выберем:
В итоге будет создан следующий проект:
Можно отметить, что структура проекта Blazor похожа на проекты ASP.NET Core, в частности, проект для Razor Page. По сути мы имеем дело с проектом приложения ASP.NET Core, в рамках которого разворачивается функциональнось фреймворка Blazor.
Основные элементы проекта:
Папка wwwroot для хранения статических файлов, по умолчанию хранит используемые файлы css, в частности, файлы фреймворка bootstrap.
Папка Data хранит классы C#, которые описывают используемые данные (класс WeatherForecast) и сервисы (класс WeatherForecastService).
Папка Pages содержит страницы Razor Pages, определяющих визуальную часть приложения и его логику, а также компоненты Razor (располагаются в файлах с расширением *.razor ), которые представляют основное содержание страницы.
_Host.cshtml — главная страница (Razor Page) приложения, в рамках которой будут разворачиваться приложение.
Counter.razor хранит код компонента Counter, суть которого в определение счетчика, значение которого увеличивается при нажатии на кнопку.
Error.razor хранит код компонента Error, который применяется для вывода сообщения об ошибке.
FetchData.razor хранит код компонента FetchData, который с помощью сервиса WeatherForecastService получает некоторые данные и выводит их на веб-страницу
Index.razor хранит код компонента Index.
Папка Shared хранит дополнительные компоненты Razor
MainLayout.razor хранит код компонента MainLayout, который определяет структуру или компоновку страницы.
NavMenu.razor хранит код компонента NavMenu, который определяет элементы навигации
_Imports.razor содержит подключения пространств имен с помощью директивы using, которые будут подключаться в компоненты Razor (файлы с расширением .razor).
App.razor содержит определение корневого компонента приложения, который позволяет установить маршрутизацию между вложенными компонентами с помощью другого встроенного компонента Router.
Файл appsettings.json хранит конфигурацию приложения.
Файл Startup.cs представляет класс Startup, стандартный для приложения ASP.NET Core, где настраивается конвейер обработки запроса, внедряются сервисы и осуществляется конфигурация приложения.
Файл Program.cs содержит класс Program, который представляет точку входа в приложение. В данном случае это стандартный для приложения ASP.NET Core класс Program, который запускает и конфигурирует хост, в рамках которого разворачивается приложение с Blazor.
Таким образом, проект Blazor Server уже содержит некоторую базовую типовую функциональность, который позволяе нам запустить проект и оценить работу фреймворка. Итак, запустим проект. Вначале мы увидим код компонента Index:
С помощью меню в левой части страницы мы можем перейти к другим компонентам. Например, перейдем к компоненту Counter:
Или к компоненту FetchData, который с помощью сервиса WeatherForecastService получит некоторые данные и выведет их на страницу:
Принцип работы проекта Blazor Server
Теперь разберем, как вообще работает стандартный проект Blazor Server с типовым содержанием.
Класс Startup
Прежде всего, чтобы задействовать функциональность Blazor Server, надо в классе Startup добавить необходимые сервисы и настроить обработку запросов. Например, возьмем типовой класс Startup из проекта по умолчанию:
В методе ConfigureServices добавляются сервисы Blazor Server:
В методе Configure при определении конечных точек, с которыми будут связываться входящие запросы, вызывается метод MapBlazorHub() , который позволяет установить подключение с браузером посредством SignalR. Благодаря этом будет происходить обмен сообщениями между сервером и клиентов в режиме реального времени.
Кроме того, вызов метода MapFallbackToPage(«/_Host») позволяет установить страницу Razor Page по умолчанию для приложения (по умолчанию это страница Pages/_Host.cshtml ). Она и представляет собственно тот интерфейс, который увидит пользователь.
Страница _Host.cshtml
Страница Razor _Host.cshtml , которая располагается в папке Pages, является корневой страницей всего приложения. Когда к любой странице (компоненту) приходит запрос, то именно эта страница возвращается в ответ. А отдельные компоненты располагаются внутри этой страницы.
_Host.cshtml представляет типичную страницу Razor Page, которая содержит код C# и HTML:
В данном случае мы видим, что она определяет каркас веб-страницы, которую увидит пользователь в своем браузере. Но прежде всего следует обратить внимание на подключение внизу страницы скрипта _framework/blazor.server.js . Это автоматически подключаемый скрипт, который устанавливает подключение между браузером и сервером посредством SignalR.
Какое содержимое будет вставляться в страницу _Host.cshtml? Это определяет компонент App. На странице он подключается с помощью tag-хелпера :
Атрибут type указывает на тип класса компонента. В данном случае это класс App. И атрибут render-mode со значением ServerPrerendered указывает, что компонент будет предварительно рендерится в статический html-код, который затем будет загружаться на страницу.
Компонент App
Итак, на странцу _Host.cshtml загружается компонент App. Он располагается в файле App.razor в корне проекта:
Компонент App использует встроенный компонент Router , который добавляет возможность маршрутизации по вложенным компонентам. Его атрибут AppAssembly указывает на сборку, в которой следует искать запрошенные вложенные компоненты.
При запросе компонентов может быть две ситуации: запрошенный ресурс (компонент) найден и ресурс не найден. Соответственно для каждой из этих ситуаций определены соответственно два элемента: Found и NotFound
Компонент Found содержит другой компонент — RouteView . Через атрибут RouteData он получает контекст маршрутизации, который будут использоваться при обработке запроса. А другой атрибут — DefaultLayout устанавливает компонент, который будет определять компоновку (layout) содержимого — в данном случае это компонент MainLayout.
Комопонент NotFound определяет, как будет рендерится ответ, если компонент для обработки запроса не найден. С помощью вложенного компонента LayoutView определяется компонент, который будет задавать компоновку. В данном случае это опять же компонент MainLayout.
MainLayout
В обоих случаях в компоненте App, как было описано выше будет использоваться компонент MainLayout , который определен в файле MainLayout.razor в папке Shared:
Здесь мы видим, что этот компонент наследуется от класса LayoutComponentBase, который определяет некоторую базовую функцональность для подобных компонентов.
С помощью элемента добавляется компонент NavMenu из файла Shared/NavMenu.razor , который создает систему навигации. Благодаря чему при загрузке приложения в левой части станицы мы можем переходить внутри приложения по набору ссылок.
Другой отличительной особенностью компонента MainLayout является использование свойства Body , унаследованного от класса LayoutComponentBase:
Посредством свойства Body в определенном месте разметки будет рендерится выбранный для обработки запроса компонент. То есть именно за место вызова @Body будет добавляться контент компонентов Index, Counter и FetchData из папки Pages.
Выбор компонентов
Основные комопненты, которые представляют отдельные ресурсы и к которым пользователь может осуществлять запросы, располагаются в папке Pages — это компоненты Index, Counter, FetchData.
Возьмем самый простой компонент — Index:
Директива @page «/» указывает, что этот компонент будет сопоставляться с запросами к корню приложения, например, https://localhost:44304/ . То есть по сути этот компонент можно рассматривать как главную страницу приложения.
Или другой комопнент — Counter:
Данный компонент будет сопоставляться с запросами по пути «/counter», например, https://localhost:44304/counter .
Компонент counter также определяет некоторую логику на C#. В частности, он определяет переменную currentCount и метод IncrementCount, который увеличивает значение переменной:
В коде html мы можем установить привязку к переменным и методам компонента:
Метод привязывается к событию кнопки onclick, благодаря чему при нажатии на кнопку будет срабатывает метод IncrementCount, и пользователь увидит новое значение currentCount.
При этом следует отметить, что несмотря на то, что пользователь сможет увидеть в своем браузере и значение переменной currentCount и также сможет нажать на кнопку, которая сгенерирует событие нажатия, но вся логика C# срабатывает на сервере. То есть после нажатия на кнопку с помощью подключенного скрипта _framework/blazor.server.js на сервер через соединение SignalR будет отправляться информация о событии нажатии, а сервер в ответ высылает клиенту инструкции о том, как обновить содержимое веб-страницы.
Третий компонент — FetchData получает с помощью внедренного сервиса WeatherForecastService данные и выводит их в код html:
Источник
Последнее обновление: 20.05.2020
В Blazor Server основная логика приложения располагается на стороне сервера. Если на стороне клиента присходят какие-то события, то посредством
SignalR клиент посылает серверу информацию о произведенных действиях. Сервер получает эту информацию, обрабатывает ее и посылает клиенту ответ.
Обновление элементов пользовательского интерфейса, обработка событий, вызовы JavaScript на клиентской стороне
осуществляются посредством взаимодействия сервера и клиента через SignalR.
Создадим первое приложение с использованию Blazor Server. Для этого откроем среду Visual Studio 2019 или выше. Данная среда разработки
имеет прекрасную поддержку фреймворка Blazor, в частности, уже по умолчанию шаблон для создания проекта приложения Blazor.
Итак, в Visual Studio при создании проекта выберем шаблон Blazor App:
После выбора данного шаблона дадим проекту какое-нибудь имя, например, HelloBlazorServerApp:
Далее нам откроется окно для выборка шаблона проекта Blazor. На момент написания статьи был доступен только один шаблон —
Blazor Server App, который собствено и выберем:
В итоге будет создан следующий проект:
Можно отметить, что структура проекта Blazor похожа на проекты ASP.NET Core, в частности, проект для Razor Page. По сути мы имеем дело с
проектом приложения ASP.NET Core, в рамках которого разворачивается функциональнось фреймворка Blazor.
Основные элементы проекта:
-
Папка wwwroot для хранения статических файлов, по умолчанию хранит используемые файлы css, в частности, файлы фреймворка bootstrap.
-
Папка Data хранит классы C#, которые описывают используемые данные (класс WeatherForecast) и сервисы (класс WeatherForecastService).
-
Папка Pages содержит страницы Razor Pages, определяющих визуальную часть приложения и его логику, а также компоненты Razor (располагаются в файлах с
расширением *.razor), которые представляют основное содержание страницы.-
_Host.cshtml — главная страница (Razor Page) приложения, в рамках которой будут разворачиваться приложение.
-
Counter.razor хранит код компонента Counter, суть которого в определение счетчика, значение которого увеличивается при
нажатии на кнопку. -
Error.razor хранит код компонента Error, который применяется для вывода сообщения об ошибке.
-
FetchData.razor хранит код компонента FetchData, который с помощью сервиса WeatherForecastService получает некоторые данные и
выводит их на веб-страницу -
Index.razor хранит код компонента Index.
-
-
Папка Shared хранит дополнительные компоненты Razor
-
MainLayout.razor хранит код компонента MainLayout, который определяет структуру или компоновку страницы.
-
NavMenu.razor хранит код компонента NavMenu, который определяет элементы навигации
-
-
_Imports.razor содержит подключения пространств имен с помощью директивы using, которые будут подключаться
в компоненты Razor (файлы с расширением .razor). -
App.razor содержит определение корневого компонента приложения, который позволяет установить маршрутизацию между вложенными компонентами с помощью другого встроенного компонента Router.
-
Файл appsettings.json хранит конфигурацию приложения.
-
Файл Startup.cs представляет класс Startup, стандартный для приложения ASP.NET Core, где настраивается конвейер обработки запроса, внедряются сервисы и осуществляется конфигурация приложения.
-
Файл Program.cs содержит класс Program, который представляет точку входа в приложение. В данном случае это стандартный для приложения
ASP.NET Core класс Program, который запускает и конфигурирует хост, в рамках которого разворачивается приложение с Blazor.
Таким образом, проект Blazor Server уже содержит некоторую базовую типовую функциональность, который позволяе нам запустить проект и оценить работу фреймворка.
Итак, запустим проект. Вначале мы увидим код компонента Index:
С помощью меню в левой части страницы мы можем перейти к другим компонентам. Например, перейдем к компоненту Counter:
Или к компоненту FetchData, который с помощью сервиса WeatherForecastService получит некоторые данные и выведет их на страницу:
Принцип работы проекта Blazor Server
Теперь разберем, как вообще работает стандартный проект Blazor Server с типовым содержанием.
Класс Startup
Прежде всего, чтобы задействовать функциональность Blazor Server, надо в классе Startup
добавить необходимые сервисы и настроить обработку запросов. Например, возьмем типовой класс Startup из проекта по умолчанию:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using HelloBlazorServerApp.Data; namespace HelloBlazorServerApp { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddServerSideBlazor(); services.AddSingleton<WeatherForecastService>(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host"); }); } } }
В методе ConfigureServices добавляются сервисы Blazor Server:
services.AddServerSideBlazor();
В методе Configure при определении конечных точек, с которыми будут связываться входящие запросы, вызывается метод MapBlazorHub(),
который позволяет установить подключение с браузером посредством SignalR. Благодаря этом будет происходить обмен сообщениями между сервером и клиентов в режиме реального времени.
app.UseEndpoints(endpoints => { endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host"); });
Кроме того, вызов метода MapFallbackToPage("/_Host")
позволяет установить страницу Razor Page по умолчанию для приложения
(по умолчанию это страница Pages/_Host.cshtml). Она и представляет собственно тот интерфейс, который увидит пользователь.
Страница _Host.cshtml
Страница Razor _Host.cshtml, которая располагается в папке Pages, является корневой страницей всего приложения. Когда к любой странице (компоненту)
приходит запрос, то именно эта страница возвращается в ответ. А отдельные компоненты располагаются внутри этой страницы.
_Host.cshtml представляет типичную страницу Razor Page, которая содержит код C# и HTML:
@page "/" @namespace HelloBlazorServerApp.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @{ Layout = null; } <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>HelloBlazorServerApp</title> <base href="~/" /> <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" /> <link href="css/site.css" rel="stylesheet" /> </head> <body> <app> <component type="typeof(App)" render-mode="ServerPrerendered" /> </app> <div id="blazor-error-ui"> <environment include="Staging,Production"> An error has occurred. This application may no longer respond until reloaded. </environment> <environment include="Development"> An unhandled exception has occurred. See browser dev tools for details. </environment> <a href="" class="reload">Reload</a> <a class="dismiss">🗙</a> </div> <script src="_framework/blazor.server.js"></script> </body> </html>
В данном случае мы видим, что она определяет каркас веб-страницы, которую увидит пользователь в своем браузере. Но прежде всего следует обратить
внимание на подключение внизу страницы скрипта _framework/blazor.server.js. Это автоматически подключаемый скрипт,
который устанавливает подключение между браузером и сервером посредством SignalR.
Какое содержимое будет вставляться в страницу _Host.cshtml? Это определяет компонент App. На странице он подключается с помощью tag-хелпера
<Component>
:
<app> <component type="typeof(App)" render-mode="ServerPrerendered" /> </app>
Атрибут type
указывает на тип класса компонента. В данном случае это класс App. И атрибут render-mode
со значением
ServerPrerendered указывает, что компонент будет предварительно рендерится в статический html-код, который затем будет загружаться на страницу.
Компонент App
Итак, на странцу _Host.cshtml загружается компонент App. Он располагается в файле App.razor в корне проекта:
<Router AppAssembly="@typeof(Program).Assembly"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> </Found> <NotFound> <LayoutView Layout="@typeof(MainLayout)"> <p>Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router>
Компонент App использует встроенный компонент Router, который добавляет возможность маршрутизации по вложенным компонентам.
Его атрибут AppAssembly указывает на сборку, в которой следует искать запрошенные вложенные компоненты.
При запросе компонентов может быть две ситуации: запрошенный ресурс (компонент) найден и ресурс не найден. Соответственно для каждой из этих ситуаций
определены соответственно два элемента: Found и NotFound
Компонент Found содержит другой компонент — RouteView. Через атрибут RouteData он получает контекст маршрутизации,
который будут использоваться при обработке запроса. А другой атрибут — DefaultLayout устанавливает компонент, который
будет определять компоновку (layout) содержимого — в данном случае это компонент MainLayout.
Комопонент NotFound определяет, как будет рендерится ответ, если компонент для обработки запроса не найден. С помощью вложенного
компонента LayoutView определяется компонент, который будет задавать компоновку. В данном случае это опять же компонент MainLayout.
MainLayout
В обоих случаях в компоненте App, как было описано выше будет использоваться компонент MainLayout,
который определен в файле MainLayout.razor в папке Shared:
@inherits LayoutComponentBase <div class="sidebar"> <NavMenu /> </div> <div class="main"> <div class="top-row px-4"> <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a> </div> <div class="content px-4"> @Body </div> </div>
Здесь мы видим, что этот компонент наследуется от класса LayoutComponentBase, который определяет некоторую базовую функцональность для подобных компонентов.
С помощью элемента <NavMenu />
добавляется компонент NavMenu из файла Shared/NavMenu.razor, который создает систему навигации. Благодаря чему
при загрузке приложения в левой части станицы мы можем переходить внутри приложения по набору ссылок.
Другой отличительной особенностью компонента MainLayout является использование свойства Body, унаследованного от класса LayoutComponentBase:
<div class="content px-4"> @Body </div>
Посредством свойства Body в определенном месте разметки будет рендерится выбранный для обработки запроса компонент. То есть именно за место вызова @Body
будет добавляться контент компонентов Index, Counter и FetchData из папки Pages.
Выбор компонентов
Основные комопненты, которые представляют отдельные ресурсы и к которым пользователь может осуществлять запросы, располагаются в папке Pages — это
компоненты Index, Counter, FetchData.
Возьмем самый простой компонент — Index:
@page "/" <h1>Hello, world!</h1> Welcome to your new app.
Директива @page "/"
указывает, что этот компонент будет сопоставляться с запросами к корню приложения, например, https://localhost:44304/
. То есть по сути
этот компонент можно рассматривать как главную страницу приложения.
Или другой комопнент — Counter:
@page "/counter" <h1>Counter</h1> <p>Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int currentCount = 0; private void IncrementCount() { currentCount++; } }
Данный компонент будет сопоставляться с запросами по пути «/counter», например, https://localhost:44304/counter
.
Компонент counter также определяет некоторую логику на C#. В частности, он определяет переменную currentCount и метод IncrementCount, который увеличивает
значение переменной:
private int currentCount = 0; private void IncrementCount() { currentCount++; }
В коде html мы можем установить привязку к переменным и методам компонента:
<p>Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
Метод привязывается к событию кнопки onclick, благодаря чему при нажатии на кнопку будет срабатывает метод IncrementCount, и пользователь
увидит новое значение currentCount.
При этом следует отметить, что несмотря на то, что пользователь сможет увидеть в своем браузере и значение переменной currentCount и также сможет
нажать на кнопку, которая сгенерирует событие нажатия, но вся логика C# срабатывает на сервере. То есть после нажатия на кнопку с помощью подключенного
скрипта _framework/blazor.server.js
на сервер через соединение SignalR будет отправляться информация о событии нажатии, а сервер в ответ высылает клиенту инструкции
о том, как обновить содержимое веб-страницы.
Третий компонент — FetchData получает с помощью внедренного сервиса WeatherForecastService данные и выводит их в код html:
@page "/fetchdata" @using HelloBlazorServerApp.Data @inject WeatherForecastService ForecastService <h1>Weather forecast</h1> <p>This component demonstrates fetching data from a service.</p> @if (forecasts == null) { <p><em>Loading...</em></p> } else { <table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </thead> <tbody> @foreach (var forecast in forecasts) { <tr> <td>@forecast.Date.ToShortDateString()</td> <td>@forecast.TemperatureC</td> <td>@forecast.TemperatureF</td> <td>@forecast.Summary</td> </tr> } </tbody> </table> } @code { private WeatherForecast[] forecasts; protected override async Task OnInitializedAsync() { forecasts = await ForecastService.GetForecastAsync(DateTime.Now); } }
Я работаю над приложением Blazor Server, в котором используется редактор кода. В качестве редактора кода я использую CodeMirror. Текстовая область на странице Blazor отображается нормально, но привязка данных не работает с соответствующим полем C #.
Скриншот страницы Blazor
Мой файл _Host.cshtml выглядит следующим образом:
@page "/"
@namespace BlazorApp1.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BlazorApp1</title>
<base href="~/" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link href="css/site.css" rel="stylesheet" />
<script src="~/codemirror/codemirror.js"></script>
<link rel="stylesheet" href="~/codemirror/codemirror.css">
<script src="~/codemirror/mode/clike.js"></script>
<script src="~/codemirror/addon/display/fullscreen.js"></script>
<link rel="stylesheet" href="~/codemirror/addon/display/fullscreen.css">
<link rel="stylesheet" href="/codemirror/theme/monokai.css">
<script>
function loadCodeEditor() {
var codemirrorEditor = CodeMirror.fromTextArea(document.getElementById('codearea'), {
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true,
mode: "text/x-csharp",
theme: "monokai",
extraKeys: {
"F11": function (cm) {
cm.setOption("fullScreen", !cm.getOption("fullScreen"));
},
"Esc": function (cm) {
if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
}
}
});
codemirrorEditor.setSize(900, 300);
}
</script>
</head>
<body>
<app>
<component type="typeof(App)" render-mode="ServerPrerendered" />
</app>
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.server.js"></script>
</body>
</html>
А код страницы бритвы следующий:
@page "/"
@inject IJSRuntime JSRuntime;
<h1>Hello, world!</h1>
Welcome to your new app.
<textarea id="codearea" rows="20" @bind="UserCodeInput" @bind:event="oninput" style="height:100px;"></textarea>
<SurveyPrompt Title="How is Blazor working for you?" />
Input Code: @UserCodeInput
@code{
public string UserCodeInput { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("loadCodeEditor");
}
}
}
Если я удалю id = «codearea» из атрибута textarea и закомментирую вызов метода JSInterop, привязка будет работать нормально.
Подскажите, пожалуйста, как исправить привязку с помощью CodeMirror.
1 ответ
Лучший ответ
Наконец, я могу связать поле с редактором CodeMirror. Благодаря нижеприведенным темам:
Когда я редактирую код в TextArea с помощью CodeMirror, как отразить это в другом текстовом поле с помощью js или jQuery
Как обновить значение c # в Blazor с помощью javascript?
Мне пришлось передать ссылку на объект DotNet при вызове функции JavaScript, а затем в функции JavaScript использовать событие onchange CodeMirror для вызова метода C # и передачи значения редактора кода.
Ниже представлены обновленные файлы с рабочим кодом:
_Host.cshtml
@page "/"
@namespace BlazorApp1.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BlazorApp1</title>
<base href="~/" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link href="css/site.css" rel="stylesheet" />
<script src="~/codemirror/codemirror.js"></script>
<link rel="stylesheet" href="~/codemirror/codemirror.css">
<script src="~/codemirror/mode/clike.js"></script>
<script src="~/codemirror/addon/display/fullscreen.js"></script>
<link rel="stylesheet" href="~/codemirror/addon/display/fullscreen.css">
<link rel="stylesheet" href="/codemirror/theme/cobalt.css">
<link rel="stylesheet" href="/codemirror/theme/monokai.css">
</head>
<body>
<app>
<component type="typeof(App)" render-mode="ServerPrerendered" />
</app>
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.server.js"></script>
<script>
function loadCodeEditor(dontNetObjRef) {
var codemirrorEditor = CodeMirror.fromTextArea(document.getElementById('codearea'), {
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true,
mode: "text/x-csharp",
theme: "monokai",
extraKeys: {
"F11": function (cm) {
cm.setOption("fullScreen", !cm.getOption("fullScreen"));
},
"Esc": function (cm) {
if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
}
}
});
codemirrorEditor.setSize(900, 500);
codemirrorEditor.on("change", editor => {
dontNetObjRef.invokeMethodAsync("UpdateField", editor.getValue());
console.log(editor.getValue());
});
}
</script>
</body>
</html>
Страница бритвы:
@page "/"
@inject IJSRuntime JSRuntime;
<h1>Hello, world!</h1>
Welcome to your new app.
<textarea id="codearea"></textarea>
<SurveyPrompt Title="How is Blazor working for you?" />
<br />
<br />
Input Code: @UserCodeInput
@code{
public string UserCodeInput { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("loadCodeEditor", DotNetObjectReference.Create(this));
}
}
[JSInvokable("UpdateField")]
public Task UpdateField(string codeValue)
{
UserCodeInput = codeValue;
StateHasChanged();
return Task.CompletedTask;
}
}
1
raghavranjan
29 Окт 2020 в 08:13
Подборка полезных Телеграм ботов от разработчика AnyBoost
Akinator: игра
Загадай любого реального или вымышленного персонажа, а бот его отгадает за несколько вопросов
BigVoicy: текст в речь
Отправляй боту текст, а он озвучит его голосом. Большое количество голосов к вашим услугам!
XOchatBot: крестики-нолики
Крестики-нолики для Telegram: играйте с друзьями, в группах или со случайными соперниками
FairyTell: анонимный чат
Общайтесь анонимно со случайными собеседниками, получайте валентинки анонимно от своих друзей
AnyMelody: музыка
Самый полноценный бот с музыкой создаст в вашем Telegram полноценный музыкальный плеер
Fontium: шрифты
Укрась свой текст красивыми шрифтами не выходя из любимого мессенджера. Ⲡⲟⲡⲣⲟⳝⲩύ ⲥⲉύⳡⲁⲥ!
VkBoosting: накрутка
Накрутка VK: лайки, подписчики, друзья, комментарии, репосты. Стань популярным бесплатно и без регистрации
Рекомендуем также:
BomjGame: симулятор
Симулятор бомжа: соревнуйся с друзьями и прокачивай своего персонажа, чтобы завоевать мир!
Thinkia: викторина
Полноценная викторина для групповых чатов и одиночной игры. Сыграй с друзьями и установи свой рекорд
Flaginator: игра
Сыграй в увлекательную игру: попробуй отгадать флаги стран мира, проверь свои знания и установи рекорд
Бот Максим: озвучка текста
Тот самый бот Максим — лучший сервис для профессиональной озвучки ваших текстов в Телеграм
Бот Алеша: голос из донатов
Алёша озвучит текст голосом робота из донатов
Summary
Running a Blazor Server application and refreshing the page sometimes creates the following error for the user:
An error has occurred. This application may no longer respond until reloaded. Reload
Turning on browser debugging may show connection errors similar to the following:
blazor.server.js:1 WebSocket connection to 'ws://helloblazor.test/_blazor?id=gsHIh62GVm39WvDVxjpJMg' failed: Error during WebSocket handshake: Unexpected response code: 404
---
Error: Failed to start the transport 'WebSockets': Error: There was an error with the transport.
---
GET http://helloblazor.test/_blazor?id=oobUaSlKLPyrC5RPZVg3uw&_=1597287612026 404 (Not Found)
---
Error: Failed to start the transport 'LongPolling': Error: Not Found
---
Error: Failed to start the connection: Error: Unable to connect to the server with any of the available transports. WebSockets failed: Error: There was an error with the transport. ServerSentEvents failed: Error: 'ServerSentEvents' does not support Binary. LongPolling failed: Error: Not Found
---
Error: Error: Unable to connect to the server with any of the available transports. WebSockets failed: Error: There was an error with the transport. ServerSentEvents failed: Error: 'ServerSentEvents' does not support Binary. LongPolling failed: Error: Not Found
---
Uncaught (in promise) Error: Cannot send data if the connection is not in the 'Connected' State.
at e.send (blazor.server.js:1)
at e.sendMessage (blazor.server.js:1)
at e.sendWithProtocol (blazor.server.js:1)
at blazor.server.js:1
at new Promise (<anonymous>)
at e.invoke (blazor.server.js:1)
at e.<anonymous> (blazor.server.js:15)
at blazor.server.js:15
at Object.next (blazor.server.js:15)
at blazor.server.js:15
These errors show that two (2) types of connection transports were attempted, (1) WebSocket and (2) LongPolling (ServerSentEvents), and both failed to connect.
This connection error can occur when the host is running in a load-balanced server environment. In that type of environment, change the load balancer algorithm for the user to have server affinity (also referred to as sticky sessions). Enabling server affinity ensures the user connection is re-established to the same server on refreshes.
Description
Load balancing two (2) or more servers helps ensure application availability in case one of the servers experience an outage. A server outage could be caused by component failure (hardware/software) or by being overloaded relative to its physical capacity (CPU, Memory, Disk, Network). Adding a second server in a load balanced configuration allow users to be connected to any of the servers that are available to accept the request.
For many web server type applications, the content the server provides to the user does not require the user to stay connected to the server for long durations. They work by a request-response pattern where the user’s browser requests content, such as ‘homepage.htm’, and the server responds by sending the content back. Once the request-response round-trip has completed the user has a local copy from the server and disconnects. Next, if the user refreshes the browser for the same homepage.htm, a 2nd request-response round trip is performed. As the user was disconnected from the server after the 1st request-response completed, a load balancer may forward the 2nd request to a different server to respond.
When a load balancer operates in this mode where a request can receive a response from any server, it is referred to as having ‘no affinity’. When no affinity is used, the load balancer can have different ways (aka algorithms) for how it chooses to distribute the requests between the available servers. A common algorithm is round-robin where the load balancer gives each server a turn when a request is received. When all available servers have taken a turn, it starts over again from the first server in the round-robin list.
As a load balancer can support ‘no affinity’, it can also support ‘affinity’. With affinity enabled the load balancer will stick all requests from a specific user to the same server. This is also referred to as ‘sticky sessions’. The session part stems from the stickiness often being temporal in nature either due to an expiration by the load balancer or the server becoming unavailable. If the server becomes unavailable, the load balancer will establish affinity to a new server for the user.
With the background of load balancing and affinity rules behind us, lets come back to the beginning of this article where the user is experiencing an error in the Blazor Server application. What we reviewed on the web server request-response pattern above is referred to as a one-way communication. The user calls the web server and responds, but the web server does not call the user. This one-way communication is foundational to the HTTP protocol used on the Internet.
A Blazor Server application works over a WebSocket protocol. This protocol allows two-way communication where both the user and web server can initiate requests to each other. For this two-way communication to work, the user is the initiating party that starts a request to the server over the HTTP protocol and then negotiates a protocol transition to WebSocket if supported. Once the WebSocket connection is established, both the user and the web server can initiate a call to each other, thus enabling two-way communication.
For the web server to call the user, it needs to know which of its connections is connected to what user to ensure it is sending the request to the right recipient. This logic is provided by the Blazor Server framework and is transparent to the application code but conceptually looks something similar to the following:
As we experienced in the early days of cellular phones (thankfully less these days), dropped calls can happen. With cellular phones we call the party back and continue the conversation where we left off because with both have ‘memory’ of us talking to each other. Like cellular phone communication, a WebSocket connection may experience a ‘drop’ and will need to be re-established. In order to keep the communication going from the point where it dropped, both the user and web server needs ‘memory’ of each other like people with cellular phones. This memory is held on both the user and web server side so when communication is re-established both parties remember each other and continue from where they dropped. All the user and web server memory and communication reconnects are done for us by the Blazor Server framework.
This memory and reconnect works fine when the load balancer only sends us to one web server as above. But what happens to our memory on a reconnect if we are in a multi-web server environment that is load balanced with no affinity?
As depicted above, if the user was having a two-way communication with Server 1 that drops, then the ‘memory’ of the communication is between the user and Server 1. When the connection reconnects to Server 2, Server 2 will have no memory of the prior communication. It’s like calling your friend back on the cellular phone and continue the conversation only to realize you called the wrong number. The Blazor Server framework accounted for this situation so instead of having Server 2 play along in a conversation it ha no memory of, it responds with a ‘wrong number’ (404 Not Found) to inform the user that it will not establish the connection. This ultimately leads to a connection error on the user side which can be seen in the browser debugger with the following connection errors:
blazor.server.js:1 WebSocket connection to 'ws://helloblazor.test/_blazor?id=gsHIh62GVm39WvDVxjpJMg' failed: Error during WebSocket handshake: Unexpected response code: 404
---
Error: Failed to start the transport 'WebSockets': Error: There was an error with the transport.
---
GET http://helloblazor.test/_blazor?id=oobUaSlKLPyrC5RPZVg3uw&_=1597287612026 404 (Not Found)
That is very polite gesture instead of running a prank and attempt to play along in a conversation it knows nothing about 🙂
The Blazor Server framework comes with an additional backup transports if WebSockets fail, including ServerSentEvents (SSE) and long-polling. However, as the reconnect is happening to the wrong server, all three (3) connections fail as seen in the subsequent errors:
Error: Failed to start the transport 'LongPolling': Error: Not Found
---
Error: Failed to start the connection: Error: Unable to connect to the server with any of the available transports. WebSockets failed: Error: There was an error with the transport. ServerSentEvents failed: Error: 'ServerSentEvents' does not support Binary. LongPolling failed: Error: Not Found
---
Error: Error: Unable to connect to the server with any of the available transports. WebSockets failed: Error: There was an error with the transport. ServerSentEvents failed: Error: 'ServerSentEvents' does not support Binary. LongPolling failed: Error: Not Found
As indicated by the errors, ServerSentEvents never actually tried to connect because the transport errored out due to the binary message format not being supported over that transport. Both WebSocket and long-polling were attempted to reconnect.
This is analogous to first calling the friend back with the wrong number on the cellular phone (WebSocket) and then retrying the wrong number on a landline (long-polling). Both phones are connecting to the wrong number. The end result is a connection failure as both attempts failed:
Uncaught (in promise) Error: Cannot send data if the connection is not in the 'Connected' State.
at e.send (blazor.server.js:1)
at e.sendMessage (blazor.server.js:1)
at e.sendWithProtocol (blazor.server.js:1)
at blazor.server.js:1
at new Promise (<anonymous>)
at e.invoke (blazor.server.js:1)
at e.<anonymous> (blazor.server.js:15)
at blazor.server.js:15
at Object.next (blazor.server.js:15)
at blazor.server.js:15
With the understanding of load balancing affinity behavior we can then change the load balancer from ‘no affinity’ to ‘affinity’ to ensure that the load balancer always sends the user back to the same server upon reconnects.
With the load balancer affinity enabled we should now be able to reconnect successful and verify the connection handshake succeeded in the browser debugger:
Information: WebSocket connected to ws://helloblazor.test/_blazor?id=NfBNcfX6EMrOjIxWpDUwsg.
This is not the only cause for an error has occurred, but it may be one to explore as well as reviewing the browser debugger information for additional information to help troubleshoot. As multiple replicas are common for server availability and easy to do with Docker containerization it can be an early cause to write off the troubleshooting list.
See Docker Blazor App on Linux ARM for additional information on containerizing Blazor Server applications.
Как получать анонимные сообщения, вопросы, валентинки от друзей и знакомых в Telegram?
Инструкция:
1: запустите бота с анонимным чатом: FairyTellBot
2: нажмите «Моя страничка». Если странички ещё нет, бот предложит создать её — просто следуйте указаниям
3: бот отправит вам ссылку на вашу страничку в системе FairyTell, поделитесь этой ссылкой с друзьями, например разместите её у себя ВКонтакте или где-нибудь ещё,
где ваши друзья смогут её увидеть
4: когда вам напишут, FairyTell оповестит вас об этом через Telegram и направит ссылку чтобы вы могли ответить тому, кто отправил вам анонимку.
Частые вопросы:
Если я создам страничку со своим настоящим именем, собеседники в боте будут его видеть?
— Нет, эта страничка и ваше имя на ней будут видны только тем, кто перейдёт по вашей ссылке чтобы написать вам анонимку.
Если я отвечу на анонимку, как аноним узнает об этом?
— FairyTell оповестит об этом того, кому вы ответили и продублирует ему ссылку на мини-чат, вы буквально сможете переписываться с анонимом в режиме реального времени на специальной страничке.