Setting Up the Basic Tutor Interface

Welcome to the first lesson of our course on developing a personal tutor web application with ASP.NET Core and Razor Pages! In this lesson, we will focus on setting up a basic tutor interface using ASP.NET Core and HTML. This is an essential step in creating a user-friendly web application that enhances the learning experience. A well-designed interface is crucial for engaging students and ensuring they can interact with the tutor seamlessly.

ASP.NET Core is a modern, high-performance web framework for building web applications with C#. It provides a flexible approach to web development, allowing you to create dynamic web pages, APIs, and more. In this lesson, we will use Razor Pages, a feature of ASP.NET Core that makes it easy to build web pages with server-side logic and dynamic content.

Basic Tutor Interface Mockup

To help you better visualize the interface we're building, here's a simple mockup of how the tutor web page will look after following the steps in this lesson:

+------------------------------------------------------+
|          Welcome to Your Personal Tutor              |
|        What would you like to learn today?           |
+------------------------------------------------------+
|                                                      |
|  [Conversation appears here as messages]             |
|                                                      |
|  [User] Hello, can you help me with math?            |
|  [Assistant] Sure thing! Let's get started with ...  |
|                                                      |
+------------------------------------------------------+
| [ Type your question... ]                            |
| [Send] [New Session]                                 |
+------------------------------------------------------+
  • The header is at the top, welcoming the user.
  • The messages area is in the center, where the conversation between the student and tutor appears.
  • At the bottom, there is an input field for typing questions, a Send button, and a New Session button.
HTML Rendering with ASP.NET Core and Razor

ASP.NET Core uses a templating engine called Razor to render dynamic HTML content. Razor allows you to combine C# code with HTML markup, making it easy to generate web pages that can display dynamic data and respond to user interactions.

Razor Pages are structured so that each page consists of a .cshtml file (which contains the HTML and Razor markup) and an optional C# code-behind file for server-side logic. Razor syntax lets you:

  • Insert dynamic C# variables into HTML
  • Use control structures like loops and conditionals
  • Create reusable components with partial views
  • Apply filters and formatting to data during rendering

By using Razor Pages, you can keep your application logic and presentation layer organized and maintainable while taking advantage of the full power of C# for your web application.

Creating the Pages Directory

To serve HTML templates with ASP.NET Core, we use a dedicated Pages directory in our project. This directory stores all our Razor view files (.cshtml) that will be rendered by the application.

TutorApp/

├── Pages/
│   └── Index.cshtml
│   └── Index.cshtml.cs
├── wwwroot/
├── Program.cs
├── Startup.cs
└── ...

The Pages directory is where Razor Pages are placed by default in an ASP.NET Core Razor Pages project. Each page consists of a .cshtml file for the markup and an optional .cshtml.cs file for the page model (C# code-behind). This organization follows the conventional structure for ASP.NET Core web applications and keeps your view files separate from the application logic.

Setting Up Razor Pages in ASP.NET Core

Once we have our directory structure in place, we need to configure ASP.NET Core to use Razor Pages for template rendering. This is done by setting up the project to use Razor Pages in the Startup.cs file (or Program.cs in newer versions).

// Program.cs (for .NET 6 and later)
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();
app.UseRouting();

app.MapRazorPages();

app.Run();

The AddRazorPages() method registers Razor Pages services with the application. The MapRazorPages() method enables routing to Razor Pages. This setup allows ASP.NET Core to locate and render .cshtml files in the Pages directory.

The practice Program.cs includes extra setup (like session, APIs, and Swagger), but you still enable Razor Pages with AddRazorPages() and MapRazorPages(), and you can redirect the root URL to your main page using return Results.Redirect("/Index");.

Rendering Views and Passing Data

With Razor Pages configured, we can now render HTML templates and pass data to them. Each Razor Page consists of a .cshtml file and an optional page model (.cshtml.cs) for handling server-side logic.

Pages/Index.cshtml

@page
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Personal Tutor</title>
</head>
<body>
    <div class="header">
        <h1>Welcome to Your Personal Tutor</h1>
        <p>What would you like to learn today?</p>
    </div>
    ...
</body>
</html>

What is @page?

The @page directive at the top of the .cshtml file tells ASP.NET Core that this file is a Razor Page and can be directly accessed via a URL. It enables routing to the page without needing a separate controller. When you add @page to a Razor file, ASP.NET Core treats it as an endpoint, so visiting /Index in your browser will render Pages/Index.cshtml. This directive is required for Razor Pages to work and is typically the first line in each page’s .cshtml file.

Tutor Container and Input Elements

    ...
    <div id="chat-container">
        <div id="messages"></div>
        <div class="input-container">
            <div class="input-wrapper">
                <input type="text" id="message-input" placeholder="Type your question..." />
            </div>
            <button onclick="sendQuery()">Send</button>
            <button id="new-chat-btn" onclick="startNewSession()">New Session</button>
        </div>
    </div>
    <script>
        // JavaScript for Interactivity
    </script>
</body>
</html>

Pages/Index.cshtml.cs

using Microsoft.AspNetCore.Mvc.RazorPages;

public class IndexModel : PageModel
{
    public void OnGet()
    {
        // Server-side logic (if needed)
    }
}

This structure allows you to keep your markup and server-side logic organized. The .cshtml file contains the HTML and Razor markup, while the .cshtml.cs file (the page model) contains any C# code needed to handle requests or pass data to the view. By using the @page directive and this file structure, ASP.NET Core can render dynamic content and respond to user interactions efficiently.

HTML Structure and Header

The HTML template for the tutor interface begins with the basic structure of an HTML document. This includes the <!DOCTYPE html> declaration, which defines the document type and version of HTML being used. The <html> tag wraps the entire content of the page, and within it, the <head> section is defined.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Personal Tutor</title>
</head>
</html>

In the <head> section, we set the title of the page to "Personal Tutor", establishing the foundation for the tutor interface.

Body and Header Section

Moving into the <body> of the document, we start with a header section that sets the tone for the tutor interface. This section is designed to welcome students and encourage them to engage with the personal tutor.

<body>
    <div class="header">
        <h1>Welcome to Your Personal Tutor</h1>
        <p>What would you like to learn today?</p>
    </div>
</body>

The header includes a main heading (<h1>) and a paragraph (<p>), providing a friendly introduction to the tutoring service. This sets the stage for the interactive elements that follow.

Tutor Container and Input Elements

Following the header, we define the tutor container, which is the core of the user interface. This section is responsible for displaying the conversation and providing input elements for student interaction.

<div id="chat-container">
    <div id="messages"></div>
    <div class="input-container">
        <div class="input-wrapper">
            <input type="text" id="message-input" placeholder="Type your question..." />
        </div>
        <button onclick="sendQuery()">Send</button>
        <button id="new-chat-btn" onclick="startNewSession()">New Session</button>
    </div>
</div>

The #messages div is where the conversation between the student and tutor will appear, while the input field and buttons allow students to type and send questions. The "Send" button triggers the sendQuery function, and the "New Session" button clears the conversation history, preparing the interface for a new tutoring session.

JavaScript for Interactivity

After setting up the HTML structure, we move on to adding interactivity to our tutor interface using JavaScript. This is done by placing a script section at the bottom of the HTML document, where we'll define the necessary JavaScript functions.

In this section, we create a script block within our HTML code to define JavaScript functions that enable interactivity in the tutor interface. By using plain JavaScript, we can directly manipulate HTML elements and handle user events. Placing the script at the end of the document ensures that all HTML elements are fully loaded before the script runs, preventing errors that might occur if the script tries to access elements that haven't been rendered yet. This approach allows us to seamlessly integrate JavaScript into our HTML, enhancing the functionality of our web application.

Before implementing the functions that handle tutor interactions, it's important to obtain references to the necessary DOM elements. This allows us to manipulate these elements directly within our JavaScript code.

<script>
    // Get references to the messages container and message input field
    const messagesContainer = document.getElementById('messages');
    const messageInput = document.getElementById('message-input');
</script>

By retrieving references to the messagesContainer and messageInput elements, we can easily update the tutor interface and handle student input. The messagesContainer is where the conversation will be displayed, and the messageInput is the field where students type their questions. These references are crucial for implementing the interactive functions that follow.

StartNewSession Function

With the necessary DOM elements initialized, we can proceed to create functions that enhance the interactivity of our tutor interface. The startNewSession function is designed to clear the conversation history, allowing students to begin a fresh tutoring session. This function is triggered when the "New Session" button is clicked.

<script>
    function startNewSession() {
        // Clear the chat history
        messagesContainer.innerHTML = '';
    }

    // Start a session automatically when the page loads
    document.addEventListener('DOMContentLoaded', startNewSession);
</script>

The startNewSession function clears all messages from the tutor interface, providing a clean slate for students to start a new learning session. This functionality is essential for resetting the conversation and enhancing the user experience by allowing multiple interactions without refreshing the page.

Additionally, by adding an event listener for the DOMContentLoaded event, we ensure that the startNewSession function is automatically called when the page finishes loading. This means the tutor interface is always initialized with a clean state, ready for student interaction as soon as the page is accessed. This approach enhances the learning experience by ensuring the tutor is ready to use immediately upon loading.

AppendMessage Function

To effectively display the conversation in our tutor interface, we use the appendMessage function. This function creates a new message element, assigns it a CSS class based on the message's origin (user or assistant), appends it to the conversation container, and ensures the view scrolls to the latest message.

<script>
    function appendMessage(role, content) {
        // Create a new div element for the message
        const messageDiv = document.createElement('div');

        // Assign a class to the message based on its role (user or assistant)
        messageDiv.className = `message ${role}`;

        // Set the text content of the message
        messageDiv.textContent = content;

        // Append the message to the messages container
        messagesContainer.appendChild(messageDiv);

        // Scroll the messages container to the bottom to show the latest message
        messagesContainer.scrollTop = messagesContainer.scrollHeight;
    }
</script>

The appendMessage function is crucial for dynamically adding messages to the tutor interface. It creates a new <div> element for each message, assigns a class to differentiate between student and tutor messages, and appends it to the messagesContainer. This function also includes code to automatically scroll the conversation view to the bottom, keeping the latest messages in view. (Note: The scrolling will work as intended after we implement the CSS file in a later step.)

SendQuery Function

Building on the appendMessage function, the sendQuery function handles student input and updates the tutor interface. It processes the student's question, displays it, and simulates a response from the tutor. This function is triggered when the "Send" button is clicked or when the student presses Enter.

<script>
    function sendQuery() {
        // Retrieve and trim the input value
        const message = messageInput.value.trim();

        // If the message is empty, do not proceed
        if (!message) return;

        // Add user message to display
        appendMessage('user', message);

        // Clear the input field after sending the message
        messageInput.value = '';

        // For now, just echo the message back
        setTimeout(() => {
            appendMessage('assistant', `You asked: ${message}`);
        }, 500);
    }
</script>

The sendQuery function is responsible for capturing the student's input, ensuring it's not empty, and then displaying it in the tutor interface using the appendMessage function. After sending the question, it clears the input field to prepare for the next question. It also simulates a response from the tutor by echoing the student's question back after a short delay, demonstrating basic interactivity in the tutoring application.

Handling the Enter Key

To enhance the user experience, we can allow students to send questions by pressing the Enter key. This functionality is implemented by listening for the Enter key press event on the input field.

<script>
    // Handle Enter key
    messageInput.addEventListener('keypress', function (e) {
        if (e.key === 'Enter') {
            // Prevent the default form submission behavior
            e.preventDefault();
            // Send the message when Enter key is pressed
            sendQuery();
        }
    });
</script>

This code snippet listens for the keypress event on the messageInput field. When the Enter key is pressed, it prevents the default behavior (which would be to insert a newline) and calls the sendQuery function. This allows students to quickly send questions using the keyboard, improving the tutor interface's usability.

Summary and Preparation for Practice

In this lesson, we covered the essential steps for setting up a basic tutor interface using ASP.NET Core Razor Pages and HTML. We explored how ASP.NET Core serves HTML templates using Razor and how JavaScript is used to handle student interactions. By understanding the integration among ASP.NET Core, Razor Pages, HTML, and JavaScript, you have laid the groundwork for building a dynamic web application for personalized tutoring. As you move on to the practice exercises, focus on reinforcing these concepts and experimenting with the code to deepen your understanding. This foundational knowledge will be crucial as we continue to enhance the personal tutor's capabilities in future lessons.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal