DEV Community

Cover image for Morphing Feature in WebForms Core 2.1
Elanat Framework
Elanat Framework

Posted on

Morphing Feature in WebForms Core 2.1

WebForms Core 2.1 is coming soon from Elanat. The new version introduces a collection of capabilities designed to further expand the server-driven approach of WebForms Core. One of these new capabilities is Morphing.

Morphing provides a way to synchronize an existing DOM element with a new HTML structure without necessarily replacing the existing element itself.

This makes it possible to update HTML structures while preserving the identity of existing DOM elements.


Morphing

Morphing is a DOM synchronization mechanism that compares an existing HTML element with a new HTML structure and applies the required changes to the existing DOM.

Unlike a traditional replacement operation such as:

element.outerHTML = html;
Enter fullscreen mode Exit fullscreen mode

Morphing does not simply discard the existing element and create another one.

Instead, it analyzes the existing element and the new element and performs the necessary operations:

  • Add new attributes
  • Update existing attributes
  • Remove attributes that no longer exist
  • Add new child elements
  • Update existing child elements
  • Remove obsolete child elements
  • Match elements using id and cb-data-id
  • Preserve existing DOM element identity whenever possible
  • Preserve registered event listeners when new Nodes have to be created

The goal is to make the smallest necessary changes to the DOM.


Reflection vs Morphing

WebForms Core 2.1 contains both Reflection and Morphing, but they serve different purposes.

Reflection is primarily a merge operation.

For example, if the target contains:

 id="userCard">
    

User

Enter fullscreen mode Exit fullscreen mode

and the source contains:

 class="premium">
    
Enter fullscreen mode Exit fullscreen mode

Reflection can merge the source into the target, adding the class and child without treating the source as a complete replacement definition.

Morphing has a different philosophy.

The source represents the desired structure.

If the source does not contain an element or attribute that exists in the target, Morphing can remove it.

Therefore:

Reflection
    Target + Source
         ↓
       Merge

Morphing
    Target → Source
         ↓
      Synchronize
Enter fullscreen mode Exit fullscreen mode

This distinction makes both mechanisms useful for different types of server-driven UI operations.


The WebForms Core API

Morphing can be invoked from the WebForms class.

The first method accepts HTML directly:

SetMorph(string InputPlace, string Tag)
Enter fullscreen mode Exit fullscreen mode

The second method uses an existing element as the source:

SetMorphByOutputPlace(string InputPlace, string OutputPlace)
Enter fullscreen mode Exit fullscreen mode

For example:

form.SetMorphByOutputPlace(
    "userCard",
    "userCardTemplate"
);
Enter fullscreen mode Exit fullscreen mode

Here:

  • userCard is the destination element.
  • userCardTemplate is the source element.

The developer does not need to manually write JavaScript to perform the DOM synchronization.


A Complete Example

Consider the following page.

Initially, the page contains a user card:

 id="userCard"
     class="card"
     data-user="123"
     style="border: 1px solid #ccc; padding: 10px;">

    

User

id="userEmail"> Email: user@example.com

id="editButton" class="btn-edit" onclick="editUser()"> Edit id="oldTag"> Old tag
Enter fullscreen mode Exit fullscreen mode

A template describes the new state of this element:

 id="userCardTemplate">

     id="userCard"
         class="card premium"
         data-user="456"
         data-role="admin"
         style="background-color: #f0f8ff; border: 2px solid blue; margin: 5px;">

        

User

id="editButton" class="btn-vip" onclick="VIP()"> VIP id="newTag"> New tag
Enter fullscreen mode Exit fullscreen mode

The controller can perform the Morph operation with:

using CodeBehind;

public partial class MorphController : CodeBehindController
{
    public void PageLoad(HttpContext context)
    {
        // Checking "Post-Back" in Request Headers for Maintain Interactive mode when Refreshing the Page in the Browser.
        if (context.Request.Query.ContainsKey("set-morph") && context.Request.Headers.ContainsKey("Post-Back"))
        {
            SetMorph_OnClick(context);
            return;
        }
    }

    private void SetMorph_OnClick(HttpContext context)
    {
        WebForms form = new WebForms();

        form.SetMorphByOutputPlace("userCard", "userCardTemplate");

        IgnoreAll();
        Write(form.Response());
    }
}
Enter fullscreen mode Exit fullscreen mode

The page can trigger the operation with a normal WebForms Core request:

 href="?set-morph">Click to Link for Morph
Enter fullscreen mode Exit fullscreen mode

What happens during Morphing?

Before Morphing, the target is:

 id="userCard"
     class="card"
     data-user="123"
     style="border: 1px solid #ccc; padding: 10px;">

    

User

id="userEmail"> Email: user@example.com

id="editButton" class="btn-edit" onclick="editUser()"> Edit id="oldTag"> Old tag
Enter fullscreen mode Exit fullscreen mode

The source describes:

 id="userCard"
     class="card premium"
     data-user="456"
     data-role="admin"
     style="background-color: #f0f8ff; border: 2px solid blue; margin: 5px;">

    

User

id="editButton" class="btn-vip" onclick="VIP()"> VIP id="newTag"> New tag
Enter fullscreen mode Exit fullscreen mode

Morphing compares these two structures and applies the necessary changes.

The resulting DOM becomes:

 id="userCard"
     class="card premium"
     data-user="456"
     style="background-color: #f0f8ff; border: 2px solid blue; margin: 5px;"
     data-role="admin">

    

User

id="editButton" class="btn-vip" onclick="VIP()"> VIP id="newTag"> New tag
Enter fullscreen mode Exit fullscreen mode

Attribute Morphing

Attributes are synchronized between the target and source.

For example:

data-user="123"
Enter fullscreen mode Exit fullscreen mode

becomes:

data-user="456"
Enter fullscreen mode Exit fullscreen mode

The new attribute:

data-role="admin"
Enter fullscreen mode Exit fullscreen mode

is added.

The class:

class="card"
Enter fullscreen mode Exit fullscreen mode

becomes:

class="card premium"
Enter fullscreen mode Exit fullscreen mode

And the style is updated according to the source.

Attributes that exist in the target but no longer exist in the source are removed.

Therefore Morphing effectively synchronizes the attribute set:

Target attributes
        ↓
     Compare
        ↓
Source attributes
        ↓
Add / Update / Remove
Enter fullscreen mode Exit fullscreen mode

Child Element Morphing

Morphing also operates recursively on child Nodes.

In the example, this element:

 id="userEmail">
    Email: user@example.com

Enter fullscreen mode Exit fullscreen mode

does not exist in the new structure.

Therefore it is removed.

Likewise:

 id="oldTag">
    Old tag

Enter fullscreen mode Exit fullscreen mode

is removed.

At the same time:

 id="newTag">
    New tag

Enter fullscreen mode Exit fullscreen mode

is added.

The existing:

User

Enter fullscreen mode Exit fullscreen mode

is retained and synchronized.


Element Matching

One of the important parts of Morphing is determining whether a new element corresponds to an existing element.

WebForms Core uses identifiers such as:

id="editButton"
Enter fullscreen mode Exit fullscreen mode

and:

cb-data-id="..."
Enter fullscreen mode Exit fullscreen mode

for this purpose.

For example, both the old and new structures contain:

 id="editButton">
Enter fullscreen mode Exit fullscreen mode

Therefore Morphing can recognize that they represent the same DOM element.

The existing Node can consequently be retained while its attributes and contents are updated.

This is different from simply deleting the old button and creating a completely new button.


Event Listeners

DOM cloning has an important limitation:

cloneNode(true)
Enter fullscreen mode Exit fullscreen mode

does not copy event listeners registered through addEventListener.

WebForms Core Morphing therefore integrates with the WebForms Core event registry.

When Morphing needs to create a new Node, registered event listeners can be transferred to the new Node.

This is particularly important for applications that dynamically update their interface while maintaining interactive behavior.

The principle is:

Existing Node
      ↓
Keep Node
      ↓
Keep its event listeners

New Node
      ↓
Create Node
      ↓
Transfer registered listeners
Enter fullscreen mode Exit fullscreen mode

This allows Morphing to modify the DOM without unnecessarily destroying its interactive state.


Why use