Appearance
Are you an LLM? You can read better optimized documentation at /core_features/views.md for this page in Markdown format
Views and partial views
This document provides a detailed technical overview of the Razor view and partial view implementation in TheExampleApp. It covers the overall structure, layout templates, content rendering components, and utility views. The application employs a server-side rendering (SSR) architecture using ASP.NET Core 2.1 and the Razor templating engine.
View structure
The application's view layer is organized following standard ASP.NET Core conventions. All Razor templates (.cshtml files) are located within the Views directory.
- Controller-Specific Views: Views tied to specific controllers are organized in subdirectories named after the controller (e.g.,
Views/Courses/). - Shared Views: Reusable views, partials, and components are located in
Views/Shared/. This is where the majority of the application's modular UI components reside.
Views are strongly typed, binding to model classes that represent data fetched from the Contentful CMS. This approach ensures type safety and provides IntelliSense support during development. For more information on the models used, see the Models documentation.
The rendering pipeline is driven entirely on the server. Content is fetched from Contentful, mapped to models, and then passed to the appropriate views for HTML generation before being sent to the client.
Layout and shared views
The core application shell and global UI elements are defined in a main layout file and several shared View Components.
_Layout.cshtml
The primary layout template for the entire application is TheExampleApp/Views/Shared/_Layout.cshtml. It defines the main HTML document structure, including the <head>, header, footer, and the main content placeholder.
Key Implementation Details:
- Main Content Area: The
@RenderBody()directive marks the location where controller-specific views are injected. - View Components: The layout makes extensive use of View Components for rendering self-contained UI elements like metadata tags, API selection controls, and locale switching. This promotes modularity and separation of concerns. See the View Components documentation for more details.csharp-razor
// The <vc:meta-tags> View Component dynamically renders meta tags. <vc:meta-tags title="@ViewData["Title"]"></vc:meta-tags> // Other View Components for header controls. <vc:selected-api current-path="@rawTarget"></vc:selected-api> <vc:locales></vc:locales>1
2
3
4
5
6 - Internationalization (i18n): Text content is localized using the
IViewLocalizerservice, accessed via the@Localizerproperty. This allows for easy translation of static UI strings.csharp-razor<span>@Localizer["whatIsThisApp"]</span> <a href="/courses" class="@isActive(null)">@Localizer["coursesLabel"]</a>1
2 - Dynamic Navigation State: A local C# function,
isActive, is defined at the top of the file to dynamically apply anactiveCSS class to the current navigation link based on the value ofViewData["ActivePage"].csharp// C# function defined within the Razor view @{ Func<string, string> isActive = (s) => { return ViewData["ActivePage"]?.ToString() == s ? "active" : ""; }; } // Usage in navigation <li><a href="/" class="@isActive("Home")">@Localizer["homeLabel"]</a></li>1
2
3
4
5
6
7 - Custom Credentials Support: A
getCustomParametersfunction generates query string parameters when the application is configured with custom Contentful API credentials. This allows users to carry their custom space configuration across different platform implementations of the example app.csharpFunc<string> getCustomParameters = () => { if (!Manager.IsUsingCustomCredentials) { return ""; } return $"?space_id={Manager.Options.SpaceId}&preview_token={Manager.Options.PreviewApiKey}&delivery_token={Manager.Options.DeliveryApiKey}&api={(Manager.Options.UsePreviewApi ? "cpa" : "cda")}{(Context.Session.GetString("EditorialFeatures") == "Enabled" ? "&editorial_features=enabled" : "")}"; };1
2
3
4
5
6
7 - About This App Modal: The layout includes a modal dialog (identified by
#about-this-modal) that explains the purpose of the application and provides links to equivalent implementations in other programming languages (Node.js, Ruby, PHP, Python, Java, Swift, Android/Kotlin). The modal is triggered by clicking the "What is this app?" link in the header. - Scripts Section: The layout includes
@RenderSection("Scripts", required: false), allowing individual views to inject page-specific JavaScript into the layout. Therequired: falseparameter makes this section optional. - Environment-Specific Rendering: The
Analyticspartial view is conditionally included only when the application is running in an environment namedHeroku. This is a clean way to separate production-only logic, such as analytics tracking, from development builds.csharp-razor<environment include="Heroku"> @Html.Partial("Analytics") </environment>1
2
3
Content rendering views
The application's content-driven nature relies on a set of specialized partial views designed to render specific content modules fetched from Contentful. This architecture allows content editors to compose pages with different layouts and content types dynamically. For a higher-level overview of this pattern, refer to the Content Display documentation.
These partials are typically located in Views/Shared/ and are rendered by a controller, which selects the appropriate partial based on the Contentful content type ID.
Layout Modules
Layout modules are coarse-grained components used to build the primary structure of landing pages.
Example: LayoutCopy.cshtml
This partial view renders a block of text with an optional headline and a call-to-action (CTA) button.
csharp-razor
@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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- Model Binding: The view is strongly typed to the
LayoutCopymodel. - Conditional Styling: It dynamically adds CSS modifier classes (e.g.,
--emphasized) based on theVisualStyleproperty of the model, allowing for presentational variations controlled from the CMS. - Markdown Rendering: It uses a custom
<markdown>Tag Helper to parse and render Markdown content from theModel.Copyproperty into HTML. This is a key feature for rich text rendering. For more information, see the Tag Helpers documentation.
Lesson Modules
Lesson modules are finer-grained components used to construct the content of a course lesson.
Example: LessonCopy.cshtml
This view renders a simple block of Markdown text within a lesson.
csharp-razor
@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
2
3
4
5
6
7
This modular approach enables content editors to assemble lessons from various components (e.g., LessonCopy, LessonImage, LessonCodeSnippets) in any order.
CourseCard Component
The CourseCard.cshtml partial view is a reusable component for displaying a summary of a course on a listing page.
csharp-razor
@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
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- Complex Model: It binds to the
Coursemodel, which contains nested data like a list ofCategories. - Category Links: The component loops through categories and generates links to category-specific course listings using each category's
Slugproperty. - Editorial State: It utilizes the
<vc:entry-state>View Component to display the publishing status of the Contentful entry, providing visual feedback to editors using the preview environment. - Localized UI Text: The "View Course" link text uses the
@Localizer["viewCourseLabel"]service for internationalization, consistent with the application's i18n approach.
Utility views
This category includes partial views that provide cross-cutting functionality or handle specific UI states.
Breadcrumbs
The Breadcrumbs.cshtml partial renders a breadcrumb navigation trail.
csharp-razor
@{
var crumbs = Context.Items["breadcrumbs"] as List<TheExampleApp.Configuration.Breadcrumb>;
}
@if (crumbs != null)
{
<nav class="breadcrumb">
<ul>
@foreach (var crumb in crumbs)
{
<li><a href="@crumb.Path">@crumb.Label</a></li>
}
</ul>
</nav>
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Implementation Gotcha: This view's logic is decoupled from data generation. It retrieves the breadcrumb data from Context.Items["breadcrumbs"]. This implies that a controller action or a custom middleware is responsible for populating this HttpContext item before the view is rendered. Developers modifying navigation or adding new pages must ensure the breadcrumb data is correctly populated upstream.
NoContent Fallback
The application includes a NoContent.cshtml partial view (not shown) that is rendered when a requested piece of content cannot be found in Contentful. This provides a more graceful user experience than a generic 404 error page, often including context-specific messaging.
Analytics
The Analytics.cshtml partial contains the analytics tracking script (e.g., Google Analytics). As noted in the _Layout.cshtml section, it is rendered conditionally using the environment tag helper, ensuring it is only active in the designated production environment. This prevents test and development traffic from polluting analytics data.