<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[TheFaithApp]]></title><description><![CDATA[TheFaithApp]]></description><link>https://thefaithapp.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>TheFaithApp</title><link>https://thefaithapp.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Mon, 12 Oct 2026 15:44:27 GMT</lastBuildDate><atom:link href="https://thefaithapp.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Editable template generators and preserving the draft during filtering]]></title><description><![CDATA[TheFaithApp builds church management software, including free tools that help church teams prepare messages and visitor follow-up plans. One small interaction deserves careful treatment: a user edits ]]></description><link>https://thefaithapp.hashnode.dev/editable-template-generators-and-preserving-the-draft-during-filtering</link><guid isPermaLink="true">https://thefaithapp.hashnode.dev/editable-template-generators-and-preserving-the-draft-during-filtering</guid><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Rowan Hale]]></dc:creator><pubDate>Thu, 08 Oct 2026 07:56:32 GMT</pubDate><content:encoded><![CDATA[<p>TheFaithApp builds church management software, including free tools that help church teams prepare messages and visitor follow-up plans. One small interaction deserves careful treatment: a user edits a generated message, then changes the occasion filter.</p>
<p>The filter should change which drafts are visible while keeping the edited text. Regeneration has a different purpose: it creates fresh drafts from the current settings and can replace those edits. Keeping those operations separate makes the interface easier to understand and the output easier to trust.</p>
<p>The complete runnable HTML example is included in <a href="https://dev.to/thefaithapp/a-category-filter-should-not-reset-an-edited-textarea-2e6">A category filter should not reset an edited textarea</a>. Save the code as <code>example.html</code> to try the editing sequence.</p>
<h2>Keep the template and the edited draft separate</h2>
<p>A template generator has three useful inputs to distinguish. The original template defines the starting words. Personalization values fill its placeholders. The editable draft contains the words a person currently wants to use, including any changes they made after generation.</p>
<p>For example, a generated reminder might say “Please bring a notebook.” A person could replace that with “Bring the blue registration folder.” Rebuilding the message from its original template would erase that decision.</p>
<p>Use a stable identifier for each template, such as <code>event-reminder</code>. In a small page, a map from that identifier to an existing card is enough to find its textarea without recreating the control:</p>
<pre><code class="language-js">const cards = new Map(templates.map((template) =&gt; [
  template.id,
  document.querySelector(`[data-id="${template.id}"]`),
]));
</code></pre>
<p>The textarea’s <code>value</code> is the current editable text. Copying the original template or reading its initial HTML content can miss later edits. <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement/value">MDN explains how the textarea value property reads and sets its current contents.</a></p>
<p>For a larger interface that removes cards from the DOM, store the edited text by the same stable identifier and restore it when a card returns. Keeping existing controls is simpler for a short list; it does not provide persistence across a page reload.</p>
<h2>Make filtering change visibility</h2>
<p>The filter handler only needs to update which cards are shown:</p>
<pre><code class="language-js">function filterCards() {
  for (const template of templates) {
    const card = cards.get(template.id);
    card.hidden = category.value !== 'all'
      &amp;&amp; template.category !== category.value;
  }
}
</code></pre>
<p>This function never writes to a textarea. A draft can disappear when someone selects another category and return with the same text. The HTML <code>hidden</code> attribute controls whether a card is rendered. It is a visibility mechanism, so any later operation that needs only visible drafts must explicitly filter for them. <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/hidden">MDN documents the hidden attribute and its behavior.</a></p>
<p>A common failure is connecting the category selector to a general “render everything” function that also assigns newly generated text. The list looks correctly filtered, but the person’s edits have disappeared. Give visibility and generation separate functions so the category handler cannot accidentally replace the draft.</p>
<h2>Make replacement deliberate</h2>
<p>Regeneration intentionally assigns fresh values:</p>
<pre><code class="language-js">function regenerate() {
  const values = {
    group: group.value.trim() || 'Community team',
    time: time.value.trim() || 'Thursday at 6pm',
  };
  for (const template of templates) {
    const card = cards.get(template.id);
    card.querySelector('textarea').value = template.build(values);
    updateCount(card);
  }
  filterCards();
}
</code></pre>
<p>In this standalone example, changing the settings leaves the current drafts intact until a person clicks <strong>Regenerate drafts</strong>. The warning beside that button explains that regeneration replaces manual edits in every draft, including hidden drafts.</p>
<p>TheFaithApp’s SMS generator uses a related boundary: changing the occasion preserves edited drafts, while changing personalization or opt-out settings rebuilds them. That replacement policy needs visible wording beside the settings and a clear status message. An explicit button, as used in the example, is another way to make the boundary easy to see.</p>
<p>Avoid saying “your edits are saved” when they only remain in the current page. Tell people to copy or download their work before leaving if the tool has no persistent draft storage.</p>
<h2>Copy and download the current text</h2>
<p>Read the textarea when the person requests an output. A freshly generated message may differ from the text they have just reviewed.</p>
<pre><code class="language-js">function visibleDrafts() {
  return templates
    .filter((template) =&gt; !cards.get(template.id).hidden)
    .map((template) =&gt; ({
      title: template.title,
      message: cards.get(template.id).querySelector('textarea').value,
    }));
}
</code></pre>
<p>A per-card copy action can read that card’s textarea directly. Await the clipboard write before displaying a success message. If the browser rejects the write, select the draft and offer manual copying. Clipboard access has security requirements and can fail, so the fallback should preserve the text. <a href="https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText">MDN describes the clipboard write operation and its failure conditions.</a></p>
<p>For a download, collect the current visible drafts, build a text <code>Blob</code>, and create a temporary object URL. Release that URL after initiating the download. <a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL_static">MDN explains object URLs and their lifecycle.</a></p>
<p>The example’s <code>Array.from(text).length</code> counter counts Unicode code points. It does not calculate visible grapheme clusters, SMS segments, or delivery charges. <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Symbol.iterator">JavaScript string iteration yields code points.</a></p>
<h2>Keep entered text out of analytics</h2>
<p>The sample uses no analytics, network calls, or storage. If a production tool records interactions, a fixed template identifier and a fixed action name can describe a copy or filter event. Draft text, personalization values, and clipboard contents should stay out of those event payloads.</p>
<h2>Check the complete editing sequence</h2>
<p>Exercise the interaction as a person would use it:</p>
<ol>
<li><p>Edit a generated draft, select another category, and return. The edited text should remain unchanged.</p>
</li>
<li><p>Change the settings and regenerate. Fresh text should replace the earlier edits according to the stated policy.</p>
</li>
<li><p>Edit that fresh draft again, then copy it. The output should match the final textarea value.</p>
</li>
<li><p>Download one category. The file should contain its current edits and exclude hidden drafts.</p>
</li>
<li><p>Deny clipboard access. The page should offer manual copying without claiming that the automatic copy succeeded.</p>
</li>
</ol>
<p>Those checks cover the handoff from generated text to a person’s work. Keeping that handoff explicit protects the draft through the small interactions people use to find, refine, and keep it.</p>
<p>Disclosure: AI assisted the wording and code example.</p>
]]></content:encoded></item></channel></rss>