Uploaded image for project: 'Fluid Infusion'
  1. Fluid Infusion
  2. FLUID-5930

Framework clears injected subcomponents before onDestroy



    • Type: Bug
    • Status: Closed
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: 2.0
    • Component/s: IoC System
    • Labels:


      FLUID-5812 has improved the fluid.clearComponent workflow somewhat, but the current implementation is still capable of "surprising" the user. The general contract should be that at the time of an "onDestroy" event handler, no effects from destruction of the component's tree should yet be visible. However, in the case where a component has subcomponents which have been injected, these are aggressively cleared first - here is the current relevant section from fluid.clearComponent:

      1026:            if (created) {
      1027:                // Clear injected instance of this component from all other paths - historically we didn't bother
      1028:                // to do this since injecting into a shorter scope is an error - but now we have resolveRoot area
      1029:                fluid.each(childShadow.injectedPaths, function (troo, injectedPath) {
      1030:                    var parentPath = fluid.model.getToTailPath(injectedPath);
      1031:                    var otherParent = that.pathToComponent[parentPath];
      1032:                    that.clearComponent(otherParent, fluid.model.getTailPath(injectedPath), child);
      1033:                });
      1034:                fluid.visitComponentChildren(child, function (gchild, gchildname, segs, i) {
      1035:                    var parentPath = that.composeSegments.apply(null, segs.slice(0, i));
      1036:                    that.clearComponent(child, gchildname, null, options, true, parentPath);
      1037:                }, options, that.parseEL(childPath));
      1038:                fluid.doDestroy(child, name, component);
      1039:                fluid.clearDistributions(childShadow);
      1040:                fluid.clearListeners(childShadow);
      1041:                fluid.fireEvent(child, "afterDestroy", [child, name, component]);
      1042:                delete that.idToShadow[child.id];
      1043:            } else {
      1044:                fluid.remove_if(childShadow.injectedPaths, function (troo, path) {
      1045:                    return path === childPath;
      1046:                });

      This implementation has become a bit "belt and braces" in that there are two distinct sites where "injectedPaths" may be cleared - once at line 1029 where other paths at which this (concrete) component has been injected are cleared, and again at line 1044 where non-concrete child paths of a concrete component have their path records cleared out.

      We need to take the block at line 1029 and move it below the "doDestroy" call at line 1038.

      This emerged whilst refactoring the LifecycleManager for GPII-580. We implemented the proto-framework "DynamicComponentIndexer" which looks as follows:

          fluid.defaults("gpii.indexedDynamicComponent", {
              gradeNames: "fluid.component",
              components: {
                  // This reference needs to be overridden by the concrete grade user
                  // TODO: Enhance "notImplemented" scheme to support custom messages when user has not overridden material
                  // which requires to be overridden
                  dynamicIndexTarget: "{fluid.notImplemented}.mustBeOverridden",
              // The path of the collection/member at which the index is to be held
              dynamicIndexTargetPath: "{fluid.notImplemented}.mustBeOverridden",
              // The path in this component at which the key is to be found
              dynamicIndexKeyPath: "{fluid.notImplemented}.mustBeOverridden",
              listeners: {
                  "onCreate.indexedDynamicComponent": "gpii.indexedDynamicComponent.onCreate",
                  "onDestroy.indexedDynamicComponent": "gpii.indexedDynamicComponent.onDestroy"
          gpii.indexedDynamicComponent.onCreate = function (that) {
              var key = fluid.getForComponent(that, that.options.dynamicIndexKeyPath);
              var ourPath = fluid.pathForComponent(that);
              var memberName = ourPath[ourPath.length - 1];
              var index = fluid.get(that.dynamicIndexTarget, that.options.dynamicIndexTargetPath);
              index[key] = memberName;
          gpii.indexedDynamicComponent.onDestroy = function (that) {
              var key = fluid.getForComponent(that, that.options.dynamicIndexKeyPath);
              var index = fluid.get(that.dynamicIndexTarget, that.options.dynamicIndexTargetPath);
              delete index[key];

      In the sample usage, we have

          fluid.defaults("gpii.lifecycleManager.sessionIndexer", {
              gradeNames: "gpii.indexedDynamicComponent",
              components: {
                  dynamicIndexTarget: "{gpii.lifecycleManager}"
              dynamicIndexTargetPath: "sessionIndex",
              dynamicIndexKeyPath: "options.userToken"

      During the destruction of gpii.lifecycleManager, its injected paths are cleared out early - then when we come to destroy the session, we have a failure during onDestroy where index becomes undefined.




            antranig Antranig Basman
            antranig Antranig Basman
            0 Vote for this issue
            2 Start watching this issue