From 3f45eadee9d365a62087ff0d1b7f6adda7301a0d Mon Sep 17 00:00:00 2001 From: Engincan VESKE Date: Thu, 8 Sep 2022 09:02:26 +0300 Subject: [PATCH] Document Layout Hooks for Blazor UI --- docs/en/UI/Blazor/Layout-Hooks.md | 125 ++++++++++++++++++ docs/en/docs-nav.json | 4 + docs/en/images/bookstore-banner-component.png | Bin 0 -> 11827 bytes 3 files changed, 129 insertions(+) create mode 100644 docs/en/UI/Blazor/Layout-Hooks.md create mode 100644 docs/en/images/bookstore-banner-component.png diff --git a/docs/en/UI/Blazor/Layout-Hooks.md b/docs/en/UI/Blazor/Layout-Hooks.md new file mode 100644 index 0000000000..1bddc1c677 --- /dev/null +++ b/docs/en/UI/Blazor/Layout-Hooks.md @@ -0,0 +1,125 @@ +# Blazor UI: Layout Hooks + +ABP Framework theming system places the page layout into the [theme](Theming.md) NuGet packages. That means the final application doesn't include a layout, so you can't directly change the layout code to customize it. + +> If you create a Blazor WASM project, the `index.html` file will be included within the template. You can also customize it to your needs. + +You can copy the theme code into your solution. In this case, you are completely free to customize it. However, then you won't be able to get automatic updates of the theme (by upgrading the theme NuGet package). + +The **Layout Hook System** allows you to **add code** at some specific parts of the layout. All layouts of all themes should implement these hooks. Finally, you can add a **razor component** into a hook point. + +## Example: Add Simple Announcement Alert + +Assume that you need to add a simple banner to the layout (that will be available for all the pages) to make an announcement about your new product. First, **create a razor component** in your project: + +![bookstore-banner-component](../../images/bookstore-banner-component.png) + +**AnnouncementComponent.razor.cs** + +```csharp +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; + +namespace Acme.BookStore.Blazor.Components; + +public partial class AnnouncementComponent : ComponentBase +{ + private const string AnnouncementLocalStorageKey = "product-announcement-status"; + + [Inject] + public IJSRuntime JsRuntime { get; set; } + + public bool ShowBanner { get; set; } + + protected override async Task OnInitializedAsync() + { + ShowBanner = await ShowAnnouncementBannerAsync(); + } + + private async Task ShowAnnouncementBannerAsync() + { + var announcementLocalStorageValue = await JsRuntime.InvokeAsync("localStorage.getItem", AnnouncementLocalStorageKey); + + return announcementLocalStorageValue != null && + bool.TryParse(announcementLocalStorageValue, out var showAnnouncementBanner) && showAnnouncementBanner; + } + + private async Task HideAnnouncementBannerAsync() + { + await JsRuntime.InvokeVoidAsync("localStorage.setItem", AnnouncementLocalStorageKey, true); + ShowBanner = false; + StateHasChanged(); + } +} +``` + +**AnnouncementComponent.razor** + +```html +@if(ShowBanner) +{ +
+ A brand new product is in sale. Click here to learn more. + +
+} +``` + +Then, you can add this component to any of the hook points in the `ConfigureServices` of your module class: + +```csharp +Configure(options => +{ + options.Add( + LayoutHooks.Body.Last, //The hook name + typeof(AnnouncementComponent) //The component to add + ); +}); +``` + +Now, the `AnnouncementComponent` will be rendered in the `body` of the page as the last item. + +### Specifying the Layout + +The configuration above adds the `AnnouncementComponent` to all layouts. You may want to only add to a specific layout: + +````csharp +Configure(options => +{ + options.Add( + LayoutHooks.Body.Last, + typeof(AnnouncementComponent), + layout: StandardLayouts.Application //Set the layout to add + ); +}); +```` + +See the *Layouts* section below to learn more about the layout system. + +## Layout Hook Points + +There are some pre-defined layout hook points. The standard hook points are; + +* `LayoutHooks.Body.First`: Used to add a component as the first item in the HTML body tag. +* `LayoutHooks.Body.Last`: Used to add a component as the last item in the HTML body tag. + +> You (or the modules you are using) can add **multiple items to the same hook point**. All of them will be added to the layout in the order they were added. + +## Layouts + +The layout system allows themes to define the standard, named layouts and allows any page to select a proper layout for its purpose. There is one pre-defined layout: + +* "**Application**": The main (and the default) layout for an application. It typically contains header, menu (sidebar), footer, toolbar... etc. + +This layout is defined in the `StandardLayouts` class as constants. You can definitely create your own layouts, but this layout name is the standard layout name and implemented by all the themes out of the box. + +> If you don't specify the layout, your razor component will be rendered in all of the layouts. + +### Layout Location + +You can find the `MainLayout.razor` in [here](https://github.com/abpframework/abp/blob/dev/modules/basic-theme/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/MainLayout.razor) for the basic theme. You can take it as a reference to build your own layouts or you can override it, if necessary. + +## See Also + +* [Customization / Overriding Components](Customization-Overriding-Components.md) \ No newline at end of file diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 14c584d376..035f150705 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -939,6 +939,10 @@ { "text": "PWA Configuration", "path": "UI/Blazor/Pwa-Configuration.md" + }, + { + "text": "Layout Hooks", + "path": "UI/Blazor/Layout-Hooks.md" } ] }, diff --git a/docs/en/images/bookstore-banner-component.png b/docs/en/images/bookstore-banner-component.png new file mode 100644 index 0000000000000000000000000000000000000000..2363b06fae4e8c5c3f33d080d2bccdaa17650c87 GIT binary patch literal 11827 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 Y#E8RkZ34C73%y^+Nh?cLOPB=xFa8~GO8@`> literal 0 HcmV?d00001