Previously, we learned how to get started with the Sitecore XM Cloud ASP.NET Starter Kit, and built simpler Model-Bound views. In this article, we’ll focus on more complex components. These are code-driven views similar to those you have built using an MVC controller in the past.
For demonstration purposes, we’ll build a API driven component that retrieves user data and displays them on a new component. Since this is purely for demonstation, we’ll use the Random User Generator API, but in your case, you’ll write your own complex code or use your needed APIs.
Create the Component
A few of our steps are the same as when creating a model-bound view, so let’s get started.
- Begin by understanding the basics.
- Create the Sitecore items and definitions. Stop when you get to the “Create the code” section in the linked article.
Create the View Component Code
Similar to a model bound view, we need a model. For our demonstration, we’re using the Random User Generator API to generate some random information about a fake user and display those results in our component.
To begin, we need a model that will hold our field information from Sitecore. This could be any data or parameters you wish for the author to enter when creating the component datsaource. In the model below, SitecoreImge is a field on our datasource template, while UserModelResponse will come from the API.
using Sitecore.AspNetCore.SDK.LayoutService.Client.Response.Model.Fields;
using Sitecore.AspNetCore.SDK.RenderingEngine.Binding.Attributes;
namespace Sitecore.AspNetCore.Starter.Models.User
{
public class UserProfileWidgetModel : BaseModel
{
[SitecoreComponentField(Name = "Image")]
public ImageField? SitecoreImage { get; set; }
public UserResponse? UserModelResponse { get; set; }
}
}
C#The API above returns the data as JSON, and we’ll want to create our C# model from it.
{
"results": [
{
"gender": "female",
"name": {
"title": "Miss",
"first": "Jennie",
"last": "Nichols"
},
"location": {
"street": {
"number": 8929,
"name": "Valwood Pkwy",
},
"city": "Billings",
"state": "Michigan",
"country": "United States",
"postcode": "63104",
"coordinates": {
"latitude": "-69.8246",
"longitude": "134.8719"
},
"timezone": {
"offset": "+9:30",
"description": "Adelaide, Darwin"
}
},
"email": "jennie.nichols@example.com",
"login": {
"uuid": "7a0eed16-9430-4d68-901f-c0d4c1c3bf00",
"username": "yellowpeacock117",
"password": "addison",
"salt": "sld1yGtd",
"md5": "ab54ac4c0be9480ae8fa5e9e2a5196a3",
"sha1": "edcf2ce613cbdea349133c52dc2f3b83168dc51b",
"sha256": "48df5229235ada28389b91e60a935e4f9b73eb4bdb855ef9258a1751f10bdc5d"
},
"dob": {
"date": "1992-03-08T15:13:16.688Z",
"age": 30
},
"registered": {
"date": "2007-07-09T05:51:59.390Z",
"age": 14
},
"phone": "(272) 790-0888",
"cell": "(489) 330-2385",
"id": {
"name": "SSN",
"value": "405-88-3636"
},
"picture": {
"large": "https://randomuser.me/api/portraits/men/75.jpg",
"medium": "https://randomuser.me/api/portraits/med/men/75.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/men/75.jpg"
},
"nat": "US"
}
],
"info": {
"seed": "56d27f4a53bd5441",
"results": 1,
"page": 1,
"version": "1.4"
}
}
JSONNow, let’s convert that to our C# model.
PRO TIP: You can use online generators to make this process easy and painless.
using System.Text.Json.Serialization;
namespace Sitecore.AspNetCore.Starter.Models.User
{
public class UserResponse
{
[JsonPropertyName("results")]
public List<UserModel>? UserModel { get; set; }
[JsonPropertyName("info")]
public Info? Info { get; set; }
}
public class UserModel
{
[JsonPropertyName("gender")]
public string? Gender { get; set; }
[JsonPropertyName("name")]
public Name? Name { get; set; }
[JsonPropertyName("location")]
public Location? Location { get; set; }
[JsonPropertyName("email")]
public string? Email { get; set; }
[JsonPropertyName("login")]
public Login? Login { get; set; }
[JsonPropertyName("dob")]
public Dob? Dob { get; set; }
[JsonPropertyName("registered")]
public Registered? Registered { get; set; }
[JsonPropertyName("phone")]
public string? Phone { get; set; }
[JsonPropertyName("cell")]
public string? Cell { get; set; }
[JsonPropertyName("id")]
public Id? Id { get; set; }
[JsonPropertyName("picture")]
public Picture? Picture { get; set; }
[JsonPropertyName("nat")]
public string? Nat { get; set; }
}
public class Coordinates
{
[JsonPropertyName("latitude")]
public string? Latitude { get; set; }
[JsonPropertyName("longitude")]
public string? Longitude { get; set; }
}
public class Dob
{
[JsonPropertyName("date")]
public DateTime? Date { get; set; }
[JsonPropertyName("age")]
public int? Age { get; set; }
}
public class Id
{
[JsonPropertyName("name")]
public string? Name { get; set; }
[JsonPropertyName("value")]
public string? Value { get; set; }
}
public class Info
{
[JsonPropertyName("seed")]
public string? Seed { get; set; }
[JsonPropertyName("results")]
public int? Results { get; set; }
[JsonPropertyName("page")]
public int? Page { get; set; }
[JsonPropertyName("version")]
public string? Version { get; set; }
}
public class Location
{
[JsonPropertyName("street")]
public Street? Street { get; set; }
[JsonPropertyName("city")]
public string? City { get; set; }
[JsonPropertyName("state")]
public string? State { get; set; }
[JsonPropertyName("country")]
public string? Country { get; set; }
[JsonPropertyName("postcode")]
public string? Postcode { get; set; }
[JsonPropertyName("coordinates")]
public Coordinates? Coordinates { get; set; }
[JsonPropertyName("timezone")]
public Timezone? Timezone { get; set; }
}
public class Login
{
[JsonPropertyName("uuid")]
public string? Uuid { get; set; }
[JsonPropertyName("username")]
public string? Username { get; set; }
[JsonPropertyName("password")]
public string? Password { get; set; }
[JsonPropertyName("salt")]
public string? Salt { get; set; }
[JsonPropertyName("md5")]
public string? Md5 { get; set; }
[JsonPropertyName("sha1")]
public string? Sha1 { get; set; }
[JsonPropertyName("sha256")]
public string? Sha256 { get; set; }
}
public class Name
{
[JsonPropertyName("title")]
public string? Title { get; set; }
[JsonPropertyName("first")]
public string? First { get; set; }
[JsonPropertyName("last")]
public string? Last { get; set; }
}
public class Picture
{
[JsonPropertyName("large")]
public string? Large { get; set; }
[JsonPropertyName("medium")]
public string? Medium { get; set; }
[JsonPropertyName("thumbnail")]
public string? Thumbnail { get; set; }
}
public class Registered
{
[JsonPropertyName("date")]
public DateTime? Date { get; set; }
[JsonPropertyName("age")]
public int? Age { get; set; }
}
public class Street
{
[JsonPropertyName("number")]
public int? Number { get; set; }
[JsonPropertyName("name")]
public string? Name { get; set; }
}
public class Timezone
{
[JsonPropertyName("offset")]
public string? Offset { get; set; }
[JsonPropertyName("description")]
public string? Description { get; set; }
}
}
C#With our model fully built out, we can now focus on the actual View Component code that retrieves this data, binds the model and performs any other complex logic we need, all with .NET code.
View Components must inherit from Microsoft.AspNetCore.Mvc.ViewComponent class and must inject an instance of IViewModelBinder. In our case, we’re calling an API, so we’ll also inject IHttpClientFactory, but this is optional if you’re not making any HttpClient calls in your component.
Let’s take a look at the code and we can break it down:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewComponents;
using Sitecore.AspNetCore.SDK.RenderingEngine.Binding;
using Sitecore.AspNetCore.Starter.Models.User;
using System.Text.Json;
namespace Sitecore.AspNetCore.Starter.ViewComponents
{
[ViewComponent(Name = ViewComponentName)]
public class UserProfileComponent(IViewModelBinder modelBinder, IHttpClientFactory httpClientFactory) : ViewComponent
{
public const string ViewComponentName = "UserProfileComponent";
private readonly IViewModelBinder _modelBinder = modelBinder;
private readonly IHttpClientFactory _httpClientFactory = httpClientFactory;
public async Task<IViewComponentResult> InvokeAsync()
{
IViewComponentResult result = new ContentViewComponentResult(string.Empty);
// Get the model from the ViewModelBinder
var model = await _modelBinder.Bind<UserProfileWidgetModel>(ViewContext);
try
{
//instantiating the http client (defined in the Program.cs)
var client = _httpClientFactory.CreateClient("UserProfileClient");
//get the weather data async
HttpResponseMessage response = await client.GetAsync("api/");
response.EnsureSuccessStatusCode();
//read the json
string jsonString = await response.Content.ReadAsStringAsync();
//read the response and deserialize it
model.UserModelResponse = JsonSerializer.Deserialize<UserResponse>(jsonString);
// Use the model to render the view
result = View("~/Views/Shared/Components/Users/UserProfileComponent.cshtml", model);
}
catch (Exception ex)
{
// Handle exceptions and log errors if necessary
Console.WriteLine($"Error: {ex.Message}");
}
return result;
}
}
}
C#Let’s break down the code.
Line 12: public const string ViewComponentName = "UserProfileComponent";
The is where we define the name of the component. This is important because it has to match the component name we provided to Sitecore in the rendering definition
Line 9: [ViewComponent(Name = ViewComponentName)]
This references the name we provided.
Our code for the component is executed inside the InvokeAsync()
method. Alternatively, if you plan on executing your code synchronously, you can execute the Invoke()
method instead.
Line 20: var model = await _modelBinder.Bind(ViewContext);
we use the SDK’s model binder to bind our datasource to our model.
We then call the API, and handle the response, and return our view with the complete model. The last step in making it all work is to bind our ViewComponent to our Rendering Engine. In the Sitecore Starter Kit, this is done in the Extensions/ServiceCollectionExtensions.cs file. Simply add your mapping:
renderingEngineOptions..AddViewComponent(UserProfileComponent.ViewComponentName);
C#To make the HttpClient example above work, the following was added to program.cs. We’re basically creating a reusable HttpClient that can be used in multiple components.
builder.Services.AddHttpClient("UserProfileClient", client =>
{
client.BaseAddress = new Uri("https://randomuser.me/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
});
C#Our view is then is pretty straightforward and can map our model properties to the view. Obviously, your view and final HTML would be built according to your needs.
@model Sitecore.AspNetCore.Starter.Models.User.UserProfileWidgetModel
<div class="card">
<div class="details">
<div class="user_photo horizontal_center" id="user_photo">
<sc-img asp-for="@Model.SitecoreImage" />
</div>
<p id="user_title">Hi, My name is</p>
<p id="user_value">@(Model.UserModelResponse?.PrimaryModel?.Name?.FullName)</p>
</div>
<ul class="values_list horizontal_center" id="values_list">
<li data-title="Hi, My name is"
data-value="@Model.UserModelResponse?.PrimaryModel?.Name?.FullName"
data-label="name"
class="active"></li>
<li data-title="My email address is"
data-value="@Model.UserModelResponse?.PrimaryModel?.Email"
data-label="email"
data-caps="false"></li>
<li data-title="My birthday is"
data-value="@Model.UserModelResponse?.PrimaryModel?.Dob?.Date?.ToShortDateString()"
data-label="birthday"></li>
<li data-title="My address is"
data-value="@Model.UserModelResponse?.PrimaryModel?.Location?.Street?.Address"
data-label="location"></li>
<li data-title="My phone number is"
data-value="@Model.UserModelResponse?.PrimaryModel?.Phone"
data-label="phone"></li>
<li data-title="My password is"
data-value="@Model.UserModelResponse?.PrimaryModel?.Login?.Password"
data-label="pass"
data-caps="false"></li>
</ul>
</div>
RazorOnce you add the rendering to your available options, you can then see it in your components list and drag it onto the page.

And if it all works well, you ‘ll end up with something like this:

Related
Create a View Component for Sitecore XM Cloud, using the ASP.NET Core SDK