fast icon indicating copy to clipboard operation
fast copied to clipboard

feat(fast-html): allow granular observerMap configuration via TemplateElement options

Open radium-v opened this issue 3 months ago • 0 comments

🙋 Feature Request

The ObserverMap settings for the TemplateElement options currently only accepts "all", which forces all bindings present in the template to be observed. This currently doesn't account for attributes, and it doesn't account for decorated @observable properties defined in the class.

🤔 Expected Behavior

Developers should have fine-grained control over which properties are observed by the ObserverMap. This includes:

  1. Selective property observation: Ability to specify which root properties from template bindings should be observed
  2. Exclusion patterns: Ability to exclude specific properties (e.g., internal/cached properties)
  3. Attribute observation: Option to observe element attributes in addition to template bindings
  4. Opt-out capability: Ability to disable observation entirely with "none"
  5. Backward compatibility: Existing "all" option continues to work as before

Use Cases

  • Performance optimization: Observe only the properties that need deep reactivity, avoiding unnecessary proxy overhead for static or rarely-changing data
  • Attribute integration: Include element attributes (e.g., @attr decorated properties) in the observation system
  • Selective observation: Observe user-facing data (users, items) while excluding internal state (_cache, _metadata)

😯 Current Behavior

Currently, the only option is "all":

TemplateElement.options({
  "some-element": {
    observerMap: "all",
  }
}).define({ name: "f-template" });

This observes all root properties found in template bindings but provides no way to:

  • Observe only specific properties
  • Exclude certain properties
  • Include attributes
  • Disable observation

💁 Possible Solution

Extend the observerMap option to accept multiple configuration formats:

1. String literal (backward compatible)

TemplateElement.options({
  "some-element": {
    observerMap: "all",  // Observe all template bindings (current behavior)
    // or
    observerMap: "none", // Disable observation
  }
}).define({ name: "f-template" });

2. Array of property names

TemplateElement.options({
  "some-element": {
    observerMap: ["users", "items"], // Observe only these root properties
  }
}).define({ name: "f-template" });

3. Object with granular control

TemplateElement.options({
  "some-element": {
    observerMap: {
      include: ["users", "items"],      // Root properties to observe
      exclude: ["_cache", "_internal"], // Properties to exclude
      attributes: true,                  // Also observe element attributes
      // or
      attributes: ["data-id", "role"],  // Specific attributes to observe
    },
  }
}).define({ name: "f-template" });

Type Definition

export const ObserverMapOption = {
    all: "all",
    none: "none",
} as const;

export interface ObserverMapConfig {
    include?: string[];           // Root properties to observe
    exclude?: string[];           // Root properties to exclude
    attributes?: boolean | string[]; // Observe attributes (all or specific)
}

export type ObserverMapOption =
    | (typeof ObserverMapOption)[keyof typeof ObserverMapOption]  // "all" | "none"
    | string[]                                                     // Array of property names
    | ObserverMapConfig;                                          // Object configuration

Implementation Notes

  • The Schema class already tracks all binding paths via addPath() and can provide root properties via getRootProperties()
  • The ObserverMap operates at the root property level, defining observables on the class prototype
  • Filtering logic would be applied during ObserverMap construction before calling defineProperties()
  • Attribute observation would require integration with the @attr decorator system from @microsoft/fast-element

radium-v avatar Nov 19 '25 05:11 radium-v