This is part 3.
In Part 1, I explained the reasons as to why we chose to use Parsley in our projects.
In Part 2, I explained what Parsley is and some basic concepts and how they relate to Parsley. If you are very new to Parsley or new to IOC, you are strongly encouraged to read through Part 2.
In Part 4, we will look at a simple Messaging example.
In this article will look at a very basic example to get the idea of how Injection works in Parsley.
Parsley Basic Injection Example
Parsley works with Flex 3, Flex 4, and Flash. I will show examples using Flex 3 projects because they can easily be imported into Flex 4, sorry Flash users. You can still get the idea but I'm going to have to send you back to the docs. Flex 4 users must note that when building a Flex 4 project and when adding non visual components in MXML, they go in between the <fx:Declarations> </fx:Declartions> tags
Download Parsley
If you haven't downloaded Parsley yet, you should. You can download it here. As of this writing the current version of Parsley is 2.3.0. You can download the source code, but we will be working with the compiled SWC files.Setting up Parsley
Parsley's zip download includes a release folder. In that folder you will find five SWC files. You only really need two of those, one for Spicelib (which Parsley uses) and one for Parsley itself. Which ones you use, depends on what tool/framework you are using.
For example if you are using Flex 3, you would use the parsley-flex3-2.3.0.swc and the spicelib-flex-2.3.0.swc; if you are using the Flex 4 framework you would use parsley-flex4-2.3.0.swc and spicelib-flex-2.3.0.swc files.
So, now that you have selected the two files that you need, simply copy them to the "libs" folder of your FlexBuilder's project. That's it. Parsley is ready to be used in that project. If you don't have a "libs" folder already in your project, you can go to the Project Properties and select Flex Build Path on the left, then select the Library Path tab on the right side. Add a swc folder and make sure it is set to Merge into Code as the link type.
Using Parsley
Now that Parsley is ready to go let's look at how you actually use it. If you recall from the previous article, for Parsley to be able to help you, it needs to be told what objects you wish for it to manage. You do that with the <ContextBuilder /> tag. You should include the <ContextBuilder /> in any view that you wish to be the "root" of any context. For our simple example we will include the <ContextBuilder /> tag in the main application file. This basically creates something similar to an application wide singleton. The <ContextBuilder /> tag has several options, but one of the most important is the config option. Config basically tells the ContextBuilder where is the file that has the description of what objects you wish to have Parsley manage. The config file can be either MXML or XML. Having the file in MXML has the benefit of making sure that the classes that you wish Parsley to mange do get linked into the final swf.
So here is an example. In my main application MXML file I would have the following:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="280" height="128" xmlns:views="view.*" xmlns:parsley="http://www.spicefactory.org/parsley" viewSourceURL="srcview/index.html"> <parsley:ContextBuilder config="BasicExampleContextConfig" /> </mx:Application>Now let's look at what a Context config file looks like. This is what my "BasicExampleContextConfig.mxml" looks like:
<?xml version="1.0" encoding="utf-8"?> <!-- This is the file that declares which objects you wish to manage through Parsley. Any object declared within the Object Tag will be allowed to participate in any of Parsley's features. This is a Simple MXML implementation, but this can also be declared in and XML file. --> <mx:Object xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:model="model.*"> <!-- Withing the object tags you declare de objects. In this case I'm just including the basic Model class --> <model:BasicModel /> </mx:Object>
Now if you simply ignore all the comments, it's just and <Object> <BasicModel /> </Object>.
That's it. Parsley will now create an instance of the class BasicModel and manage that instance for you.
Flex 4 users. Remember that non visual elements must go in between <fx:Declarations> </fx:Declartions> tags so, the Context config file would look like this.
<?xml version="1.0" encoding="utf-8"?> <!-- This is the file that declares which objects you wish to manage through Parsley. Any object declared within the Object Tag will be allowed to participate in any of Parsley's features. This is a Simple MXML implementation, but this can also be declared in and XML file. --> <mx:Object xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:model="model.*"> <fx:Declarations> <!-- Withing the object tags you declare de objects. In this case I'm just including the BasicModel class --> <model:BasicModel /> </fx:Declarations> </mx:Object>
You are probably wondering what is in the BasicModel class. Well, nothing fancy. Basic Model can be any class that you want Parsley to manage for you. When we talk about being managed by Parsley it means that it has access to Parsleys features; that includes: messaging, being created and destroyed automatically, and Injection, among other things. So let's look at it.
package model { /********************************************************************* * BasicModel is a very simple class to demonstrate the Shared Model * Concept. It contains a Bindable String and a Bindable Integer * That will be shared by different views. * ******************************************************************/ public class BasicModel { /********************************************************************* * Constructor * ******************************************************************/ public function BasicModel() { } [Bindable] public var sharedString:String; // The string to be shared [Bindable] public var sharedInteger:int; // The integer to be shared } }Notice that the constructor is not taking any parameters. If the constructor was taking parameters, it could still be managed by Parsley and declared in the MXML context config file, but more on that on a different article.
Great, so now we have a class that is being managed by Parsley. So what can we do with it. Well, let's inject it somewhere.
I'll create a small view called "ViewOne", which will be a VBox that will hold 3 elements. The first a CheckBox that will be checked once our object has been injected, the second a TextInput that will be bound to the sharedString of our BasicModel, and finally a NumericStepper that will be bound to our BasicModel's sharedInteger property.
So here is ViewOne:
<?xml version="1.0" encoding="utf-8"?> <!-- This is a simple view that will have our BasicModel Object injected into it. --> <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="128" height="82" xmlns:parsley="http://www.spicefactory.org/parsley"> <!-- Configure is very important. Configure dispatches an event that instructs Parsley that you wish to have this view Managed by Parsley. If you do not include this tag or do not launch the configure event, Injection will not happen in this view. --> <parsley:Configure /> <!-- This CheckBox simple get's a check once the Basic Model has been injected. If no injection has occured, the value will be null --> <mx:CheckBox label="BasicModel Ready" width="100%" enabled="false" selected="{sharedBasicModel != null}"/> <!-- The Text input will access the shared string of our model. I'll use two directional Binding so that it will be updated when it's changed on any other view. Note that Flex 3.x Binding is only one way by default. --> <mx:Binding destination="sharedTextBox.text" source="sharedBasicModel.sharedString" /> <mx:Binding destination="sharedBasicModel.sharedString" source="sharedTextBox.text" /> <mx:TextInput width="100%" id="sharedTextBox"/> <!-- The Numeric stepper will access the shared integer of our BasicModel. I'll use two directional Binding so that it will be updated when it's changed on any other view. Note that Flex 3.x Binding is only one way by default. --> <mx:Binding destination="sharedStepper.value" source="sharedBasicModel.sharedInteger" /> <mx:Binding destination="sharedBasicModel.sharedInteger" source="sharedStepper.value" /> <mx:NumericStepper width="100%" id="sharedStepper" /> <mx:Script> <![CDATA[ import model.BasicModel; /***************************************************************************** * sharedBasicModel is the property accesible within this view that will have * the shared BasicModel injected into it. Parsley figures out what to inject * because of the Class Type. * Parsley doesn't care if it's Bindable or not. I've declared it Bindable * so that I could bind it to some Flex Components * The [Inject] metatag is what instructs parsley inject the shared object. * **************************************************************************/ [Inject] [Bindable] public var sharedBasicModel:BasicModel; ]]> </mx:Script> </mx:VBox>
Towards the bottom in the Script section is where Parsley gets involved. Notice that both the Stepper and the TextBox are bound to a local property called "sharedBasicModel". It's local right, but above the declaration for sharedBasicModel you have two tags, one the [Bindable] so that binding works and the second one is the [Inject] tag. Parsley reads this [Inject] tag and looks through the current context to see if it is managing any class of the type "BasicModel", if it is, then it assigns (injects) it's managed object into this local variable.
VERY IMPORTANT NOTE: For injection to work, the property to be injected must be public, otherwise it can't be assigned from outside this class.
Now, Parsley doesn't do look for the [Inject] or any other tag in every view, it does this only for the views that you tell it to do so. And you tell it with the <Configure /> tag. This tag simply launches a bubling event that Parsley catches and proceeds to manage this view also. Without this, injection will not work. Note: You can instruct Parsley to manage All views automatically without the need to use the <Configure /> tag, but more on that on a later time.
That's it. All you have to do is add this view as child of the view that has the context and it will work.
Notice how there is almost nothing different from how you would do this with manual dependency injection. Parsley doesn't make you extend anything or write a bunch of code to get things working.
And this is what our projects structure looks like if you copied and pasted the code:
Now that we have a view, well let's go back to the Main app file (in my case it's ParsleyBasicExample1.mxml and add two views to see it work.
Here is the "final" version of the main file.
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="280" height="128" xmlns:views="view.*" xmlns:parsley="http://www.spicefactory.org/parsley" viewSourceURL="srcview/index.html"> <!-- This is a very simple application which demonstrates how to set up Parsley's managed Dependency Injection. It uses one shared object named BasicModel that you must instruct Parsley that you wish for it to manage it. This is accomplished with the ContextBuilder tag found below. Parsley will ONLY manage objects that you instruct it to do. In this example there is one class, that has two instances. Each instance will have the shared object injected into them, which means that if you updated the shared object in one view, it will be udated in ALL views that share that same object. --> <mx:VBox width="95%" height="100%" horizontalCenter="0" horizontalAlign="center"> <mx:Label text="Parsley Basic Example 1" fontWeight="bold" fontSize="13"/> <mx:HBox x="0" y="0" width="100%"> <views:ViewOne id="firstInstanceViewOne"> </views:ViewOne> <views:ViewOne id="secondInstanceViewOne"> </views:ViewOne> </mx:HBox> </mx:VBox> <!-- ContextBuilder is how you tell parsley to manage objects that are not Views. If you wish to have Parsley manage a View, you do that through the Configure tag. The Configure Tag is being used in the ViewOne component. In this case I'm instructing Parsley to load the BasicExampleContextConfig file which has the "list" of objects you wish to have managed. In Parsley you can have nested Contextes, I will show that in another example project. --> <parsley:ContextBuilder config="BasicExampleContextConfig" /> </mx:Application>
Nothing really new Parsley related, just added two ViewOnes.
What happens is that any change done in one view is immediately copied on to the other view.
And here is the application running. View source is enabled.
It's a very simple example well that doesn't do much more than simply illustrate some of the basic concepts.
You can also download the source code here.
As I have said before, it's fairly easy to take this application out of Parsley. In the ViewOne all I would need to do is remove the <Configure /> tag. The main application would look more like this:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="280" height="128" xmlns:views="view.*" xmlns:parsley="http://www.spicefactory.org/parsley" viewSourceURL="srcview/index.html"> <!-- This is a very simple application which demonstrates how to set up Parsley's managed Dependency Injection. It uses one shared object named BasicModel that you must instruct Parsley that you wish for it to manage it. This is accomplished with the ContextBuilder tag found below. Parsley will ONLY manage objects that you instruct it to do. In this example there is one class, that has two instances. Each instance will have the shared object injected into them, which means that if you updated the shared object in one view, it will be udated in ALL views that share that same object. --> <mx:VBox width="95%" height="100%" horizontalCenter="0" horizontalAlign="center"> <mx:Label text="Parsley Basic Example 1" fontWeight="bold" fontSize="13"/> <mx:HBox x="0" y="0" width="100%"> <views:ViewOne id="firstInstanceViewOne" sharedBasicModel="{myBasicModel}"> </views:ViewOne> <views:ViewOne id="secondInstanceViewOne" sharedBasicModel="{myBasicModel}"> </views:ViewOne> </mx:HBox> </mx:VBox> <mx:Script> <![CDATA[ import model.BasicModel; var myBasicModel:BasicModel = new BasicModel(); ]]> </mx:Script> <!-- ContextBuilder is how you tell parsley to manage objects that are not Views. If you wish to have Parsley manage a View, you do that through the Configure tag. The Configure Tag is being used in the ViewOne component. In this case I'm instructing Parsley to load the BasicExampleContextConfig file which has the "list" of objects you wish to have managed. In Parsley you can have nested Contextes, I will show that in another example project. --> <!-- Removed for demonstrating a non Parsley implementation <parsley:ContextBuilder config="BasicExampleContextConfig" /> --> </mx:Application>
Notice that I commented out the <ContextBuilder /> tag and had to declare a variable (myBasicModel) that I had to inject manually into both views, but the underlying structure did not change. The point I wanted to illustrate here is that to take advantage of Parsley you don't have to follow a specific patter or structure, Parsley just makes your life easier and it will adapt to any structure or patter you wish to implement.
Well, that's it for this example. In the next example we will take a look at messaging.
Hi, can you please explain how to do this: "Note: You can instruct Parsley to manage All views automatically without the need to use the tag, but more on that on a later time."
ReplyDeleteSure,
ReplyDeleteYou change a little the Context Builder
You would use something like:
<parsley:ContextBuilder>
<parsley:ViewSettings autowireComponents="true"/>
<parsley:FlexConfig type="{MyConfig}"/>
</parsley:ContextBuilder>
You then can specify the views you wish to manage in the context config file, and the view get's configured automatically (without the <Configure /> tag.
Something like: <View type="{MainView}"/>
If you really want ALL views, which I would discourage you to do so, you would override the filter function to accomplish that.
There is more info on this specifically in chapter 8.4 of the docs.
http://www.spicefactory.org/parsley/docs/2.3/manual/
Or straight to the chapter
http://www.spicefactory.org/parsley/docs/2.3/manual/view.php#config_automatic
All working fine there by applying this to my project. I liked this easy first example, demonstrating basic things, but which I'll need to use dozens of time in the future, so better to have it "right". Thanks !
ReplyDeleteGreat to hear that Jahz, glad to be of help. Keep reading on and you will start seeing even more cool things that you can apply to your projects.
ReplyDeleteAh just one additional question: by default, I would have used
ReplyDeleteinstead of the two bindings you're defining.
But if I understand well, this would mean that the binding is only "one way", the TextInput would have been refreshed automatically as expected, but when typing smth into it, this wouldn't have updated the shared model and thus the second view, right ?
Is there no other to define this bi-directionnal binding, inside the metatag for example ?
Anyway, I'll go on with the next cool things ;)
I was meaning:
ReplyDeleteTextInput width="100%" text="{sharedBasicModel.sharedString}"
Jahz,
ReplyDeleteYou are correct. The default text binding is a read option, not write.
In Flex 3.5 that is the "easiest" way to do two-way binding, but not the only way. For example you could set up the two way binding in ActionScript using BindingUtils. Also you could set a one way binding to the text input, and use the Change event to update the value. In the Presentation model example I use that method (http://artinflex.blogspot.com/2010/11/presentation-model-implemented-in.html).
In Flex 4 you can set two-way binding a little easier. You use the @ symbol.
For example:
<TextInput id="sharedTextBox" text="{sharedBasicModel.sharedString}"/>
is one way "read" binding.
<TextInput id="fahrenheit" text="@{sharedBasicModel.sharedString}"/>
is two way read/write binding.
Hi, well I'm using Flex 4, thanks for the tip, that's perfect!
ReplyDeleteNice work, Art! Very helpful. Thanks for taking the time to write these blog entries.
ReplyDeleteI am new to flex. while going thru this tutorial I could not figure out how to create the "Object" tag...
ReplyDeletedo i create it as an MXML component or module or application or itemRenderer, when u right click and say "NEW"... tried all these methods but object tag would not show up under mx except under fx namespace..
contd... for the previous question posted by anonymous...
ReplyDeleteI am trying to create this object
Thanks
/mx:Object
ReplyDeletexmlns:mx="http://www.adobe.com/2006/mxml" xmlns:model="model.*">
</mx:Object
@Anonymous,
ReplyDeleteYeah, it's not obvious. Basically you can create any MXML component, just pick Canvas. Then go into the source view, select all the existing text and replace it with the text from the BasicExampleContextConfig.mxml example.
Thank you Jeremy.
ReplyDeleteGood work Art!
ReplyDeleteIts a great article for anyone who is new to Flex or just parsely.
Thanks,
PReddy
Could you please explain how the Method Injection works? The official Parsley documentation only mentions it, but doesn't give any clue to what this feature might be used for!
ReplyDeleteThanks!
Thanks PReddy,
ReplyDeleteSure sansegot. The name makes it sound like you are injecting a method, but that's not the case. It's injection of value through a method.
Normally, for Injection to work, the property needs to be Public, but for example, let's say that you wish to make it private, or you wish to have some code triggered whenever the value is injected. That what Method injection is good for.
So you place the [Inject] tag just before a public function. Parsley will look at the property types that the function (method) expects and will inject according to that.
For example :
private var myService:LoginService;
private var serviceReady:Boolean = false;
[Inject]
public function setTheService (service:LoginService,) : void
{
myService = service;
serviceReady = true;
}
So Parsley will look in the context for some object of type LoginService to inject into this function.
That's it. Basically the same applies to Constructor injections, except that Constructor injection might require some changes in the context config file.
Sorry about the extra comma after service:LoginService it should not be there.
ReplyDeleteArt, thank you for one more great explanation!
ReplyDeleteThe Parsley documentation is written for people already familiar with the concepts the framework is trying to solve. Without your articles I don't think I would ever start using it!
Thanks to you now not only do I understand the concepts, but I'm starting solving problems in my project with Parsley!
I really owe you a beer!
Thanks, I'll take you up on the beer someday.
ReplyDeleteDear Art, your blog is spamming me. I left a comment once and now every new comment is sent to me (not sure that I even requested this!). If I click "Unsubscribe" it takes me to a page trying to persuade me to sign up for a Blogger.com account! Arrgghhh!!!! SPAAAAAMMM please help!
ReplyDeleteHi John,
ReplyDeleteSorry about that. I had no clue blogger did that, and honestly don't know how to stop it as it's nothing in the settings, it's a blogger thing. Another reason why to move away from blogger.
I googled a bit and found this: Hope it helps.
http://www.google.com/support/forum/p/blogger/thread?tid=331aee3bc5575ec6&hl=en
The blogger account would allow you to manage your subscriptions, but that is lame that it forces you to do that. I'm really sorry.
I'll go add a comment to someone else blog so I can find a way to unsubscribe.
Does parsley allow to inject a view in another view? For example, say we have two implementations of a view (say DefaultNavigationPanel and BUNavigationPanel). If business specific implementation is available we will inject NavigationPanel1 else we will inject DefaultNavigationPanel.
ReplyDeleteAlthough it's not a common scenario, I assume it would work just fine. Do notice that Injecting it, does not mean that it will be added to the stage. That still would have to be done manually, after it was injected.
ReplyDeleteHi Art,
ReplyDeleteThank you so much for these wonderful articles on Parsley. Do you have a printable format of all these articles? It is tough to get this printed out from these web pages.
Thanks,
Vij
Hi Vij,
ReplyDeleteThanks. Unfortunately I don't have them in a printable format. Sorry.
-Art
Art, you and your little art. Magic.
ReplyDeleteThank you ever so much.
Hi Art
ReplyDeleteam new to flex & this article is a gift to me.
Thnak u so much.....
- Priya
Hi Art, thanks for writing this sets of tutorials.
ReplyDeleteI have a question on dependency injection.
I have 2 classes A and B which inject the same object of the type C (a VO class). Everything works as expected until I try to assign the injected variable to a new object instance in one of the classes (say A). Now the injected variable in the other classe B is not updated to the new instance, instead its properties are always the same as before.
public class A
{
[inject]
public var c:C;
public function reset():void
{
c = new C();
//after this the injected variable c in class B is not updated
}
}
public class B
{
[inject]
public var c:C;
}
Is this behaviour as expected and are there any workarounds?
Thanks.
Regards,
Yang
@Yang.
ReplyDeleteHi, yes, that is the expected behavior. The "workaround" would depend on what are you trying to accomplish.
For practical purposes Parsley creates an object of type C. It is assigned to your public var c in class A. When you invoke c = new C(); you basically remove the referece to the object managed by Parsley, so any changes made to the new object will not be reflected on any shared object.
If you wish to clear all the values, simply have some method in the VO that clears the values, but does not change the address of the underlying object.
Hope that helps.
Thanks Art. I really appreciate your help. Actually your suggestion is what I'm doing currently. My concern is that as I'm currently working with Blazeds, I need to have an object returned in the result event handler to be mapped directly to the value object I have in flex. Before integrating with parsley I just needed to say something like vo = data as VO; (vo is the injected variable). Now this won't work as it won't be reflected on other classes sharing this object. Does it mean I have to convert every attribute in the vo line by line now? Are there any good practices regarding this?
ReplyDeleteAgain thanks for your help, I really have learned a lot from you.
Regards,
Yang
Hi Yang,
ReplyDeleteGreat question!
Disclamer: I've always considered best practices to be something very subjective, what is best for me, may not be best for you, nor everyone in every case.
The built in Flex data/services don't fit in to well with MVC because they create a model and a VO shoved into a VO. It's cool for simple things, not that great for more complex things.
So if you really wish to get the most out of your MVC architecture, then yeah, you need to copy properties.
Now there are some things to take into account. Flex/Flash does not always copy contents, but just copies references, depending on the datatype. For example a ByteArray will not get copied, it copy a reference to the original memory location.
Anyways, I have found best to have the "copy" code in one place of the VO so that I don't have it spread all over the place in case I add/remove properties easily. I've also found it useful that in the constructor I check the datatype being passed and automatically make a copy of the object if it receives the specified type.
Now, speaking of MVC, VO's are usually not injected directly, more often the model is what is injected. The model holds the VO's. This is a bit different from how Flex/Data Services are by default, since the VO is a Model and a VO.
What makes a Model a Model is that it holds state. Holding state is what makes Models cool. Using a Model to simply go get a record and discard it immediately after is overkill.
What I mean by holding state is that you will hold that data for more than just fetching it. It can be simultaneously used elsewhere, or in the future as a cache or as a reference to see if data has changed, in other words, you care about the current state of the data, and the data can change state (values).
Hope that helps.
Also would you share your thoughts on using commands(e.g. dynamic commands in parsley) VS using the Spicefactory Task Framework?
ReplyDeleteBasically I want to use the Task framework for 2 reasons: a) I want to put a set of tasks into a group and execute them sequentially or concurrently without couplings between the tasks; b) I want to use the cancel() function for tasks and taskgroups, so that an event indicating the completion of the task won't be always dispatched.
One use case of b): I have a RemoteLoginCommand (dynamic command), in the result() function I dispatch an LoginedEvent to indicate a successful login. Somewhere else exists the messageHandler for the event, which tells the application to change to loggedIn state. Now if the user cancel the login while waiting for the response from the server, I don't want the LoginedEvent in the command class to be dispatched any more because as the user has already canceled the login, I don't want the user to be taken to the loggedIn state even when the response is received. The solution I can think of is to add a flag in the command class indicating whether the login request has been canceled. Then after the user cancels the login, dispatch another event which is listened by the command class, which will then change the flag, which will then stop the LoggedInEvent from dispatching.
With this approach I need to have a lot of events dispatching and handling, but the classes are still decoupled.
If I were to use the Task framework, I can simply do loginTask.cancel() and the result handler won't be executed anymore. This is quick but the tasks are now coupled with the class that uses them, since the start() and cancel() functions have to be called explicitly.
What approach would you recommend?
I apologize if my description is not clear to you, otherwise I'm really enjoying the discussion with you on these topics.
Regards,
Yang
Hi Art, I tried to post a comment (before the last one) but it's always been deleted immediately after it's published. Do you know why that happens?
ReplyDeleteI'll try it again.
ReplyDeleteOK that makes sense. I'm not implementing a strict MVC architecture, but presentation model instead. But I guess the underlying concepts are similar. A few questions:
I'm not sure if I fully understand your interpretation of the difference between a model and a VO. From my understanding, a model in the presentation model architecture is usually mapped to a corresponding view. So although they don't know the existence of the views which use them, they are more or less just the state holder of the view (and often they have 1:1 relationship). Whereas with VOs they don't have the explicit relationship with a particular view and can be used by presentation models and commands, etc, as needed. Is this a reasonable understanding?
You said that "VO's are usually not injected directly". What would be a common approach to use shared VOs in different models/commands if they are not declared to be managed by Parsley in the context file and injected in the PM and command classes?
ReplyDeleteRegarding "have the "copy" code in one place of the VO so that I don't have it spread all over the place in case I add/remove properties easily", are you saying I should have a method like updateProperties(?,?,?,?,?…) where I pass all the properties in the class which I want to update into the list of parameters and set the properties in there? For example, say I have just received from the server an object called data with the same properties as the VO, since I can't simply do vo = object for reasons you mentioned previously, instead I would do vo. updateProperties(data.id, data,name, data.address......)? Is this what you mean or are you talking about something totally different?
@Yang,
ReplyDeleteYour previous comments where sent to the possible spam folder, maybe you double clicked by accident and it was flagged as being sent twice or who know. Anyways.
Your first question was also asked in:
http://artinflex.blogspot.com/2010/10/quick-dive-into-parsley-dynamic-command.html
Actually you can combine both Task Framework and dynamic commands.
Check out
http://www.spicefactory.org/forum/viewtopic.php?p=2510&sid=dc5e09ae3e861eebface7a1ec75ccca2
A model does not have to be tied to a view unless it is specifically a presentation model, but that's just one type of model.
What makes a model a model is that it holds state, regardless if it's tied to one or accessed by many views (tied to none).
On the other hand you have Value Objects (VO). Now VO's are used in many different ways. In the default Data/Services of Flex, the actually hold state, which makes them a model.
To me, a VO is strictly an object that holds data, similar to a struct of C/C++, but can do validations, etc... but the VO does not hold state.
A model can host an array of VOs or a previous and current VO, or a history of VOs.
Check out:
http://artinflex.blogspot.com/2010/11/presentation-model-implemented-in.html
That might help a bit.
With the update properties, yeah. But not a long list of parameters.
For example you can have a very generic:
public function updateProp(source:Object)
{
if (source.hasOwnProperty("name"))
this.name = source.name;
if (source.hasOwnProperty("url"))
this.url = source.url;
if (source.hasOwnProperty("tel"))
this.tel = source.tel;
}
So you just pass another VO to that VO, to have a single source to update it's values from. Similar concept as how you implement a clone() function. Of course you can use something specific rather than an Object and have type checking for you. And your clone() function can call the updateProp() function and have all that in one single place.
Hope that helps.
Thanks Art. That helps.
ReplyDeleteSO if I have a VO (say UserVO) which holds the information for a User after logging on, should I be passing the vo around through events or injecting the VO in the places where I need it? This vo will be persistent during a login session.
Regards,
Yang
Following the question above, I'm basically not able to decide when to use injection and when to pass objects by event dispatching. For example, if I combine the task framework and dynamic command, should I be injecting tasks in a command class or just create tasks as local variable in the execute function, like the example shown in the Spicefactory forum?
ReplyDeleteYang,
ReplyDeleteThere is no one "perfect" solution for everything. Different needs, different solutions.
Your example fits great for using the Model concept. Rather than passing a UserVO around. You can have a "Session" model that you inject. The Session model holds State. It holds whether the user is logged on or not. Being logged on might give access to some areas, not to others. If the user is logged on, then you can have fields that describe the user. These don't necessarily need to match the "Transport" VO that you got from the server. The UserVO retrieved from the server can be used to update more than one Model. For example a "Preferences" Model, or a "Permisions" Model, not just the Session Model.
See, your Client models do not need to match your data models, and they don't need to match your server requests. They can, but they don't have too.
The concept relies on have a State that can change. For example if the user is logged in or not.
Going MVC is a bit weird at first if you are used to the concept of:
Need data -> Get data -> use data -> ditch data.
Under MVC, you view could care less about processing the data, what it cares about is the State of the data. The view will inject the data that it cares about. It might choose to have that data "refreshed", which triggers a command. The command will choose how to refresh the data, it might call the service. The service will return a VO which the command (or the service, depending on your choice) uses to update the model. The model let's the interested active parties know that it has updated data, through messaging or binding (which is messaging anyways), and the view gets the data from the model, not from the command.
What if you have multiple views that adjust behavior or display information dependent on the user being logged in. If the user logs in from anywhere, all you have to do is update the model and all the view get "magically" updated.
The model may or not have a VO in it. I use VOs if the data needs structure and if I'm going to have more than one record of that same data, for example a list of users, etc... But a session model will rarely ever have a VO in it.
A great benefit with this concept is that if you choose to dramatically change server technologies, changing the remoting VO's would not have an impact on the rest of the project.
Check out:
http://artinflex.blogspot.com/2010/10/quick-dive-into-parsley-dynamic-command.html
for a very simple example of that concept.
Now, going this way does generate more work, and you need to work on being consistent so that you can follow it in the future.
For simple one/two view projects it's just plain overkill, but good for practice and learning.
Thanks Art. I have gone through the whole sereis of your tutorials by the way, and I constantly consult them since they contain concrete examples.
ReplyDeleteI understand MVC. It's just that when using the Presentation Model variation, a model (PM) is usually specific to a view or a set of similar views in terms of the data they need. Now if I have a Session model, I can't use it as the PM for any views directly as the data in the Session model is probably not enough for any views. Therefore in the PM of a view I have to retrieve relevant data from the Session model (possibly injected) and updates it when instructed by the view.
Speaking of VO, there seems to be some confusion on the difference between VO and DTO.
http://www.adam-bien.com/roller/abien/entry/value_object_vs_data_transfer
I assume that when we talk about VO we generally refer to DTO as defined in the page above. Then when I have a Session model, in a Presentation Model scenario, what differentiate it from a VO? If I understand you correctly, since a model holds states and a vo doesn't, a vo should then be immutable (or at least not saved and shared by multiple classes through injection). Rather they would simply be used as data containers (or DTOs) which are passed around through events or remoting.
Again thanks for sharing.
Regarding the task framework/dynamic commands integration, this seems to be the major functionality improvement for Parsley 3, which will deprecate the Task lib and introduce a new Command lib which makes chaining and grouping commands a lot more natural.
ReplyDeleteYang,
ReplyDeleteYour are correct. In your PM you would inject your other models. Basically a PM separates the View from the framework.
In Flex 4 you have skins which sort of acomplish the same goal, just not at the same level.
For example with Robotlegs, you "should" use mediators for every view. A mediator is similar to a PM (except that the view just does not know about the mediator, but could if you really wanted to). In Robotlegs, a view does not have a built in way to inject anything to it, it should be done through mediators.
Parsley is more flexible in that sense. It doesn't impose any pattern. You may or not use PM or MVC or anything, you can just use whatever you want and mix and match.
Should you use PMs? Hmmmm... It depends on your project. The REAL truth is that it is very unlikely that you will switch views and keep the same PM in most project. Even if you are building a Mobile and desktop view, you will probably end up making some adjustments to your PM anyways. It is nice to have them apart when you make some changes, but it is more work to use them.
About VO's. Yes the commonly used VO term refers to DTOs. I usually call my enums as enums probably because I come from a C/C++ world.
And yes. I personally think that PM come handy when there is state involved, for example a data entry form, or any type of view that changes the value of data. You can even implement multiple Undo's, which is great for the user, and have an array of VOs (DTO) in which the state for each undo is stored (for example).
In my opinion using PM for simply displaying a list of tweets (for example) is just overkill, but if you hold state such as which you keep a history of the order in which the user viewed the details of the tweets, then you could justify it.
Yeah, I saw that news entry. I look forward to Parsley 3.
Konya
ReplyDeleteKayseri
Malatya
Elazığ
Tokat
LWLİJ8
Ankara
ReplyDeleteBolu
Sakarya
Mersin
Malatya
CUYW0
Diyarbakır
ReplyDeleteSamsun
Antep
Kırşehir
Konya
8SJ12
görüntülü show
ReplyDeleteücretlishow
5P7
https://titandijital.com.tr/
ReplyDeletekilis parça eşya taşıma
bursa parça eşya taşıma
ığdır parça eşya taşıma
bitlis parça eşya taşıma
XEAP
ankara parça eşya taşıma
ReplyDeletetakipçi satın al
antalya rent a car
antalya rent a car
ankara parça eşya taşıma
JVQWOV
rize evden eve nakliyat
ReplyDeletemuğla evden eve nakliyat
kırıkkale evden eve nakliyat
mardin evden eve nakliyat
istanbul evden eve nakliyat
ZAWİ2
6C534
ReplyDeleteGümüşhane Parça Eşya Taşıma
Adıyaman Evden Eve Nakliyat
Artvin Parça Eşya Taşıma
Karaman Evden Eve Nakliyat
Elazığ Parça Eşya Taşıma
8B05C
ReplyDeleteElazığ Lojistik
Sakarya Parça Eşya Taşıma
Osmaniye Parça Eşya Taşıma
Karabük Parça Eşya Taşıma
Siirt Parça Eşya Taşıma
C5115
ReplyDeleteBilecik Parça Eşya Taşıma
Samsun Evden Eve Nakliyat
Erzurum Parça Eşya Taşıma
Kırşehir Parça Eşya Taşıma
Kırşehir Lojistik
0A453
ReplyDeleteRize Parça Eşya Taşıma
Bayburt Parça Eşya Taşıma
Bolu Şehir İçi Nakliyat
İstanbul Şehirler Arası Nakliyat
Yalova Evden Eve Nakliyat
Kocaeli Şehir İçi Nakliyat
Ankara Şehir İçi Nakliyat
Çerkezköy Oto Boya
Aydın Şehirler Arası Nakliyat
6C3D3
ReplyDeleteIsparta Lojistik
Bolu Lojistik
Çankaya Parke Ustası
Eskişehir Evden Eve Nakliyat
Silivri Parke Ustası
Burdur Evden Eve Nakliyat
Tokat Şehirler Arası Nakliyat
Çerkezköy Fayans Ustası
Çerkezköy Bulaşık Makinesi Tamircisi
49D0E
ReplyDeletehttps://referanskodunedir.com.tr/
343BF
ReplyDeletekırşehir görüntülü sohbet ücretsiz
sesli sohbet siteleri
muğla parasız sohbet siteleri
sohbet odaları
malatya görüntülü sohbet sitesi
diyarbakır en iyi ücretsiz sohbet siteleri
bitlis rastgele canlı sohbet
Afyon Random Görüntülü Sohbet
Adıyaman Canli Goruntulu Sohbet Siteleri
601D9
ReplyDeleteığdır Canlı Sohbet Et
ankara rastgele sohbet uygulaması
Karaman Rastgele Sohbet Odaları
çorum canlı sohbet ücretsiz
aksaray canlı sohbet ücretsiz
urfa görüntülü sohbet siteleri
bitlis görüntülü sohbet ücretsiz
kızlarla rastgele sohbet
artvin bedava görüntülü sohbet
BC05E
ReplyDeleteMith Coin Hangi Borsada
Btcturk Borsası Güvenilir mi
Bitcoin Kazanma
Ön Satış Coin Nasıl Alınır
Lovely Coin Hangi Borsada
Gate io Borsası Güvenilir mi
Arbitrum Coin Hangi Borsada
Bitcoin Nasıl Para Kazanılır
Dxy Coin Hangi Borsada
YVGHKMJUJH
ReplyDeleteشركة صيانة افران بمكة
صيانة افران الغاز بمكة
ReplyDeletehgfdsdardtfgjhk
شركة عزل اسطح بالخرج xMw7cdwY54
ReplyDeleteشركة تنظيف خزانات ykUzJj9jAf
ReplyDeleteتسليك مجاري kXe4H7ZeT0
ReplyDelete