Skip to main content

inline question preview

  1. The Core Interaction: Expandable Rows

Challenge: You need to track which single question row is expanded for editing and conditionally show the editor form only for that row. Svelte 5 Solution: This is a classic state management and conditional rendering task, perfectly handled by $state and {#if} blocks.

  • $state for Selection: You'll use a $state variable to hold the ID of the currently expanded question. Clicking a row will update this variable.
<script lang="ts">
let expandedQuestionId = $state<string | null>(null);
</script>
  • {#if} for Conditional Rendering: Inside your {#each} block that lists the questions, you'll use an {#if} block to check if the current question's ID matches the one in your state variable. The editor component will only be rendered for that specific row.
  • $state for Selection: You'll use a $state variable to hold the ID of the currently expanded question. Clicking a row will update this variable.
<script lang="ts">
let expandedQuestionId = $state<string | null>(null);
</script>
  1. The Editor Experience: Single View with Live Preview

Challenge: The form needs to update a live preview in real-time as the admin types. Svelte 5 Solution: This is where Svelte's core reactivity shines. You'll use a $state object to hold the data for the question being edited and bind:value to link the form inputs to it.

  • $state for Editable Data: The form will edit a temporary copy of the question's data, held in a $state object.
  • bind:value for Two-Way Binding: Each form field (<input> for the stem, inputs for the choices) will be bound to a property on the temporary state object using bind:value.
  • Reactive Preview: The preview area will simply display the text from that same temporary state object. Because both the form and the preview are reading from the same reactive $state, any change in an input will instantly and automatically update the preview with no extra code required.
<script lang="ts">
let { question } = $props();
// `editable` is a reactive copy of the question data
let editable = $state({ ...question });
</script>

<div class="editor-layout">
<div class="form-side">
<label>Question Stem:</label>
<textarea bind:value={editable.stem}></textarea>
</div>

<div class="preview-side">
<h3>{editable.stem}</h3>
</div>
</div>
  1. Content Type Constraint: Multiple Choice Questions (MCQ) Only

Challenge: The form needs to manage a dynamic list of answer choices (adding, editing, deleting). Svelte 5 Solution: You'll leverage the deep reactivity of $state objects with arrays, combined with an {#each} block.

  • Deep Reactivity for Choices: The choices property on your editable question object will be an array. Because it's part of a $state proxy, methods like .push() or .splice() will automatically trigger UI updates.
  • {#each} for Dynamic Inputs: You will use an {#each} block to loop over the editable.choices array and render an <input> for each one. Buttons to "Add Choice" or "Remove" will simply call array methods on editable.choices.
{#each editable.choices as choice, i (i)}
<div>
<input type="text" bind:value={editable.choices[i]} />
<button onclick={() => editable.choices.splice(i, 1)}>Remove</button>
</div>
{/each}
<button onclick={() => editable.choices.push('')}>Add Choice</button>
  1. The "New Question" Flow

Challenge: A "New Question" button needs to add a blank, expanded editor row to the top of the list. Svelte 5 Solution: This is a simple state manipulation. The button's onclick handler will modify the main array of questions and set the expandedQuestionId state.

  • Event Handling: The "New Question" button will have an onclick handler.
  • Array Manipulation: The handler will create a new, blank question object and add it to the beginning of your main questions array.
  • State Update: The handler will then immediately set expandedQuestionId to the ID of the new question, causing its editor to appear.
// In your main Question Library page
function handleNewQuestion() {
const newQuestion = { id: crypto.randomUUID(), stem: '', choices: [''], ... };

// Add the new question to the top of the reactive array
questions = [newQuestion, ...questions];

// Set the state to expand the new question's editor
expandedQuestionId = newQuestion.id;
}