YUI 3 App Framework ( Y.App, Y.Router, Y.View )

Long time since my last post, when I look back and I see all the things I experience since my last post I am totally shock, but I am sure you are not reading this post to hear about my life, achievements or  failures, you are here to read about the new YUI 3.5 App Framework.

Before we get started I want to make one point clear, I am not saying that I will show you the best way you can  use YUI 3 App Framework, it’s just my view on how you can use it and put it to practice with a simple example. I am pretty sure there are other ways you can use this framework so fell free to ask your friend “Google” and find more information about this topic before starting your project.

Because I am a very pragmatic person and I really  belief  in the power of example I will show you how I use YUI 3 App Framework with a simple example. The scenario will be pretty simple:

- you can login
- add users
- see the list of users
- delete a user

Views ( Y.View, Y.Phi.BaseView, Y.Phi.ComposeView )

So the first thing that we will prepare are the views we will use in our application. We have:

1. LoginView
2. MainView
2.1. CreateUserView
2.2. ListUsersView

in total we will have 4 views. A view extends from Y.Phi.BaseView or Y.Phi.ComposeView and has two files:

- View.js
- View.tpl

for example the LoginView is a simple view and looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
var LoginView = Y.Base.create("loginView", Y.Phi.BaseView, [], {
 
	templateFile: 'Login.tpl',
 
	events: {
		"#login-btn": {
			click: "onLogin"
		}
	},
 
	initializer: function( cfg )
	{
	},
 
	/**
	 * Render view.
	 */
	render: function ()
	{
		// if the Login.tpl file is not loaded
		// we wait and only when Login.tpl is loaded we render the view
		if( !this._loaded )
		{
			this.once('load', this.render, this);
			return;
		}
 
		// Add body to container
		this.get('container').setContent( Y.mustache(this.template, {}));
 
		return this;
	},
 
    //-----------------------------------------------------------------------------------------
    // Event Handlers
    //-----------------------------------------------------------------------------------------
 
	onLogin: function( evt )
	{
		var email = this.usernameInput.get('value');
		var password = this.passwordInput.get('value');
 
		// Make login here
	}
});
 
// Publish
Y.LoginView = LoginView;

and the Login.tpl file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div class="login">
	<div class="form form-box">
		<div class="error-msg hide">Incorrect Email or Password</div>
		<div>
			<input id="username" type="text" /></div>
		<div>
 
		<input id="password" type="text" /></div>
		<div class="remember-me">
			<label for="autologin">
			<input id="autologin" class="checkbox" name="autologin" type="checkbox" value="1" />Remember me</label>
		</div>
 
		<div>
			<div id="login-btn" class="blue-btn">Login</div>
		</div>
	</div>
</div>

You will see that all my views extends Y.Phi.BaseView. This class extends from Y.View and only adds the ability to load a .tpl file and assign is content to “this.template“. I didn’t like the idea of  writing my template in the js file using quotes so after some search on the internet I have fond this solution of loading the template using an XHR request. I don’t remember the website were I have found this but I think is the most elegant solution.

So to use this you need 2 modules:

- phi-baseview
- template-loader

Model

After we have our views we need to see what kind of data we will use in our application. For this example all we need is:

- Y.User
- Y.ModelList({model: Y.User}) a collection that will contain all our users

And then all we need to do is glue all this using Y.App.

Application

The application code is very basic, I just register the two views ( LoginView and MainView ) and switch between the two views.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Application
var app = new Y.App({
	views: {
 
		loginView: {
			preserve: true,
			type: 'LoginView'
		},
 
		mainView: {
			preserve: true,
			type: 'MainView'
		}
	},
 
	users: new Y.ModelList({model: Y.User})
});

Because MainView is a compose view when we will show MainView the CreateUserView and ListUsersView will be displayed too. Another thing that you will see in this example is how to communicate between views. For example when you create a new user using CreateUserView you will want the ListUsersView to redraw the list of the users so you can see the new created user in the list. This will be easy to achieve. In ListUsersView we will add:

1
2
3
4
5
initializer: function( cfg )
{
	// Render view if tournaments change
	this.get('users').after('add', this.render, this)
}

and in index.html we will do:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.on('createUserView:save', function (evt){
 
	var user = evt.user;
 
	user.set("name", evt.name);
	......
	......
 
	user.save(null, function( err ) {
		if( !err )
			app.get('users').add( user ); // this will add user in users list and dispatch 'add' event
	});
 
});

The routes are self explanatory:

1
2
3
4
5
6
7
8
9
10
app.route('/login', function (req) {
    this.showView('loginView');
});
 
app.route('/index', function (req) {
    this.showView('mainView', {
    	user: new Y.User(),
    	users: this.users
    });
});

And this is all. To learn more please study the code and read the comments.

Demo application
Download the source ( 40.227 Kb )

Leave a comment if you have any questions.

Code formatting for Adobe Flex – Flex Formatter

After using Flash Builder for a log time I think everyone has a lot of custom settings for the workspace they use so whenever you put a fresh OS or just reinstall Flash Builder first think you do is setting up the workspace and I think everyone has his own special thinks to setup. Me for example I install subclipse, setup some custom key shorcuts, edit some perspectives and much more but I think the most useful plugin and a plugin that all of my colleagues use is Flex Formatter, this plugin it’s a must and I install this all the time, again and again and they do the same.

So because of this, most of the time people ask me: From where to download Flex Formatter ? How do I install Flex Formatter ? Can I share my format settings ? so I decided to write the answer to all this questions in a blog article.

From where to download Flex Formatter ?

Answer: You can download a .zip of Flex Formatter from : http://sourceforge.net/projects/flexformatter/

How do I install Flex Formatter ?

Answer: After you have download the .zip  archive all you have to do is to copy all the three .jar files from the archive in the Adobe Flash Builder plugins directory. For example in my case I had to copy the three files here “C:\Program Files\Adobe\Adobe Flash Builder 4\plugins\“.

Can I share my format settings ?

Answer: Yes
The formatting settings I use is :  format.properties

Using AMF and RemoteObject without services-config.xml

This article will be very short but I hope it will be very useful. The goal of this article is to show you how to use a RemoteObject without service-config.xml. The example in this post will be very simple, we will connect to a AMFPHP service and get a hello message from it.

First let’s see the service:

1
2
3
4
5
6
7
8
9
10
11
<?php
// hello.php
class Hello
{
	public function sayHello()
	{
		return "Hello back!";
	}
}
 
?>

Now in Flex all we have to do is to load a XML file ( channel.xml ) and read the channel from it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
protected function addedToStageHandler( event :Event ) :void
{
 
	var loader :URLLoader = new URLLoader();
	loader.addEventListener( Event.COMPLETE, completeHandler );
	loader.addEventListener( IOErrorEvent.IO_ERROR, ioErrorHandler );
	loader.load( new URLRequest( "channel.xml" ));
 
}
 
protected function completeHandler( event :Event ) :void
{
 
	// Load info from XML
	var xml :XML = new XML( event.target.data );
	var amfEndpoint :String = xml..channel.( @id == "my-amfphp" ).@endpoint;
 
 
	// Create a AMF channel
	var channel :AMFChannel = new AMFChannel( "my-amf", amfEndpoint );
 
 
	// Add channel to our RemoteObject
	rmtObj.channelSet = new ChannelSet();
	rmtObj.channelSet.addChannel( channel );
 
}

Now if you use the loaded channel for all your RemoteObjects you don’t have to compile again your application if you decide to change your AMF endpoint, all you have to do is to edit the channel.xml file.

This is very useful when you upload your application to a server, you don’t have to compile again the application to work on different servers all you have to do is to use different channel.xml.

You can download the code here ( click to download ).

And this is all. If you have any questions post them here.