Sencha Touch 2, How to setup an MVC

After recently working on a project with Sencha Touch, it sparked my interest with javascript MVC. The idea of MVC is new to me for javascript, which used to consist of including a file with a large amount of functions. I am sure there are many people that can relate. Sencha Touch just released a beta version 2, so I jumped at the chance to take it for a test drive and see the improvements.

The main upgrade was the performance, in particular the initial startup. If anyone has experience with mobile web apps, the initial startup time is the main killer.

This is a simple guide to help you setup the skeleton MVC. It took me a couple hours to get it right because the documentation hasn’t been updated for beta 2. Hope this will save someone out there from several hours of googling errors and a few unneeded grey hairs. Lets get into it.

This will be the basic root structure.

Creating the index.html file is pretty baisc. The 3 files that you need to link to are the sencha touch files (the main js file, and the main css file). the app.js is what we will create next that starts up the app.


<!DOCTYPE html>
<html>
<head>
<title>imdavidr mobile</title>
<link rel="stylesheet" href="sencha2/resources/css/sencha-touch.css" type="text/css" media="screen"/>
<script src="sencha2/builds/sencha-touch-all-debug.js"></script>

<script src="app/app.js"></script>

</head>

<body>
</body>
</html>

Next, create the app directory with the mvc structure and our app.js file:

Inside the app.js file this what we will have:


// Main application entry point
Ext.Loader.setConfig({enabled:true});
Ext.application({
 name: 'app',
 controllers: ['mainController'],
 autoCreateViewport: true,
 launch: function() {
console.log('app:launch');
Ext.create("app.view.Viewport")
 }
});

I am not sure why, but we must set the loader to enabled. The app will not load if we do not, not sure the logic behind this. The app name is important, make sure the you set the name to the folder name. The reason is, the new sencha loader will look for the folder directory to load all the views and controller (i.e. name: ‘app’, it will load app/controller app/views etc). Ext.create will create a instance that is immediately shown to the browser. Next we will create the viewport file. We are also passing a controller name to the controller array. This is the name of the controller to load, but we will create this a bit later. Do not the this must the name of the file, not the name given to actual controller ( without the .js of course).
Inside the view folder, create a file named Viewport.js, yes with a capital V:

Ext.define('app.view.Viewport', {
 extend: 'Ext.TabPanel',

 config:{
 fullscreen: true,
 tabBarPosition: 'bottom',
 items: [
 {
 xtype: 'homePage',
 title: 'Home',
 iconCls: 'home',
 layout: 'fit',
 },{
 xtype: 'contactPage',
 title: 'Contact',
 iconCls: 'reply',
 dockedItems: [{
 xtype: 'toolbar',
 dock: 'top',
 }],
 },
 ]
 },
})

This sets up the tabbar that is displayed when the device page loads. This is what is created in app.js by the Ext.create(). Notice that it extends the Ext.TabPanel. We can now add items with our custom xtypes that we will create in our views. This is how sencha really embrasses the MVC structure. Not that is manditory to pass a title with tab items. Next we will setup our views.

In the views folder create a file name home.js, no capital here:


Ext.define('app.view.home', {
 extend: 'Ext.Panel',

 xtype: 'homePage',

 config: {
 scroll: 'vertical',
 html: 'Yo, welcome to my homepage',
 styleHtmlContent: true
 },
 initialize: function() {
 this.callParent(arguments);

console.log('homeView:initialize');
 },
});

Notice that here we call setup the xtype name that was called by our TabPanel. A big change from Sencha touch 1.1 is now everything is passed to a config object, the init function is no loger used to setup the page elements. The initialize function is only used to inherit arguments and setup custom arguments. Another different is now every file is treated as a class, this is the defining a name at the top and ALWAYS extending elements. Now we will do the same but for our contact page.

Inside the views folder, create another file called contact.js:


Ext.define('app.view.contact', {
 extend: 'Ext.form.Panel',

 xtype: 'contactPage',
 config: {
 id: 'contactForm',
 items:[{
 xtype : 'fieldset',
 title : 'Enter Contact Information:',
 instructions: 'All fields are required',
 layout: {
 type: 'vbox'
 },
 items:[
 {
 xtype: 'textfield',
 name: 'name_contact',
 label: 'Name',
 placeHolder: 'First Last',
 required: true,
 useClearIcon: true,
 },{
 xtype: 'emailfield',
 name: 'email_contact',
 label: 'Email',
 placeHolder: 'user@example.com',
 required: true,
 useClearIcon: true,
 },{
 xtype: 'textfield',
 name: 'subject_contact',
 label: 'Subject',
 placeHolder: 'Subject',
 required: true,
 useClearIcon: true,
 },{
 xtype: 'textareafield',
 name: 'message_contact',
 label: 'Message',
 placeHolder: 'Message',
 required: true,
 useClearIcon: true,
 }
 ],
 }
 ,{
 xtype: 'button',
 text: 'Send Message',
 ui:'confirm',
 id:'sendMessage',
 action: 'sendMessage'
 }
 ]
 },
 initialize: function() {
 this.callParent(arguments);
 console.log('contactView:initialize');
 },
});

This is pretty standard Sencha stuff here, but again notice it is all being passed through the config object and nothing is really happening inside the initialize function. Something to note, the button has an action added inside the params. This will come into play later when we need to handle the action of the button click. It is also important to pass and id into the config object so we can retrieve the form once the button is clicked. Now we will create the controller to load all the views.

In the controller folder, create a folder name mainController.js:


Ext.define('app.controller.mainController', {
 extend: 'Ext.app.Controller',

 views: ['home', 'contact'],
//models:['mainModel'], if you wanted to load a model
//stores: ['mainStore'], if you wanted to load a store
 config:{
 refs: {
 ContactForm: '#contactForm'
 },
 },
 init: function() {
 console.log('mainController:init');

 this.control({
 'button[action=sendMessage]': {
 tap: 'submitContactForm'
 }
 });
 },
 submitContactForm:function(){

 var formValues = this.getContactForm().getValues();

 var $this=this;
 Ext.Ajax.request({
 url: 'ajax.php',
 method: 'POST',
 params: {
 formValues: Ext.encode({form_fields: formValues})
 },
 success: function(response, opts) {
 var obj = Ext.decode(response.responseText);
 Ext.Msg.alert('Contact Complete!', obj.responseText);
 $this.resetForm();
 },
 failure: function(response, opts) {
 console.log('server-side failure with status code ' + response.status);
 }
 });
 },
 resetForm: function() {
 this.getContactForm().reset();
 },

});

Again we define our controller name, and Extend the Ext.app.Controller to inherit its methods. Next we define our views to be loaded. This is where it is important to have the same app name and folder name because it is going to look for {app the was set in app.js}/view/home.js and {app the was set in app.js}/view/contact.js. The next things is passing our config object, this is where it got completed due to lack of documentation. Refs are a brand new concept to Sencha Touch, it is a way to setup a way to get a component when the controller is instantiated. The real magic is it queries the DOM for the component with the passed id, in our case ‘#contactForm’ , this was why it was important to pass the id in the contact.js config. Refs give you access to Sencha built methods like getContactForm() and it returns that component.

Next the init function is meant to setup button handles and so on. We use a new method call control(), we pass an object , in this case ‘button[action=sendMessage]’, which means select with button with action sendMessage. Now add a tap event listener and run submitContactForm() when tapped. If you look inside submitContactForm() you will see the ref method in use to get to form values before the form is submitted.  Here I am using an ajax request, but it can be just as easily completed with:

form.submit({
url: 'url/to/submit/to',
method: 'POST',
success: function() { alert('form submitted successfully!'); } });

Thats about it… It does get much more complicated than this when you start involving stores and models, but that will be for a later tutorial. Enjoy and comment.

Posted in Mobile, Sencha Touch, Stuff | 3 Responses

Git Cheat Sheet

Maybe a little dated but has come in handy quite a few times for me…

http://ionrails.com/2009/08/07/git-commands-adding-and-committing-cheatsheet/

Posted in Git, Stuff | Leave a comment

Custom Excerpt Length

With the_excerpt() returning the first 55 characters of the post, this was to long for my slider. Add this function to your themes function.php and adjust the return number according to your liking and it will limit the excerpt to a custom length.


function custom_excerpt_length( $length ) {
 return 30;
}
add_filter( 'excerpt_length', 'custom_excerpt_length', 999 );

Posted in Wordpress | Leave a comment

Force-Download a file with AJAX

While trying to force-download a file, I quickly realized this was not possible with AJAX. I then sat down, drank some coffee, and came up with a solution to this issue.

My problem was not being able to set the headers within the AJAX request. Using Elouia’s Force Download to download files, it was working like a dream if the file existed. I needed a way to check if the file existed and then download the file. Here is what I came up with.
The jQuery AJAX:

$('.dl_photo_hd').live('click',function(e){
		e.preventDefault();
		$url = url_to_file/force-download.php?file=directory/to/file.jpg;
		$.ajax({
			type: 'GET',
			url: $url,
  			success: function(data){
    			   if(data == true){
    				alert('This file is not available for download.');
    			   }else{
    				window.location =""+$url+"";
    			   }
  			}
		
		})
});

The force-download php file (this code should be in a file by itself):

<?php

$filename = $_GET['file'];

// required for IE, otherwise Content-disposition is ignored
if(ini_get('zlib.output_compression'))
  ini_set('zlib.output_compression', 'Off');

// addition by Jorg Weske
$file_extension = strtolower(substr(strrchr($filename,"."),1));

if( $filename == "" ) 
{
  $error = true;
  exit($error);
} elseif ( ! file_exists( $filename ) ) 
{
  $error = true;
  exit($error);
};

switch( $file_extension )
{
  case "pdf": $ctype="application/pdf"; break;
  case "exe": $ctype="application/octet-stream"; break;
  case "zip": $ctype="application/zip"; break;
  case "doc": $ctype="application/msword"; break;
  case "xls": $ctype="application/vnd.ms-excel"; break;
  case "ppt": $ctype="application/vnd.ms-powerpoint"; break;
  case "gif": $ctype="image/gif"; break;
  case "png": $ctype="image/png"; break;
  case "jpeg":
  case "jpg": $ctype="image/jpg"; break;
  default: $ctype="application/force-download";
}
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false); // required for certain browsers 
header("Content-Type: $ctype");
// change, added quotes to allow spaces in filenames, by Rajkumar Singh
header("Content-Disposition: attachment; filename=\"".basename($filename)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filename));
readfile("$filename");
exit();

?>

There needs to be validation on the $filename variable for security reasons but I will leave that up to your project’s needs.

Posted in CodeIgniter, jQuery/JavaScript/Ajax, PHP | Leave a comment

960 Grid with Sticky Footer

Below is a brief and bare example of how to use the “Sticky Footer” with the 960gs.

HTML

<div id="wrap">
   <div id="main">
      <div class="container_12">
          <div class="grid_12">
            <p>Cotent Stuff Here</p>
           </div>
       </div>
   </div>
</div>
<div id="footer">
 <p>Footer Contents</p>
</div>

CSS

html, body {height: 100%;}
#wrap {
   min-height: 100%;
}
#main {
   overflow:auto;
   padding-bottom: 30px;/* must be same height as the footer */
}  
#footer {
   position: relative;
   margin-top: -30px; /* negative value of footer height */
   height: 30px;
   clear:both;
} 


**Remember to change the height,margin-top and padding-bottom to the correct height of YOUR footer**
**Remember to link to the 960 grid!!!**

Posted in CSS | Leave a comment

Enabling File Uploader with CKEditor

While trying to setup an admin section for a client, I came to a realization. It was manditory to allow images within each of their updates. Little did I know, CKEditor does not come standard with a file browser and uploader. After searching for a free answer, I came across Penuel's blog on how to create a file browser that works with CKEditor. With small modifications, it works very well.

Thanks Penuel!

Posted in CodeIgniter, PHP | Leave a comment

Dynamic WP-SiteURL

Previously posted was a way to have a dynamic base_url in Codeigniter. This same methodology was applied to WordPress. We had to run multiple installs of WP due to svn and databases. Here is the results.

/* That's all, stop editing! Happy blogging. */
/** Absolute path to the WordPress directory. */
if ( !defined('ABSPATH') )
define('ABSPATH', dirname(__FILE__) . '/');
/* THIS IS CUSTOM CODE CREATED AT ZEROFRACTAL TO MAKE SITE ACCESS DYNAMIC */
$currenthost = "http://".$_SERVER['HTTP_HOST'];
$currentpath = preg_replace('@/+$@','',dirname($_SERVER['SCRIPT_NAME']));
$currentpath = preg_replace('/\/wp.+/','',$currentpath);
define('WP_HOME',$currenthost.$currentpath);
define('WP_SITEURL',$currenthost.$currentpath);
define('WP_CONTENT_URL', $currenthost.$currentpath.'/wp-content');
define('WP_PLUGIN_URL', $currenthost.$currentpath.'/wp-content/plugins');
define('DOMAIN_CURRENT_SITE', $currenthost.$currentpath );
@define('ADMIN_COOKIE_PATH', './');

I know WordPress has many issues with security when the url is being pulled dynamically. If anyone sees any flaws in the code, please bring them to my attention.

Thanks.

Posted in PHP, Wordpress | Leave a comment

Dynamic CodeIgniter base_url()

While at work, we were brainstorming of how to come up with a way to have a dynamic url based on a company project we were working on.

Why must the url or a Codeigniter project need to be dynamic you as? Well the reason being we had to have the project under source control and computer was setup a little different whether its MAMP putting the port number in the url or people checking out different sections of the project (some trunk, others source). So the url's were different depending on these variables.

Here is the code snippet we came up with to to dynamically replace the base url function.

	$config['base_url'] = "http://".$_SERVER['HTTP_HOST'];
	$config['base_url'] .= preg_replace('@/+$@','',dirname($_SERVER['SCRIPT_NAME'])).'/';

Enjoy!

Let me know if you guys have any other solutions.

Posted in CodeIgniter, PHP | Leave a comment

To Reinvent the Wheel or Not?!

My New Relationship with WordPress….

Honestly, I think me and WordPress just got off on the wrong foot. I was bitter about the fact of using wordpress as a CMS or just using WordPress in general.

All until I went to a local WordCamp in Miami. I feel like I had the wrong impression of WordPress. Ultimately I misunderstood it.

Now that I have seen the light (thanks to the developers at wordcamp), I have chosen WordPress to do the heavy lifting for my blog. A couple of other CMS‘s caught my eye, possibly make my own (not to reinvent the wheel ),but just to the popularity of wordpress seemed like a great way to boost my skills by modifying my own WordPress theme.

Now that I have my 2cents out of the way, lets get into the good stuff.

Posted in Stuff | Leave a comment

Enabling Thumbnails with Posts in WP

After searching around on the internet for how to enable thumbnails on post, I can across a lot of out dated information. Most of the things that I encountered dealt with earlier versions of wordpress. After trying several different methods and more research about each method, why it did and didn’t work, I finally succeeded using this function and I’m going to show you how I did it.

I am using the Thematic framework for my custom theme. If you have never used it and you work with wordpress creating themes, IT’S A MUST!!

The folder and file structure is a bit different, so your process may be a little more straight forward than mine, but the idea is the same.

  1. Enable support within your theme
  2. Place the code within the loop to output the image
  3. Set a featured image for the Post
  4. Style it to your delight

Here is how it’s done.

Inside the themes folder go to (if your not using thematic, just find the loop that outputs the post):

thematic/library/extensions/content-extensions.php

Around line 450 insert the following the if statement inside the entry-content div (line 451)

<div  class="entry-content">
<?php if ( function_exists("has_post_thumbnail") && has_post_thumbnail() ) { the_post_thumbnail(array(200,160), array("class" => "post_thumbnail")); } ?>
<?php the_excerpt(); ?>

the breakdown:

  • function_exists(“has_post_thumbnail”) check to see if the function exist
  • has_post_thumbnail() runs the function to see if there is any results
  • the_post_thumbnail(array(200,160) sets a with and height for the thumbnail (adjust as necessary)
  • array(“class” => “post_thumbnail”) specifies a class to attach to the thumbnail for styling

I also changed ‘the_content();’ to ‘the_excerpt();’. This only displays an excerpt on the home page instead of the entire post.

With that one line of code, you have now taken care of 1 and 2 , enabled support and out the image!

Now for 3 just add a new post and at the bottom right add the featured image for the post.

As far as 4 goes, it’s a personal preference. Mine just floated left with some margin bottom.

Enjoy!!

Posted in Wordpress | Leave a comment