Appearance
Are you an LLM? You can read better optimized documentation at /views/shared_views.md for this page in Markdown format
Shared views
Overview
This document provides a technical reference for the shared partial views located in the TheExampleApp/Views/Shared/ directory. These views are fundamental to the application's architecture, promoting code reuse and ensuring a consistent user interface across different pages. They encapsulate discrete pieces of UI, from complex layout modules to simple content blocks.
Understanding these shared views is crucial for developers maintaining existing pages or creating new ones. They are the primary building blocks for rendering content fetched from the Contentful CMS.
Layout module partials
Layout modules are large, self-contained sections of a page, such as hero banners or multi-column text blocks. They are dynamically rendered based on the content structure of a Layout entry in Contentful. For more information on the data models, see the Layout Modules documentation.
LayoutCopy.cshtml
This partial view renders a flexible content block that can contain a headline, a body of text, and a call-to-action (CTA) link. It supports two visual styles.
Model: LayoutCopy
Key Features:
- Dynamic Styling: The view checks the
VisualStyleproperty of the model. If the value is"Emphasized", it appends a--emphasizedmodifier to the CSS classes, allowing for distinct visual treatments. - Markdown Rendering: The main content (
Model.Copy) is processed by the<markdown>tag helper, which converts Markdown syntax into HTML. This relies on the Markdig library. - Conditional CTA: The call-to-action button is only rendered if both
CtaTitleandCtaLinkproperties are present in the model.
csharp
// File: TheExampleApp/Views/Shared/LayoutCopy.cshtml
@model LayoutCopy
@{ var visualStyle = Model.VisualStyle == "Emphasized" ? "--emphasized" : ""; }
<div class="module @("module-copy"+visualStyle)">
<div class="@("module-copy__wrapper"+visualStyle)">
<div class="@("module-copy__first" + visualStyle)">
@if (!string.IsNullOrEmpty(Model.Headline))
{
<h1 class="@("module-copy__headline" + visualStyle)">@Model.Headline </h1>
}
<div class="@("module-copy__copy" + visualStyle)"><markdown content="@Model.Copy" /></div>
</div>
<div class="@("module-copy__second" + visualStyle)">
@if (!string.IsNullOrEmpty(Model.CtaLink) && !string.IsNullOrEmpty(Model.CtaTitle))
{
<a class="cta @("module-copy__cta" + visualStyle)" href="@Model.CtaLink">@Model.CtaTitle</a>
}
</div>
</div>
</div>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
LayoutHeroImage.cshtml
This partial renders a full-width hero image with a headline overlay.
Model: LayoutHeroImage
Key Features:
- Error Handling: The view includes a robust null check. If the model or its
BackgroundImageproperty is null, it renders a localized error message instead of throwing an exception. This ensures page stability even with incomplete Contentful entries. - Custom Image Helper: The image is rendered using the
<contentful-image>tag helper. This is a custom application component responsible for handling image URLs from the Contentful CDN, potentially adding optimizations like resizing or format selection.
csharp
// File: TheExampleApp/Views/Shared/LayoutHeroImage.cshtml
@model LayoutHeroImage
@if (Model == null || Model.BackgroundImage == null)
{
// Gracefully handles missing content from the CMS.
<div class="module module-hero-image">
<h3>@Localizer["imageErrorTitle"]</h3>
</div>
}
else
{
<div class="module module-hero-image">
<div class="module-hero-image__wrapper">
<div class="module-hero-image__headline-wrapper">
<h2 class="module-hero-image__headline">@Model.Headline</h2>
</div>
<contentful-image class="module-hero-image__image" url="@Model.BackgroundImage.File.Url" />
</div>
</div>
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
LayoutHighlightedCourse
This partial is designed to feature a specific course within a generic layout. It is not detailed in the provided source but follows the same pattern of binding to a specific model from the Layout Modules documentation to render its content.
Lesson module partials
Lesson modules are smaller content blocks used specifically within the context of a lesson page. They break down lesson content into logical pieces like text, images, and code. For more on the data models, see the Lesson Modules documentation.
LessonCopy.cshtml
This is the most common lesson module, used for rendering paragraphs of text content.
Model: LessonCopy
Key Features:
- Simplicity: This view has a single responsibility: rendering a block of Markdown content.
- Markdown Rendering: Like other content views, it uses the
<markdown>tag helper to parse theModel.Copyproperty.
csharp
// File: TheExampleApp/Views/Shared/LessonCopy.cshtml
@model LessonCopy
<div class="lesson-module lesson-module-copy">
<div class="lesson-module-copy__copy">
<markdown content="@Model.Copy"></markdown>
</div>
</div>1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
LessonImage & LessonCodeSnippets
While source files are not provided, these partials follow the same convention.
- LessonImage: Renders an image within a lesson. It likely uses the
<contentful-image>tag helper and binds to aLessonImagemodel. - LessonCodeSnippets: Renders formatted code blocks. The
Copyproperty of its model would contain Markdown with code fences, which the Markdig processor would correctly parse into<pre><code>...</code></pre>blocks.
CourseCard.cshtml
This is a highly reusable component that displays a summary of a course. It is used on the course listing page and category pages.
Model: Course
Key Features:
- Component Composition: It uses the
<vc:entry-state>View Component to display the publication status (e.g., "New", "Updated") of the Contentful entry. This demonstrates how shared views can be composed of even smaller, more specialized components. See the View Components documentation for details. - Dynamic Links: It generates links to the course detail page (
/courses/@Model.Slug) and to the respective category pages (/courses/categories/@category.Slug). - Localization: The "View Course" link text is retrieved from localization resources via
@Localizer["viewCourseLabel"].
csharp
// File: TheExampleApp/Views/Shared/CourseCard.cshtml
@model Course
<div class="course-card">
<div class="course-card__categories">
@if (Model.Categories != null && Model.Categories.Any())
{
foreach (var category in Model.Categories)
{
<div class="course-card__category"><a class="course-card__category-link" href="/courses/categories/@category.Slug">@category.Title</a></div>
}
}
</div>
<h2 class="course-card__title"><a href="/courses/@Model.Slug">@Model.Title</a> <vc:entry-state sys="new[] { Model.Sys }"></vc:entry-state></h2>
<p class="course-card__description">
@Model.ShortDescription
</p>
<div class="course-card__link-wrapper">
<a class="course-card__link" href="/courses/@Model.Slug">@Localizer["viewCourseLabel"]</a>
</div>
</div>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
TableOfContents.cshtml
This partial view renders the navigation sidebar for a course page, listing the course overview and all associated lessons.
Model: Course
Key Features:
- Stateful UI Logic: The view contains significant logic for managing the UI state of the navigation links.
- Active State: The active link is determined by comparing the current lesson's slug (passed via
ViewData["slug"]) with the slug of the link being rendered. - Visited State: A
visitedCSS class is applied based on a list of visited lesson IDs stored inViewData["VisitedLessons"]. An inlineFuncdelegate (isVisited) is used to encapsulate this check for cleaner template code.
- Active State: The active link is determined by comparing the current lesson's slug (passed via
- Dependency on
ViewData: This component is tightly coupled to the controller that renders it. The controller is responsible for populatingViewData["slug"]andViewData["VisitedLessons"]for the state logic to function correctly.
csharp
// File: TheExampleApp/Views/Shared/TableOfContents.cshtml
@model Course
@{
// This delegate checks if a lesson ID exists in the ViewData collection.
Func<string, string> isVisited = (s) => { return (ViewData["VisitedLessons"] as List<string>).Contains(s) ? "visited" : ""; };
}
@if (Model != null)
{
<section class="layout-sidebar__sidebar">
<div class="layout-sidebar__sidebar-header">
<h2 class="layout-sidebar__sidebar-title">@Localizer["tableOfContentsLabel"]</h2>
</div>
<div class="layout-sidebar__sidebar-content">
<div class="table-of-contents">
<ul class="table-of-contents__list">
<li class="table-of-contents__item">
<a class="table-of-contents__link @(ViewData["slug"] == null ? "active" : "") @isVisited(Model.Sys.Id)" href="/courses/@Model.Slug">@Localizer["courseOverviewLabel"]</a>
</li>
@if (Model.Lessons != null && Model.Lessons.Any())
{
@foreach (var lesson in Model.Lessons)
{
<li class="table-of-contents__item">
<a class="table-of-contents__link @(ViewData["slug"]?.ToString() == lesson.Slug ? "active" : "") @isVisited(lesson.Sys.Id)" href="/courses/@Model.Slug/lessons/@lesson.Slug">@lesson.Title</a>
</li>
}
}
</ul>
</div>
</div>
</section>
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
NoContent.cshtml
This view serves as a standard "empty state" template. It is intended to be rendered by a controller action when the expected content cannot be retrieved from Contentful. For example, if a user navigates to a course slug that does not exist, the CoursesController would render this view instead of the main Course view.
This provides a more user-friendly experience than a 404 error or a server exception, informing the user that the content is missing in a way that is consistent with the site's design.
Component default views
In ASP.NET Core, View Components can have their Razor views bundled with them. However, a common convention is to place their default views in a shared location. This project follows that convention.
When a View Component is invoked via the tag helper (e.g., <vc:entry-state>), the framework searches for its view at Views/Shared/Components/{ComponentName}/Default.cshtml.
For example, the view for the EntryState View Component is located at: Views/Shared/Components/EntryState/Default.cshtml
This makes View Component views discoverable and manageable alongside other shared partials. For more information, refer to the View Components documentation.
Partial view conventions
To maintain a clean and predictable structure, the project adheres to the following conventions for partial views.
- Location: All shared partial views and View Component views are located in
Views/Shared/. This allows them to be rendered from any other view in the application using@await Html.PartialAsync("ViewName", model)without needing to specify the full path. - Naming: Partial views are named using PascalCase and have the
.cshtmlextension (e.g.,CourseCard.cshtml). The name should clearly describe the component's purpose. - Strongly Typed Models: Every partial view that renders dynamic data should declare a strongly-typed model using the
@modeldirective. This improves type safety and enables compile-time checking. - View Component Views: Default views for View Components are organized in subdirectories within
Views/Shared/Components/, following the pattern{ComponentName}/Default.cshtml.