The CakePHP 3.0 documentation includes an example of how to create an event using a model as an example. I've tried and tried and it's just not translating for me. Does anyone have a CakePHP 3.x example of using a custom event where a controller sets a variable in the controller triggering the event?
Let's say we have an admin dashboard that you want to inject some code into using events so that you can decouple your plugins and not hard code dashboard functionality for particular plugins into the core admin dashboard.
Create the firing of the event.
In APP/Controller/DashboardController
public function index()
{
// Once this gets to the function triggered by this event, the "$this" in the parameters will be $event->subject(). Mentioned again below.
$event = new Event('Controller.Dashboard.beforeDashboardIndex', $this)
$this->eventManager()->dispatch($event);
// your other index() code...
}
Now create a listener that waits for that event to be triggered
A good place for this might be PluginName/src/Controller/Event/DashboardListener.php
namespace Plugin\Controller\Event;
use Cake\Event\EventListenerInterface;
class DashboardListener implements EventListenerInterface {
public function implementedEvents() {
return array(
'Controller.Dashboard.beforeDashboardIndex' => 'myCustomMethod',
);
}
public function myCustomMethod($event) {
// $event->subject() = DashboardController();
$event->subject()->set('dashboardAddon', 'me me me');
}
}
Finally turn the listener on. (ex. at the bottom of APP/config/bootstrap.php)
Note, this listener initialization can be anywhere that fires before DashboardController::index
// Attach event listeners
use Cake\Event\EventManager;
use PluginName\Controller\Event\DashboardListener;
$myPluginListener = new DashboardListener();
EventManager::instance()->on($myPluginListener);
Related
I often have the requirement to dispatch a flash.events.Event with soem custom String text, like:
protected function mouseClicked(event:Event) {
//here I'd want to notify anyone interested in the button click,
//and also transfer the name of the button (or whatever) that was clicked - assume some dynamic value
dispatchEvent(new Event("myMouseEvent"), button.name));
}
Of course the above event is invalid. But is there any event that can be used for that type of events? Maybe the TextEvent, but I don't know if I'd be misusing it here.
To include additional data with your events, create a custom event class by extending Event (or any sub-class of Event) and adding your own properties. For example:
class NameEvent extends Event {
public static const NAME_CLICK:String = "nameClick";
public var name:String;
public function NameEvent(type:String, name:String) {
this.name = name;
super(type);
}
}
dispatchEvent(new NameEvent(NameEvent.NAME_CLICK, button.name));
Note that your event type strings ("nameClick" in this example) should be globally unique, otherwise listeners could get them confused with other event types. For example "click" is already expected to be a MouseEvent. I often add prefixes to my custom event types, for example "NameEvent::click".
Another option that does not require creating a custom event is to rely on the expected target to get additional data. For example:
// dispatch a custom event from a Button
dispatchEvent(new Event("myClick"));
// handler for "myClick" events on the button
function myClicked(e:Event):void {
var button:Button = e.target as Button;
trace(button.name);
}
This is not as flexible and also more fragile than using a custom event class, but sometimes a quick easy solution.
Behold this example:
addEventListener("myEventType", myFunction("argument"));
function myFunction(args:String):Function {
return function(evt:Event):void {
trace(evt.currentTarget, "has", args);
};
}
dispatchEvent(new Event("myEventType", true));
It works.
Can I do something similar, but passing "argument" through dispatchEvent()?
It'd be very handy in a situation where dispatchEvent() is in a wholly separated class from addEventListener() and myFunction().
I'll be needing this a lot, so I want to do it without creating a custom event class for every situation.
You can use native flash.events.DataEvent for passing String parameter or create custom DataEvent with data:* property in all situations where you need to pass parameters to event handler.
If you want to customize the behavior of event listener in the place of adding event listener you can create "listener" object for holding this custom parameters (but I think this technique is more complicated than custom events):
addEventListener("myEventType", new EventListener("param1").onEvent);, whereEventListener is the class like this:
public class EventListener
{
private var params:*;
public function EventListener(params:*)
{
this.params = params;
}
public function onEvent(event:Event):void
{
trace("onEvent, params = ", params);
}
}
You could take a look at Signals (https://github.com/robertpenner/as3-signals). They are an alternative to Events and you can send whatever extra params you want with a Signal.
I have a mediator created in Robotlegs, and its related view would dispatch two different kinds of events. When the mediator captures the event, it would just simply dispatch the event. The problem I ran into was that the first event is re-dispatched flawlessly, but the 2nd event was not dispatched.
However, if I manually assign a different handler to the 2nd event, the event is properly captured.
Below is the relevant code:
public class MyMediator extends Mediator
{
[Inject]
public var view:MyView;
public override function onRegister():void
{
super.onRegister();
addViewListener( SomeEventTypeA.COOL_EVENT, dispatch, SomeEventTypeA ); // This event is dispatched correctly
addViewListener( SomeEventTypeB.STUCK, dispatch, SomeEventTypeB ); // This one is not correctly dispatched
//A twist, if I uncomment the following code, the event is captured by its handler
//addViewListener( SomeEventTypeB.STUCK, view_stuck, SomeEventTypeB );
}
private function view_stuck( event:SomeEventTypeB ):void
{
//ah ha, this is called correctly if the above relevant line is uncommented
}
}
Found the cause:
The event needs to have a proper clone method in order to be re-dispatched correctly. See related link:
http://knowledge.robotlegs.org/kb/application-architecture/why-doesnt-my-event-trigger-the-command-it-is-mapped-to
I'm having difficulty with program flow control when XML loads from an external source. I'm using Flash Builder 4.6
What should happen: use a loader. Listen for when it is loaded. When it fires the COMPLETE event, proceed.
What is happening: use a loader. Listen for when it is loaded. But before the COMPLETE event fires, program control returns to the calling class. So meanwhile, the program keeps going and throws an error because it is trying to access data which is null.
Three classes are involved:
AdvocacyWiz.mxml - the base MXML file for this AIR mobile app
Model.as - the model class
myXMLLoader.as - class in which the loader loads data, then
dispatches event when it's done.
I am calling a function setUpModel() in AdvocacyWiz.mxml once AdvocacyWiz.mxml is added to the stage. The setUpModel function in that class just looks like this:
Model.Instance.initialize(); //model is a singleton
The initialize function call invokes a loader in my XMLLoader.as class. That's where the event listener is left listening for the Complete event, which happens too late to prevent a null error from occurring.
EDIT: Here's the code --
In AdvocacyWiz.mxml (this fires first):
protected function addedToStageHandler(event:Event):void
{
setUpModel();
stage.scaleMode = StageScaleMode.NO_SCALE;
//... additional layout functions
private function setUpModel():void {
Model.Instance.initialize();
}
In Model (this fires second):
public function initialize():void {
addEventListeners(); //this includes listener for StoriesXMLLoader.STORY_LOADED event which will be dispatched from the loader class.
useExternalXML();
...
}
private function useExternalXML():void
{
myLoader.getStory("1140");
}
In the Loader class:
public function getStory(storyId:String):void {
var url:String = "http://mysite.com/whatever.xml";
myLoader.dataFormat = URLLoaderDataFormat.TEXT;
myLoader.addEventListener(Event.COMPLETE, storyXMLLoaded);
myLoader.load(new URLRequest(url));
}
private function storyXMLLoaded(e:Event):void {
storyXML = new XML(e.target.data);
dispatchEvent(new Event(StoriesXMLLoader.STORY_LOADED));
}
What I want to do is call setUpModel() but not have flow returned to the mxml class until the model actually has data.
Thanks.
Well, you haven't provided any code, which would help considerably, but here's a few pointers.
What should happen: use a loader. Listen for when it is loaded. When
it fires the COMPLETE event, proceed. What is happening: use a loader.
Listen for when it is loaded. But before the COMPLETE event fires,
program control returns to the calling class. So meanwhile, the
program keeps going and throws an error because it is trying to access
data which is null.
A lot of Flex is asynchronous. What this means is that when you try to load from the external source, program control immediately returns, as you noted.
In order to have the kind of program flow you want, you need to have the function that calls load() end after that call. Then, when the COMPLETE event listener fires, you can execute the remainder of the code that causes the null exception.
I am working on a app using Adobe flash Builder which should pop up a Alert window once a particular event has been triggered.
Another event needs to be called when the Alert box is closed. But I do not see the Alert class in mx.controls library. It seems like the class (which existed in AS2) has been removed from AS3. Is there any other way to accomplish the same functionality?
Thanks,
Pritesh
Yo need to define closeHandler for your Alert control. Checkout the ActionScript 3.0 Reference api from here http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/controls/Alert.html#show()
Use ExternalInterface.
import flash.external.ExternalInterface;
// tell flash what javascript function to listen for, and what flash function to call in response
ExternalInterace.addCallback('onAlertWindowClosed', onPopupClosed);
function openPopUp():void
{
// this conditional prevents errors when running local (yes, this needs uploaded to work)
if(ExternalInterface.available)
{
// this calls a javascript function in your html
ExternalInterface.call('myJavascriptAlertFuntion');
}
}
// this function is called from the javascript callback **onAlertWindowClosed**
function onPopupClosed():void
{
// do something when your popup closes
}
and in the html:
<script type="text/javscript>
// this chunk gets the flash object so you can call its methods
function getFlashMovieObject(movieName)
{
if (window.document[movieName])
{
return window.document[movieName];
}
if (navigator.appName.indexOf("Microsoft Internet") == -1)
{
if (document.embeds && document.embeds[movieName])
return document.embeds[movieName];
}
else
{
return document.getElementById(movieName);
}
}
// function that is called from flash
function myJavascriptAlertFuntion()
{
alert("Hey! Yeah you there!");
}
// call this when you want to tell flash you are closing your popup
function tellFlashMyPopupWindowClosed()
{
// **flashContainer** should be replaced by the name parameter of your flash embed object
var flashMovie = getFlashMovieObject("flashContainer");
flashMovie.onAlertWindowClosed();
}
</script>
To have a popup alert in a Mobile project using MXML and AS3, you need to create a component based off of the SkinnablePopUpContainer from the Sparks components. (Since a simple alert has been conveniently uncluded.)
I learned alot reading up on the SkinnablePopUpContainer in the docs here:
The Spark SkinnablePopUpContainer container
To sum it up, I've created a component in MXML with SkinnablePopUpContainer as the base class. In the View that I want to have the popup to be added to, I create a new instance of the class in Actionscript. I listen to the custom events the buttons in the component will be firing on user response. To show the new popup component, simply call the static method open();. The open() method expects a parent container, and wether or not the popup should be Modal. Modal means that nothing under the component can receive user input.
var alert:SkinnablePopUpContainer = new SkinnablePopUpContainer;
alert.addEventListener( "OK", onOK );
alert.open( this, true );
function onOK(e:Event):void{ trace("User said OK") };
I'll put up some example files later when I can.