Injector is missing a mapping to handle injection into property
Hai :)
I'm having some trouble with my Injectors;
* I have a mediator that mediates a Starling view. * From this
mediator, I'd like to dispatch an Event with is mapped to a
Command.
It works fine if I dispatch the event from the mediators initialize function. But throws an error if I try to dispatch it from a function/eventlisterner in the same class.
My Error message is as follows: [Fault] exception, information=Error: Injector is missing a mapping to handle injection into property "_menuView" of object "[object StartGameCommand]" with type "commands::StartGameCommand". Target dependency: "views::MenuView|"
and here are my mediator, event and command:
` public class MenuViewMediator extends Mediator{
override public function initialize():void {
_view.addEventListener(TouchEvent.TOUCH, menuViewTouch);
// this one works
//dispatch(new StartGameEvent(StartGameEvent.START_GAME));
}
public function menuViewTouch(touch:TouchEvent):void {
var touchEnd:Touch = touch.getTouch(_view, TouchPhase.ENDED);
if (touchEnd) {
var localPos:Point; = touchEnd.getLocation(_view);
// btn Press Logic
if (_view.playButton.mouseOver(localPos)){
// this does not
//dispatch(new StartGameEvent(StartGameEvent.START_GAME));
}
}
}
}
public class StartGameCommand extends Command {
[Inject]
public var _menuView:MenuView;
public override function execute():void {
_menuView.testFunction();
}
}
public class StartGameEvent extends Event{
public static const START_GAME:String = "startGame";
public function StartGameEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false){
super(type, bubbles, cancelable);
}
public override function clone():Event {
return new StartGameEvent(type, bubbles, cancelable);
}
} `
Thanks in advance!
Mike
Comments are currently closed for this discussion. You can start a new one.
Keyboard shortcuts
Generic
? | Show this help |
---|---|
ESC | Blurs the current field |
Comment Form
r | Focus the comment reply box |
---|---|
^ + ↩ | Submit the comment |
You can use Command ⌘
instead of Control ^
on Mac
Support Staff 1 Posted by Ondina D.F. on 19 Sep, 2013 10:59 AM
Hello Mike,
As the error says, the Injector is missing a mapping for the view.
Take a look at the MediatorFactory [https://github.com/robotlegs/robotlegs-framework/blob/master/src/ro...]
A Mediator is created like this:
See the unmapTypeForFilterBinding(mapping.matcher, type, item) right after Mediator's creation on line 73? The Injector is un-mapping the view:
So, the first dispatch runs inside the initialize() method of the Mediator, when the Injector is still having a mapping for the view, thus it can inject it into the command.
After Mediator's creation, the view gets unmapped, therefore the injector will miss a rule for injection, when it tries to inject it into the command triggered by the second dispatch.
A solution to this could be to remap the view like this inside your menuViewTouch():
But, I don't think it's a good idea to inject views into commands. Is this really necessary? Can you explain why you need to do so? If I knew more about your use case, we could discuss better approaches to this.
Also, I wouldn't listen for Touch events from the view inside a mediator. The logic you have in your menuViewTouch() belongs to the view. Place this method in the view and let it dispatch a StartGameEvent.START_GAME. The mediator could add a listener like this:
The event will be re-dispatched.
or like this:
if you need to do something before re-dispatching it inside a handler method.
The advantage of using addViewListener instead of _view.addEventListener(MouseEvent.CLICK, menuViewTouch);
is that when the mediator gets destroyed, the listeners added through addViewListener or addContextListener will be automatically removed.
Otherwise you'd have to remove the listeners manually in the overridden destroy() method of your mediator:
_view.removeEventListener(MouseEvent.CLICK, menuViewTouch);
Of course, that's only important if you want your views and mediators to get garbage collected, when the views leave the stage.
Another advantage of listening to custom events dispatched from views is that you can use the same mediator for different views - mobile or desktop, without having to care if the event is a Mouse or a Touch event.
Hope this helps.
Ondina
2 Posted by Mike F. on 21 Sep, 2013 03:31 PM
"The Injector is un-mapping the view" this part confused me, since I have other mediators mapped to (starling views) which have no problem with injectors. I map them exactly the same way. An example of this is my GameView and GameViewMediator.
"Can you explain why you need to do so? If I knew more about your use case, we could discuss better approaches to this." Okay, here goes:
I have a MenuView + mediator, a GameView + mediator and a StarlingMain with a mediator. The point of the StarlingMain (sprite) class is to be the parent container for the game and menu view, and to handle the transition.
When I click on the 'Play' button in the MenuView, I would like to dispatch a StartGameEvent, which is mapped to a StartGameCommand. The StartGameCommand has the StarlingMain injected, and calls for the screenSwap/Game to start.
Your point with the mediator handling the input is totally valid. I'll take move that this eve or tomorrow.
Support Staff 3 Posted by Ondina D.F. on 22 Sep, 2013 09:40 AM
Hey Mike,
If the StarlingMainView is mediated, you don't need to inject it into a command as well. When a StartGameEvent is dispatched from your MenuView, MenuViewMediator re-dispatches it, and StarlingMainMediator, who is listening to it, accesses an API on its view in the handler of the event, and StarlingMainView performs the screenSwap - which is view logic. That's the role of mediators, to establish the communication between Views and other parts of the application. That means, views can communicate with each other through their mediators.
If the "screenSwap", which is changing the view's state, has an impact on the application's state somehow, then a command could control this, i.e. set a property on a model, say GameModel. You'd trigger that command by dispatching an event after StarlingMainMediator let its view do the screenSwap.
So, the flow could be : MenuView dispatches StartGameEvent.START_GAME->MenuMediator redispatches StartGameEvent-> StarlingMainMediator listens-> StarlingMainView does the screenSwap-> StarlingMainMediator dispatches StartGameEvent.GAME_STARTED->a command accesses GameModel.gameStarted = true or something (it's just an example)
Views take care of the view logic. Mediators listen for Views' events and re-dispatch them to other Mediators or to trigger Commands and they also listen for application events, dispatched by Commands, Services, Models, or other Mediators, passing data on to their Views or letting them do something . Commands are usually meant to access Models and Services.
So, the idea is to let Views handle their own visual state (like the Flex view states, transitions, show/hide, positioning, etc) internally, and let Models handle application states (the state of the data). Mediators can inform their Views about changes in the application’s state, and if need be, the Views can change their own visual state accordingly. If user interactions with the View are affecting its state, the best place to handle this is the View itself, and then let the application know about what just happened, via events. The View should be pretty self-sufficient and reusable.
Here, just a reminder of the MVCS-Actors' roles:
=> Services are the intermediaries between an application and the outside world; Mediators are intermediaries between the application and the user interface.
Even though I'm sure you know that already, I thought it wouldn't hurt to hear it again.
I apologize if it sounded too didactic!! :)
I wanted to make it clear, that it doesn't make much sense to inject views into commands, if they are already mediated, and in general, manipulating views in commands should be avoided, if possible.
You mean, you inject those views into their mediators and into a command as well??
Are you using the SignalCommandMap? Maybe you are passing the views to the commands via signals??
If you mean that the views are successfully injected into mediators, you're right, that's how it should work. But, if you'd try to inject a view anywhere else, after its mediator has been created, you'd run into the same issue as exposed in the previous post.
Ondina
4 Posted by Mike F. on 22 Sep, 2013 02:40 PM
"I apologize if it sounded too didactic!! :)"
Not at all, I'm super grateful for the help.. and kinda sorry for not entirely getting it.
Okay, so your suggestion would be that the MenuView dispatches an Event, the MenuViewMediator redispatchesit, and the MainViewMediator listens/reacts to that?
*I hope I understood you correctly.
I've tried to implemented it, but my Events do are not heard. (The Event I dispatch in the view, does not trigger the listener in the mediator. And the Event the menuMediator sends is also not heard by the mainMediator).
When I tried to use "addViewListener" the result was even worse. That crashed on compile
*[Fault] exception, information=TypeError: Error #1034: Type Coercion failed: cannot convert views::MenuView@64cb191 to flash.events.IEventDispatcher.*
I think that the MenuViewMediator is having trouble with the MenuView's Event because the menu dispatches a Starling, and not an native Flash Event. I'm lost as to why the Mediators cant receive each others Events, and also why "addViewListener" dose not work.
I'll continue looking for a solution. Here's the code if anyone has a minute to spare.
`
public class MenuView extends Sprite{
public function init():void {
initGraphics();
addEventListener(TouchEvent.TOUCH, menuViewTouch);
}
private function btnPressed():void {
dispatchEvent(new PlayButtonClicked(PlayButtonClicked.PLAY_CLICKED));
}
}
`
`
public class MenuViewMediator extends Mediator{
[Inject]
public var _menuView:MenuView;
public function MenuViewMediator(){}
override public function initialize():void {
_menuView.init();
eventDispatcher.addEventListener(PlayButtonClicked.PLAY_CLICKED, onStartGame);
}
private function onStartGame(event:starling.events.Event):void {
eventDispatcher.dispatchEvent(new StartGameEvent(StartGameEvent.START_GAME));
}
}
}
`
`
public class MainViewMediator extends Mediator{
[Inject]
public var _mainView:StarlingMain;
public function MainViewMediator(){}
override public function initialize():void {
_mainView.init();
eventDispatcher.addEventListener(StartGameEvent.START_GAME, onStartGame);
}
public function onStartGame(event:StartGameEvent):void {
_mainView.changeViews(1);
}
}
`
Support Staff 5 Posted by Ondina D.F. on 22 Sep, 2013 05:58 PM
I've downloaded your app and I'll take a look at it, but since it's already getting late here, I'll let you know about my findings tomorrow morning. Alright?
6 Posted by Mike F. on 22 Sep, 2013 06:14 PM
I appreciate the help, but only do it if you have free time and would otherwise be bored.. I have a bad conscience already, for the long and super detailed help..
Also, my countrymen just voted terribly and I'm in no mood to program tonight.. And tomorrow I'm going to the Intel Innovation Roadshow, so I wont be able to continue until tomorrow evening anyhow (:
Support Staff 7 Posted by Ondina D.F. on 23 Sep, 2013 01:44 PM
Hey Mike,
I really appreciate your concern, but don't feel bad :)
I'm not familiar with Starling, nor do I know enough about the inner working of the Starling extensions for robotlegs, but you're right, PlayButtonClicked is a Starling Event and MenuView is a Starling Sprite (which extends a Starling DisplayObjectContainer, which extends a Starling DisplayObject, which in turn extends a Starling EventDispatcher), which lets you dispatch only Starling Events. So, yes, it's tricky.
The addViewListener is a contraction of
eventMap.mapListener(IEventDispatcher(_viewComponent), eventString, listener, eventClass);
Obviously the casting of a Starling Sprite to an IEventDispatcher goes wrong and the Starling extension doesn't handle it - it has no addViewListener for a Mediator that's extending a StarlingMediator.
I see only 3 solutions to this, due to my limited knowledge of the Starling robotlegs extension:
Inside your view, have a getter and setter for sharedDispatcher and then dispatch the event like this:
sharedDispatcher.dispatchEvent(new GameStatusEvent (GameStatusEvent. START_GAME_REQUESTED));
The problem with this approach is that you need 2 different event types, one dispatched from the view and the other from MenuMediator to the MainMediator , unless you remove the event listener
eventDispatcher.removeEventListener(GameStatusEvent.START_GAME, onStartGameRequested);
before dispatching it to MainMediator. But, that's only possible if MenuMediator doesn't need to listen to GameStatusEvent.START_GAME ever again.
The other problem is that if the sharedDispatcher is dispatching and also listening for the same event type(START_GAME) , you don't actually need MenuMediator to add a listener for it. MainMediator would hear the event anyway. Surpassing the MenuMediator like this, is not really a good idea, that's why I used 2 event types
use Signals instead of events, like in the SARS example. Like with events, you'd need 2 Signals, one dispatched from the view and one from the mediator
listen directly to the view, which is dispatching a Starling event _menuView.addEventListener(PlayButtonClicked.PLAY_CLICKED, ononButtonClicked);
But that's exactly what I told you not to do, right?;)
At least, listening to a custom event is better than listening to Mouse or Touch events from views. So, if you decide to use something like this, don't forget to remove the event listeners (or signals) when/if the view gets removed from stage!
Perhaps there are other, much simpler, solutions to this problem, but I can't see them, probably because of my limited experience with Starling&Co. I should dedicate some time to studying it, since there are many users asking questions related to this obviously trendy utility.
The owner of those extensions (there are 3 or 4) should probably make some changes to let them handle regular events and add an addViewListener to the StarlingMediator....
Long reply...but not because I was bored;) I'm not sure how much will it help you, though.
Ondina
Support Staff 8 Posted by Ondina D.F. on 23 Sep, 2013 04:25 PM
See:
https://github.com/s9tpepper/robotlegs-starling-plugin/blob/master/...
https://github.com/s9tpepper/robotlegs-starling-plugin/blob/master/...
This might be a good solution, if only it would be ported to rl2.....
9 Posted by Mike F. on 24 Sep, 2013 11:41 AM
Oki! I just wanted to say it works, and post my solution for anyone who stumbles onto this thread.
I listen for the ButtonPress(starlingEvent) in the view mediator and then dispatch a GameStart (flash Event). I'm aware this is not perfect, but it does what I want for the moment.
If you chose this option, it's important to used "Object" as the listener function parameter (and not a native Flash or Starling Event). Thanks again Ondina, I owe you a coffee!
`public class MenuViewMediator extends Mediator{
[Inject]
public var _menuView:MenuView;
public function MenuViewMediator(){}
override public function initialize():void {
_menuView.init();
// listen for the Starling Event, in the menu
_menuView.addEventListener(PlayButtonClicked.PLAY_CLICKED, onPlayButton);
}
public function onPlayButton(e:Object): void {
//dispatch a flash Event
eventDispatcher.dispatchEvent(new StartGameEvent(StartGameEvent.START_GAME));
}
}`
Support Staff 10 Posted by Ondina D.F. on 24 Sep, 2013 03:31 PM
No need to use 'Object'. This works well:
Thanks, but I think my tips are worth 2 coffees ;)
And here another suggestion (worth a cake?):
Try to find better names for your custom events.
PlayButtonClicked is not a very good name for the event class, especially if the event type is also containing the same information, PLAY_CLICKED.
This is also redundant: StartGameEvent.START_GAME
It's not easy at all to find good names for a custom event class and its types.
Say you had a UserEvent with the following types:
LOGIN_REQUESTED
LOGGED_IN
LOGGED_OUT
In my mind, the event class names are either describing the 'subject' (a class/object), or some general interaction's type / area of functionality, whilst the event type gets into details about the different actions and/or states of the subject. The event types inform the application about what just happened or needs to happen, and are mostly 'predicates'.
TouchEvent - Touch is the type of interaction, and TOUCH_BEGIN, TOUCH_END are the concrete actions /states
VideoEvent - Video is the subject; CLOSE, COMPLETE, PAUSED_STATE_ENTERED -the actions
You got the idea...
Now, please dispatch an event to let me know if I can mark this discussion as resolved.
Ondina
11 Posted by Mike F. on 25 Sep, 2013 02:23 PM
> No need to use 'Object'. This works well:
Duh! yeah.. no clue why I thought the compiler would get confused.
Not sure yet about the cake, but two coffees at least. Available the next time you pass though Berlin.
### dispatchEvent(new ScholarEvent(ScholarEvent.LESSON_LEARNED));
Yup, the discussion is resolved.
Thanks again for your time and effort!
Support Staff 12 Posted by Ondina D.F. on 25 Sep, 2013 03:36 PM
Ondina D.F. closed this discussion on 25 Sep, 2013 03:36 PM.
Shaun Smith re-opened this discussion on 25 Sep, 2013 03:46 PM
Support Staff 13 Posted by Shaun Smith on 25 Sep, 2013 03:46 PM
There is a bug in
shakeHeadInDisappointment
(infinite recursion). Here's a potential fix:Shaun Smith closed this discussion on 25 Sep, 2013 03:46 PM.
Ondina D.F. re-opened this discussion on 25 Sep, 2013 04:10 PM
Support Staff 14 Posted by Ondina D.F. on 25 Sep, 2013 04:10 PM
Haha!
It was intentional.
Thanks for interrupting my endless headshaking, though :)
Ondina D.F. closed this discussion on 25 Sep, 2013 04:10 PM.