delphispring4d

Spring4D: resolve a TFrame with owner in create


I have some Frames registrated like:

GlobalContainer.RegisterType<TfraBasePage, TfraSetting>(IDSettingPage);
GlobalContainer.RegisterType<TfraBasePage, TfraCurrency>(IDCurrencyPage);
GlobalContainer.RegisterType<TfraBasePage, TfraTicketLayOut>(IDTicketLayOutPage);
GlobalContainer.RegisterType<TfraBasePage, TfraSetUp>(IDSetUpPage);

I can call the frame like:

  Result := GlobalContainer.Resolve<TfraBasePage>(aPageName); 

But the constructor 'create' the owner is nil. How can I pass an owner component?

Now I solve this with 'InsertComponent' after the resolve.

var
  oFrame: TfraBasePage;
begin
 oFrame:= GlobalContainer.Resolve<TfraBasePage>('SettingPage'); 
  if assigned(oFrame) then
  begin
    Self.InsertComponent(oFrame);
  end;

Is this also possible with a Factory?

 TfraBasePage = class(TfraBase, IPage)
...

 TfraSetting = class(TfraBasePage)
...

 IPageFactory = interface(IInvokable)
  ['{2E40E3C4-87B7-4313-BE0A-29202B7EB5A1}']
    function Create(aOwner: TComponent): TfraBasePage;
  end;
...

GlobalContainer.RegisterFactory<IPageFactory>;
GlobalContainer.RegisterType<TfraBasePage, TfraSetting>('SettingPage');

...
var
  oFrame: TfraBasePage;
begin
 //Here I can't solve how to get the 'SettingPage' Frame with the factory.
 //I miss something, the ('SettingPage') is on the wrong place.
 oFrame:= GlobalContainer.Resolve<IPageFactory>('SettingPage').Create(Self); 



Solution

  • There are two possible solutions:

    uses
      Classes,
      Spring.Container;
    
    type
      TBaseFrame = class(TComponent) // just for testing a TComponent descendant
      end;
    
      TDemoFrame = class(TBaseFrame)
      end;
    
      TFrameFactory = reference to function(const serviceName: string; owner: TComponent): TBaseFrame;
    
    procedure Main;
    var
      owner: TComponent;
      frame: TBaseFrame;
      factory: TFrameFactory;
    begin
      GlobalContainer.RegisterType<TBaseFrame, TDemoFrame>('myframe');
      GlobalContainer.Build;
    
      owner := TComponent.Create(nil);
      owner.Name := 'owner';
    
      // Possibility 1 - pass args to Resolve -
      // they will be matched via their type when looking for an appropriate ctor
      frame := GlobalContainer.Resolve<TBaseFrame>('myframe', [owner]);
      Writeln(frame.Owner.Name);
    
      // Possibility 2 - register a custom factory function -
      // RegisterFactory does not support passing the serviceName through the factory interface
      GlobalContainer.RegisterInstance<TFrameFactory>(
        function(const serviceName: string; owner: TComponent): TBaseFrame
        begin
          Result := GlobalContainer.Resolve<TBaseFrame>(serviceName, [owner]);
        end);
      GlobalContainer.Build;
    
      // don't forget the parentheses here else the compiler will barf 
      // because return type is something invokable
      factory := GlobalContainer.Resolve<TFrameFactory>(); 
    
      frame := factory('myframe', owner);
      Writeln(frame.Owner.Name);
    
      // yes, memoryleaks - demo code to answer the question
    end;
    
    begin
      Main;
      readln;
    end.