So commands are small classes that do things in Robotlegs.
You might be adding a view to your application, changing values in your model, calling a method on a service or even all three.
Let's go ahead and make one
public class ExampleCommand : ICommand
{
public void Execute()
{
Console.WriteLine("Executed");
}
}
That's probably even too basic for a command. Again, like a lot of classes in Robotlegs. The command class doesn't have to implement ICommand. It just needs an execute method.
With the IEventCommandMap we can map events that when dispatched onto the global event dispatcher will trigger a command. This is usually done (but not limited to) in an IConfig in the setup phase.
eventCommandMap.Map(EventClass.Type.ACTION_1).ToCommand<ExampleCommand>();
You can also change the Execute method used for the command in the mapping phase
eventCommandMap.Map(EventClass.Type.ACTION_1).ToCommand<ExampleCommand>().WithExecuteMethod("CustomMethodName");
And you can also use the WithGuards to prevent the command being invoked under certain conditions and add WithHooks to add extra functionality just before the command is executed.
eventCommandMap.Map(EventClass.Type.ACTION_1).ToCommand<ExampleCommand>().WithGuards<ExampleGuard>().WithHooks<ExampleHook>();
Tip: Read the Guards and Hooks documentation for more information
So with an event mapped to a command. All you have to do to dispatch the event with the correct type on the global event dispatcher and it will be called.
dispatcher.Dispatch(new EventClass(EventClass.Type.ACTION_1));
It's good to know that you can also invoke a command with the DirectCommandMap or if you don't like events with the Signals Extension extension instead.
So like the View when creating the mediator. An injection is made for the event that has invoked the event. So you can pass more data into the command.
Here is an example of the command injecting the event command and using that to populate a model.
public class ExampleCommand : ICommand
{
[Inject]
public EventClass evt;
[Inject]
public ExampleModel model;
public void Execute()
{
model.SetData(evt.customData);
}
}
You can also downcast your event type to IEvent in your command if you want to tie more data event types to the same command.
Say I have EventClassA
and EventClassB
which both extend Event
(which implements IEvent
).
If I map the event type down to IEvent in the mapping.
eventCommandMap.Map<IEvent>(EventClassA.Type.ACTION_1).ToCommand<MultipleEventCommand>();
eventCommandMap.Map<IEvent>(EventClassB.Type.ACTION_3).ToCommand<MultipleEventCommand>();
And dispatch the regular events:
dispatcher.Dispatch(new EventClassA(EventClassA.Type.ACTION_1));
dispatcher.Dispatch(new EventClassB(EventClassB.Type.ACTION_3));
Then I can get both data types from within the command:
public class MultipleEventCommand : ICommand
{
[Inject]
public IEvent evt;
public void Execute()
{
if (evt is EventClassA)
{
// Get data from A
}
else if (evt is EventClassB)
{
// Get data from B
}
}
}
It's possible to make a command live longer than the execute function. This is useful if you want to do so something asynchronous. But before you do, make sure you don't want to wrap this functionality into a service.
You'll need to detain your command or else your command may get picked up by the garbage collector.
public class DetainedCommand()
{
[Inject]
public IEventDispatcher dispatcher;
[Inject]
public ExampleService service;
[Inject]
public IContext context;
public void Execute()
{
context.Detain(this);
service.OnSomethingComplete += HandleDoneSuccess;
service.OnSomethingFailed += HandleDoneFail;
service.DoSomethingForLater();
}
public void HandleDoneSuccess()
{
dispatcher.Dispatch(new ExampleEvent(ExampleEvent.Type.ACTION_1));
context.Release(this);
}
public void HandleDoneFail()
{
Console.WriteLine("Failed");
context.Release(this);
}
}
The context exposes a small class in called Pin which stores and releases your object in a dictionary where it cannot not be garbage collected.
If you are using event commands, I would try not to call a command directly. However it is possible and is exposed in the DirectCommandMap.