You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@royale.apache.org by ah...@apache.org on 2018/11/23 07:40:31 UTC

[royale-asjs.wiki] branch master updated: Created Getting MX Alert To Run (markdown)

This is an automated email from the ASF dual-hosted git repository.

aharui pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/royale-asjs.wiki.git


The following commit(s) were added to refs/heads/master by this push:
     new 6035099  Created Getting MX Alert To Run (markdown)
6035099 is described below

commit 60350992e4068ea04d1a41fdb83f9b3ffe72d94f
Author: aharui <ah...@apache.org>
AuthorDate: Thu Nov 22 23:40:30 2018 -0800

    Created Getting MX Alert To Run (markdown)
---
 Getting-MX-Alert-To-Run.md | 177 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 177 insertions(+)

diff --git a/Getting-MX-Alert-To-Run.md b/Getting-MX-Alert-To-Run.md
new file mode 100644
index 0000000..5d2304b
--- /dev/null
+++ b/Getting-MX-Alert-To-Run.md
@@ -0,0 +1,177 @@
+The Royale Basic component set has a working Alert component.   It is org.apache.royale.html.Alert.  We will refer to it as "Basic Alert".
+
+To get mx.controls.Alert (we will refer to this as MX Alert) to run, we want to reuse as much of the code from Basic Alert as possible.  While subclassing Basic Alert might seem like the easy way, the problem is that there might be expectations in migrating applications that MX Alert is a UIComponent.  We could probably get away with subclassing for Alert, but the expectation that other MX and Spark controls extend UIComponent is more likely so this article will show how to reuse code fr [...]
+
+Almost all Royale Basic components are comprised of plugins called "beads".  There are a few exceptions for components that thinly wrap an HTMLElement like Button, Checkbox, RadioButton, TextInput, TextArea, Image.  These components have pretty much been emulated.  All remaining components that need to be implemented in the Emulation component set are going to reuse beads from bead-based Basic components.  These Basic components use an MVC pattern.  There is at least a model bead and vie [...]
+
+The component, such as Alert, should have very little code in it.  It's main goal is to pass data to/from the model, and serve as the dispatcher of events from the controller logic.  So, if you look at Basic Alert, it looks something like this:
+```
+package org.apache.royale.html
+{
+	import org.apache.royale.core.IAlertModel;
+	import org.apache.royale.core.IPopUp;
+
+	public class Alert extends org.apache.royale.html.Group implements IPopUp
+	{
+		/**
+		 *  The message to display in the Alert body.
+		 */
+		public function get message():String
+		{
+			return IAlertModel(model).message;
+		}
+		public function set message(value:String):void
+		{
+			IAlertModel(model).message = value;
+		}
+...
+```
+
+Over in the MX Alert, the "message" property was called "text" and has a completely different implementation.  But because we are going to reuse the Basic beads, we want to use this code from Basic Alert., so we will replace the "text" implementation in MX Alert with:
+
+```
+	public class Alert extends mx.controls.Panel implements IPopUp
+	{
+		/**
+		 *  The message to display in the Alert body.
+		 */
+		public function get text():String
+		{
+			return IAlertModel(model).message;
+		}
+		public function set text(value:String):void
+		{
+			IAlertModel(model).message = value;
+		}
+```
+
+The same process copies the "flags" property from Basic Alert to MX Alert as "buttonFlags".  The "title" property has already been handled in the Panel base class.
+
+Next, the Basic component's constructor has:
+
+```
+		public function Alert()
+		{
+			super();
+			typeNames = "Alert";
+		}
+```
+
+Copy the "typeNames" line to the constructor in the MX emulation.
+
+The next step is to assign the same beads used by the Basic Alert to the MX Alert.  In Basic/src/main/resources/defaults.css you will find:
+
+```
+Alert
+{
+	IBeadLayout: ClassReference("org.apache.royale.html.beads.layouts.VerticalFlexLayout");
+	IBeadView:  ClassReference("org.apache.royale.html.beads.AlertView");
+	IBeadController: ClassReference("org.apache.royale.html.beads.controllers.AlertController");
+	IBeadModel: ClassReference("org.apache.royale.html.beads.models.AlertModel");
+
+	background-color: #FFFFFF;
+	border-style: solid;
+	border-color: #000000;
+	border-width: 1px;
+}
+...
+@media -royale-swf
+{
+...	
+	Alert
+	{
+		IBackgroundBead: ClassReference("org.apache.royale.html.beads.SolidBackgroundBead");
+		IBorderBead: ClassReference("org.apache.royale.html.beads.SingleLineBorderBead");
+	}
+...
+```
+
+These selectors get copied into MXRoyale/src/main/resources/defaults.css
+
+And now, it is time to compile MXRoyale.swc and a test app to see if it works.  It probably won't work, but it might.  Don't forget to clean out the bin folder before compiling the test application.  When changing framework classes, the compiler will not overwrite older copies of JS files that come from a SWC, so you must delete them to force the compiler to copy in your new version from the updated MXRoyale.swc.
+
+When the test app ran, the Alert didn't work.  First thing to check is the browser console.  It contained lots of lines, but there was one exception:
+
+`	TypeError: Type Coercion failed  "as"  (Language.js:87)`
+
+Type Coercion errors are very common when reusing beads from Basic because the beads might have expectations about the type of something.  In this case, there is some code like:
+
+`	SomeType(someObject)`
+
+Which results in a call to the "as" method in Language.as.  In this case:
+
+`	PanelModel(model)`
+
+What to do about it depends a bit on the situation.  Some choices are:
+
+1) Change to some Interface instead of a Class
+2) Use (someObject as SomeType) and @royaleignorecoercion SomeType 
+3) Have the model actually extend PanelModel so the coercion passes.
+4) Override so this code doesn't run.
+5) other ideas
+
+In this case, we will choose #2.  There is no real value to checking the type of the model in JS.  This coercion was probably mainly to make the compiler happy.  So, the code change is:
+
+```
+   /**
+     *  @royaleignorecoercion mx.containers.beads.models.PanelModel
+     */
+   ...
+    (model as PanelModel).title
+   ...
+```
+
+This caused that exception to go away and we ended up with a new one:
+
+`   TypeError: null is not an object (evaluating 'panelView.contentArea')`
+
+Debugging into this shows that the base class Panel is expecting the view to be a PanelView and have a contentArea property.  And our AlertView we borrowed from Basic doesn't have one.  We can get rid of the coercion by using @royaleignorecoercion, but what should we do about the contentArea property?  We don't want to add one to the Basic Alert view, that would weigh down the Basic component with "just-in-case" code.  Instead, we'll try adding the contentArea property in a subclass of A [...]
+
+Subclasses typically go in a "beads" folder.  So, we create mx/controls/beads/AlertView.as:
+
+```
+package mx.controls.beads
+{
+    import org.apache.royale.core.UIBase;
+    import org.apache.royale.html.beads.AlertView;
+...
+    public class AlertView extends org.apache.royale.html.beads.AlertView
+    {
+...
+        /**
+         *  @royaleignorecoercion org.apache.royale.core.UIBase
+         */
+        public function get contentArea():UIBase
+        {
+            return _strand as UIBase;
+        }
+```
+
+Basic Alert adds its children to itself and not some contentArea so it should work to return the _strand (a protected property from the base class).  And to use this bead, we have to list it as the view in the defaults.css:
+
+```
+Alert
+{
+	IBeadLayout: ClassReference("org.apache.royale.html.beads.layouts.VerticalFlexLayout");
+	IBeadView:  ClassReference("mx.controls.beads.AlertView");
+	IBeadController: ClassReference("org.apache.royale.html.beads.controllers.AlertController");
+	IBeadModel: ClassReference("org.apache.royale.html.beads.models.AlertModel");
+...
+```
+
+That didn't work either.  We got a stack overflow because the Panel's addElement calls contentArea.addElement, which is the Panel, so it is infinite recursion.  Since folks aren't supposed to add children to an Alert, we can override the Alert's addElement and have it skip around the Panel's addElement.  This can be done by calling a "skip-around"l API in UIComponent.  A "skip-around" API is a "undocumented" API that subclasses can use to access an un-overridden API in a base class when  [...]
+
+```
+    /**
+     * @private
+     * @royaleignorecoercion mx.core.IUIComponent
+     */
+    override public function addElement(c:IChild, dispatchEvent:Boolean = true):void
+    {
+        $uibase_addChild(c as IUIComponent);
+    }
+```
+
+And with that change, Alert worked!  It isn't pretty, but it is functional.  We'll make it prettier later.
+
+Hopefully, this walk-through will give you an idea of what it takes to get an Emulation Component to run.  The problems you may run into getting another component to run may be different and require some thought as to how to get past it, but this should give you an idea of some solutions to some problems.