Wednesday, May 10, 2017

Old Sketches

based on Kenan Yarar drawings


leman/l-manyak characters

kenan yarar/bahadır baruter

unfinished cem karaca

bahadır baruter

Saturday, June 9, 2012

UITableView Smooth Scrolling

In this post, I will try to show a step by step tutorial on how to implement a UITableView with smooth scrolling. We will build a simple twitter client. Our final application will be like this :
Final Application
Final Application
Our final application will be consisted of a UISearchBar and UITableView. Also as you can notice the UITableView will have dynamic row height and rounded images. Although it seems an ordinary application we will draw all the text and images manually using drawRect method of UIView which is the key point for having a smooth scrolling UITableView.

Creating the Project

We will start with the empty project template.


Now we have an empty project, we will add a new storyboard file for the UI and name it "Main". Then we need to add a storyboard file and name it "Main".




To make this storyboard file as our main UI interface we will make two changes. First we need to set the "Main StoryBoard" settings of the Targets section.  Write the name of our storyboard file in the Main Storyboard field.


Second we need the comment out the window creation code in the AppDelegate.m file.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
   /* self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];*/
    return YES;
}
because the storyboard will create the window for us automatically. Now we can add UI elements to our storyboard file. Our application will consist of a search field and a UITableView.
  • First add a View Controller
  • Second add an Table View
  • Third add an Search Bar to the Table View Element

Now add a subclass of UIViewController to manage the UITableView and SearchBar using File-> New->File Menu. We will call it MainViewController.


After adding MainViewController file switch to storyboard and change the underlying class of the ViewController to "MainViewController" in the identity inspector section.

Adding Third Party Libraries
We will use three third party libraries

you can find detailed instructions on how to add these libraries to your projects in the provided links above. Create a new group under your project folder called Libraries and add the third party libraries to this group to keep things organized.

Now in order to handle the UITableView and UISearchBar events we will add two class called TableHelper and SearchBarHelper. Usually all the delegate methods for UIView controls are kept inside one container UIViewController but I think it is much easier to have different classes for each UIView controls.
Now let's look at SearchHelper implementation :

SearchBarHelper for UISearchBar Delegate Methods

In order to handle UISearchBar delegate methods our custom class will implement UISearchBarDelegate protocol and we will use
- (void)searchBarTextDidBeginEditing:
- (void)searchBarSearchButtonClicked:
delegate methods. Here is the SearchBarHelper.h and SearchBarHelper.m
#import <Foundation/Foundation.h>
@interface SearchBarHelper : NSObject<UISearchBarDelegate>
-(void)hideOverlay;
@end

#import "SearchBarHelper.h"
#import "Helper.h"
#import "MBProgressHUD.h"

@interface SearchBarHelper ()
{
    UIView* overlayView;
    UISearchBar *m_searchBar;
}
@end

@implementation SearchBarHelper

#pragma mark optional methods

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
    [self hideOverlay];
    
    MBProgressHUD *hud =  [MBProgressHUD showHUDAddedTo:searchBar.superview animated:YES];
    hud.userInteractionEnabled = NO;
    hud.labelText = @"Loading";
    hud.dimBackground = YES;
    [[Helper sharedInstance] bindData:searchBar.text CallBack:^{
        [MBProgressHUD hideHUDForView:searchBar.superview animated:YES];
    }];
}

- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar{
    
    if (!m_searchBar) {
        m_searchBar = searchBar;
    }
    
    if (!overlayView) {
        CGRect tableViewRect = [searchBar superview].bounds;
        CGRect searchBarRect = searchBar.bounds;
        CGRect overlayRect = CGRectMake(0, searchBarRect.size.height, searchBarRect.size.width, tableViewRect.size.height);
        overlayView = [[UIView alloc] initWithFrame:overlayRect];
        overlayView.alpha = 0;
        overlayView.backgroundColor = [UIColor blackColor];
        
        [overlayView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideOverlay)]];
        
    }
    [UIView animateWithDuration:0.4 animations:^{
        overlayView.alpha = 0.7;
        [searchBar.superview addSubview:overlayView];
    }];
}
-(void)hideOverlay{
    [m_searchBar resignFirstResponder];
    [overlayView removeFromSuperview];
    overlayView.alpha = 0;
}
@end
it is a pretty simple implemantation. In the searchBarTextDidBeginEditing we save the calling UISearchBar into a variable. We could also use an IBoutlet for this but by doing saving a reference without using an IBoutlet we make the SearchBarHelper more flexible. Also we lazily create an UIView instance when user starts typing. After user hit the search button we call a helper method to download and parse the Json data which we will as a data source UITableView. Now let's look at the Helper class that contains helper methods for json download & parsing, image downloading and image processing.

Helper class

We will use a singleton class called Helper which will wrap task for json parsing, calculating boundaries for dynamic row height and rounded image borders. Here is the Helper class. Helper.h
#import <Foundation/Foundation.h>
#import "AFImageRequestOperation.h"
#import "AFJSONRequestOperation.h"
#import "AFNetworking/AFHTTPClient.h"
#import "Twit.h"
#import "MBProgressHUD.h"

typedef void (^ImageCompletionBlock)(UIImage* image, NSError *error);
typedef void (^JSonCompletionBlock)(id json, NSError *error);
typedef void (^CallbackBlock)(void);

@interface Helper : NSObject

@property (nonatomic, strong) NSMutableArray* data;

+(id)sharedInstance;
-(void) requestImageWithUrl:(NSString*)url CallBack:(ImageCompletionBlock)callBack;
-(void) requestJsonWithUrl:(NSString*)url CallBack:(JSonCompletionBlock)callBack;
-(void) bindData:(NSString*)searchKey CallBack:(CallbackBlock) callBack;
-(UIImage*) roundCorneredImage: (UIImage*) orig radius:(CGFloat) r;
@end
#import "Helper.h"
#import "Constants.h"

@interface Helper()
@property (nonatomic, strong) NSCache *sharedCache;
-(void) initializeReachability;
@end

@implementation Helper
@synthesize data = _data;
@synthesize sharedCache = _sharedCache;

static Helper* m_helper;

-(void) initializeReachability{
    
    UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
    
    [[AFHTTPClient clientWithBaseURL:[NSURL URLWithString:@"http://www.google.com"]]
     setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
         if (status == AFNetworkReachabilityStatusNotReachable) {
             dispatch_async(dispatch_get_main_queue(), ^{
                 MBProgressHUD*  hud= [MBProgressHUD showHUDAddedTo:window
                                                           animated:YES];
                 hud.mode = MBProgressHUDModeText;
                 hud.dimBackground = YES;
                 hud.labelText = @"No active internet connection";
             });
         }
         else {
             dispatch_async(dispatch_get_main_queue(), ^{
                 [MBProgressHUD hideAllHUDsForView:window animated:NO];
             });
         }
     }];
}

+(void)initialize{
    if (self == [Helper class] && m_helper == nil) {
        m_helper = [[Helper alloc] init];
        m_helper->_sharedCache = [[NSCache alloc] init];
        [m_helper initializeReachability];
    }
}
+(id)sharedInstance{
    return m_helper;
}

-(void)requestImageWithUrl:(NSString *)stringUrl CallBack:(ImageCompletionBlock)callBack{
    UIImage *img = [self.sharedCache objectForKey:stringUrl];
    if (img) {
        callBack(img, nil);
    }
    else {
        NSURL *url = [NSURL URLWithString:[stringUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
        NSURLRequest* request = [NSURLRequest requestWithURL:url];
        
        AFImageRequestOperation *imageRequest = [AFImageRequestOperation
                                                 imageRequestOperationWithRequest:request
                                                 imageProcessingBlock:^UIImage *(UIImage *image) {
                                                     return  [self roundCorneredImage:image radius:IMAGE_BORDER];
                                                     
                                                 } success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
                                                     [self.sharedCache setObject:image forKey:stringUrl];
                                                     callBack(image, nil);
                                                 } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
                                                     callBack(nil, error);
                                                 }];
        [imageRequest start];
    }
}

-(void)requestJsonWithUrl:(NSString *)stringUrl CallBack:(JSonCompletionBlock)callBack{
    NSURL *url = [NSURL URLWithString:[stringUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    AFJSONRequestOperation *jsonRequest = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        callBack(JSON, nil);
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
        callBack(nil, error);
    }];
    
    [jsonRequest start];
}

-(void) bindData:(NSString *)searchKey CallBack:(CallbackBlock)callBack{
    
    NSString *url = [NSString stringWithFormat:SEARCH_URL, [searchKey stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
    
    [self requestJsonWithUrl:url CallBack:^(NSDictionary *json, NSError *error) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSMutableArray *dataArray = [[NSMutableArray alloc] init];
            NSArray *results = [json objectForKey:@"results"];
            float cellWidth = [UIScreen mainScreen].bounds.size.width;
            float textContentWidth = cellWidth - (IMAGE_SIZE + 3 * MARGIN);
            float textStartIndex = IMAGE_SIZE + MARGIN * 2.0f;
            
            for (NSDictionary *item in results) {
                Twit *twit = [[Twit alloc] init];
                twit.id = [item objectForKey:@"id"];
                twit.created_at = [item objectForKey:@"created_at"];
                twit.from_user = [item objectForKey:@"from_user"];
                twit.from_user_name = [item objectForKey:@"from_user_name"];
                twit.profile_image_url = [[item objectForKey:@"profile_image_url"] stringByReplacingOccurrencesOfString:@"normal" withString:@"bigger"];
                twit.text = [item objectForKey:@"text"];
                
                CGSize sizeTitleFrame = [twit.from_user_name sizeWithFont:[UIFont boldSystemFontOfSize:FONT_SIZE_SMALL] constrainedToSize:CGSizeMake(textContentWidth, FONT_SIZE_SMALL) lineBreakMode:UILineBreakModeHeadTruncation];
                
                twit.rectTitleFrame = CGRectMake(textStartIndex, MARGIN, sizeTitleFrame.width, sizeTitleFrame.height + MARGIN);
                
                
                CGSize sizeTextFrame = [twit.text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE_BIG] constrainedToSize:CGSizeMake(textContentWidth, 20000) lineBreakMode:UILineBreakModeWordWrap];
                twit.rectTextFrame = CGRectMake(textStartIndex, twit.rectTitleFrame.size.height + MARGIN, sizeTextFrame.width, sizeTextFrame.height);
                
                twit.cellHeight = MAX(twit.rectTitleFrame.size.height + twit.rectTextFrame.size.height + 2 * MARGIN, textStartIndex);
                [dataArray addObject:twit];
            }
            
            self.data = dataArray;
            
            dispatch_async(dispatch_get_main_queue(), ^{
                callBack();
            });
        });
    }];
}

- (UIImage*) roundCorneredImage: (UIImage*) orig radius:(CGFloat) r {
    
    UIGraphicsBeginImageContextWithOptions(orig.size, NO, [UIScreen mainScreen].scale);
    [[UIBezierPath bezierPathWithRoundedRect:(CGRect){CGPointZero, orig.size}
                                cornerRadius:r] addClip];
    [orig drawInRect:(CGRect){CGPointZero, orig.size}];
    UIImage* result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return result;
}

@end
  • The "Helper" class has a class property called sharedInstance which will be used across the application to get a reference to the singleton instance.
  • The initializeReachability instance method notify user about the network changes
  • The requestJsonWithUrl:CallBack: method downloads the json request and fires a callback block on success or failure
  • The requestImageWithUrl:CallBack: methods downloads the image and fires a callback block on success or failure
  • The bindData:CallBack: method loop over the supplied array data and creates a twit object and calculates the boundaries of for the text, image and user name.
We use a model class for the twits. Here ise the Twit object.

#import <Foundation/Foundation.h>

@interface Twit : NSObject

@property (nonatomic, strong) NSString *id;
@property (nonatomic, strong) NSString *created_at;
@property (nonatomic, strong) NSString *from_user;
@property (nonatomic, strong) NSString *from_user_name;
@property (nonatomic, strong) NSString *profile_image_url;
@property (nonatomic, strong) NSString *text;
@property (nonatomic) float  cellHeight;
@property (nonatomic) CGRect rectTextFrame;
@property (nonatomic) CGRect rectTitleFrame;
@property (nonatomic, strong) NSString *profileImgUrl;
@property (nonatomic, strong) UIImage *profileImg;

@end


#import "Twit.h"

@implementation Twit

@synthesize id;
@synthesize created_at;
@synthesize from_user;
@synthesize from_user_name;
@synthesize profile_image_url;
@synthesize text;
@synthesize profileImgUrl;
@synthesize profileImg;
@synthesize cellHeight;
@synthesize rectTextFrame;
@synthesize rectTitleFrame;
@end

Custom UITableViewCell and custom UIView classes

We need to implement a custom UITableViewCell which will contain a custom UIView that we will draw our cell content manually in draw. Here is the TwitCell implementation.
TwitCell.h
#import <UIKit/UIKit.h>
#import "TwitView.h"
#import "Twit.h"

@interface TwitCell : UITableViewCell
@property (nonatomic, strong) Twit *modelData;
@end
TwitCell.m
#import "TwitCell.h"

@interface TwitCell ()
{
    TwitView *_customView;
}
@end

@implementation TwitCell
@synthesize modelData = _modelData;

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        CGSize bounds = self.contentView.bounds.size;
        self.opaque = YES;
        _customView = [[TwitView alloc] initWithFrame:CGRectMake(0, 0, bounds.width, bounds.height)];
        _customView.backgroundColor = [UIColor whiteColor];
        
        [self.contentView addSubview:_customView];
    }
    return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

-(void)setModelData:(Twit *)modelData{
    _modelData = modelData;
    _customView.modelData = modelData;
}

@end
In the init method of TwitCell class a custom UIView class is initialized which we will draw our cell content into. Also there is a modelData property which is an instance of Twit class. Here is the custom UIView class that we will our cell content into.
TwitView.h
#import <UIKit/UIKit.h>
#import "Twit.h"

@interface TwitView : UIView

@property (nonatomic, strong) Twit* modelData;
@end
TwitView.m
#import "TwitView.h"
#import "Constants.h"

@implementation TwitView
@synthesize modelData = _modelData;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

- (void)drawRect:(CGRect)rect
{
    [_modelData.from_user_name drawInRect:_modelData.rectTitleFrame withFont:[UIFont boldSystemFontOfSize:FONT_SIZE_SMALL]];
    [_modelData.text drawInRect:_modelData.rectTextFrame withFont:[UIFont systemFontOfSize:FONT_SIZE_BIG]];
    [_modelData.profileImg drawInRect:CGRectMake(MARGIN, MARGIN, IMAGE_SIZE, IMAGE_SIZE)];
    
}

-(void)setModelData:(Twit *)modelData{
    _modelData = modelData;
    self.frame = CGRectMake(0, 0, self.frame.size.width, modelData.cellHeight);
    [self setNeedsDisplay];
}
@end
and here is the constant.h file that are the macros used in the TwitterView class.
Constant.h
#ifndef News_Constants_h
#define News_Constants_h

#define MARGIN              10
#define IMAGE_SIZE          60
#define FONT_SIZE_BIG       14
#define FONT_SIZE_SMALL     11
#define IMAGE_BORDER        5
#define SEARCH_URL @"http://search.twitter.com/search.json?q=%@&rpp=100&include_entities=false&result_type=recent"

#endif

UITableViewDelegate implemantation : TableHelper

Here is TableHelper class that we will create our custom drawn UITableViewCell. Also we will also override the -(void)observeValueForKeyPath:ofObject:change: which will tell the UITableView to reload its data.
TableHelper.h
#import <Foundation/Foundation.h>

@interface TableHelper : NSObject<UITableViewDataSource, UITableViewDelegate>
@end
TableHelper.m
#import "TableHelper.h"
#import "Twit.h"
#import "TwitCell.h"
#import "Helper.h"
@interface TableHelper ()
{
    NSMutableArray *data;
    UITableView *m_tableView;
    Helper* m_helper;
}
@end

@implementation TableHelper

#pragma mark optional UITableView method

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    Twit *twit = [data objectAtIndex:indexPath.row];
    return twit.cellHeight;
}

#pragma mark required UITableView method

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    static NSString *cellIdentifier = @"customcell";
    
    TwitCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    
    if (cell == nil) {
        cell = [[TwitCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
    }
    Twit *modelData = [data objectAtIndex:indexPath.row];
    
    if (!modelData.profileImg) {
        
        [m_helper requestImageWithUrl:modelData.profile_image_url CallBack:^(UIImage *image, NSError *error) {
            TwitCell* cell = (TwitCell*)[tableView cellForRowAtIndexPath:indexPath];
            Twit *modelData = [data objectAtIndex:indexPath.row];
            modelData.profileImg = image;
            cell.modelData = modelData;
        }];
    }
    cell.modelData = modelData;
    return cell;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    if (!m_tableView) {
        m_tableView = tableView;
    }
    return [data count];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    if (!m_helper) {
        m_helper = object;
    }
    data = [change objectForKey:NSKeyValueChangeNewKey];
    [m_tableView reloadData];
}

@end

Connecting The Delegates

Here is the custom UIViewController that will connect the UISearchBar and UITableView.
UIViewController.h
#import <UIKit/UIKit.h>
#import "SearchBarHelper.h"
#import "TableHelper.h"

@interface MainViewController : UIViewController
@property (strong, nonatomic) IBOutlet SearchBarHelper *searchBarDelegate;
@property (strong, nonatomic) IBOutlet TableHelper *tableViewDelegate;

@end
UIViewController.m
#import "MainViewController.h"
#import "Helper.h"

@implementation MainViewController
@synthesize searchBarDelegate;
@synthesize tableViewDelegate;

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [[Helper sharedInstance] addObserver:tableViewDelegate forKeyPath:@"data" options:NSKeyValueObservingOptionNew context:nil];
}

- (void)viewDidUnload
{
    [self setSearchBarDelegate:nil];
    [self setTableViewDelegate:nil];
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

@end
Here is the final result :

You can find the project files here
Hope this helps...

Saturday, June 2, 2012

Asp.Net Mvc : Ajax with jQuery

I have been using Asp.Net Mvc framework for quite some time and really love it. It is easy to make ajax stuff is very easy but I think it is even easier if you use jQuery. In this post I will cover
  • How to make partial update
  • How to make a Get request with additional parameters in query string
  • How to make a Post request with additional paremeters inside request body
  • What happens if javascript is disabled or the user using an older browser

Menu Interface

The menu items in the master page will be as below ;
<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
   <title>
       <asp:ContentPlaceHolder ID="TitleContent" runat="server" />
   </title>
   <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
   <div class="page">
       <div id="header">
           <div id="title">
               <h1>
                   Asp.Net Mvc & jQuery Demo</h1>
           </div>
           <div id="logindisplay">
               <% Html.RenderPartial("LogOnUserControl"); %>
           </div>
           <div id="menucontainer">
               <ul id="menu">
                   <li>
                       <%= Html.ActionLink("Home", "Index", "Home")%></li>
                   <li>
                       <%= Html.ActionLink("Html", "TestMethod", "Home")%></li>
                   <li>
                       <%= Html.ActionLink("Partial Update", "TestMethod", "Home",
                                       new {name = "cem", surname = "karaca"},
                                       new { @class = "html" })%>
                   </li>
                   <li>
                       <%= Html.ActionLink("Json (GET)", "TestMethod", "Home",
                                       new { name = "yasin", surname= "tarim"},
                                       new { @class = "jsonGET" })
                       %>
                   </li>
                   <li>
                       <%= Html.ActionLink("Json (POST)", "TestMethod", "Home",
                                       new { name = "yasin", surname= "tarim"},
                                       new { @class = "jsonPOST" })
                       %>
                   </li>
                   <li>
                       <%= Html.ActionLink("About", "About", "Home")%></li>
               </ul>
           </div>
       </div>
       <div id="main">
           <asp:ContentPlaceHolder ID="MainContent" runat="server" />
           <div id="footer">
           </div>
       </div>
   </div>
 
   <script type="text/javascript" src="../../Scripts/jquery-1.4.1.min.js"></script>
 
   <script type="text/javascript" src="../../Scripts/Helper.js"></script>
 
</body>
</html>
As seen in the highlighted sections instead of using Ajax.ActionLink I will use Html.ActionLink with custom class attributes specifying what kind of request will be invoked by the jQuery ajax method. All the menu invokes TestMethod action method in the HomeController class. We need to add the jquery library and a custom javascript file containing helper methods. Here is the Helper.js
/// <reference path="jquery-1.4.1.min-vsdoc.js" />
//Helper object for making request and updating view
var HELPER = (function () {
    var methods = {};
    methods.updateView = function (data) {
        //locate the object that will be updated
        //which is inside div with id 'main'
        var $placeHolder = $("#main > :not(#footer)"),
            html, i, l, item;
        //if data is a string object simply update
        //view with data
        if (typeof data === "string") {
            $placeHolder.replaceWith(data);
            return;
        }
        //if data is a javascript object with additional
        //properties loop over the properties for each item
        //and create an html ul element
        html = ['<ul>'];
        for (i = 0, l = data.length; i < l; i++) {
            html.push("<li>");
            for (item in data[i]) {
                if (data[i].hasOwnProperty(item)) {
                    html.push("<span><b>" + item + "</b> : " + data[i][item] + "</span></br>");
                }
            }
            html.push("</li>");
        }
        html.push("</ul>");
        $placeHolder.replaceWith(html.join(''));
    };
    methods.makeRequest = function (obj) {
        $.ajax({
            type: obj.type || "POST",
            url: obj.url,
            data: obj.data || " ",
            contentType: "application/{contentType}; charset=utf-8".
            replace("{contentType}", obj.contentType || "json"),
            dataType: obj.contentType || "json",
            beforeSend: obj.beforeSend ||
            function (xhr) {},
            success: obj.success ||
            function (msg) {},
            error: obj.error ||
            function (msg) {}
        });
    };
    return {
        makeRequest: methods.makeRequest,
        updateView: methods.updateView
    };
}());

$(function () {
    //bind the click event to menu div
    //catch all the click event from anchor element
    $("#menu").click(function (e) {
        if (e.target.nodeName === "A") {
            var trg = e.target,
                url = trg.href,
                $trg = $(trg);

            //if the anchor element has html class
            //make a http post request for a partial update
            if ($trg.hasClass("html")) {
                HELPER.makeRequest({
                    type: "POST",
                    contentType: "html",
                    url: url,
                    success: function (arg) {
                        HELPER.updateView(arg);
                    }
                });

            }
            // if the anchor element has jsonPost class
            //make a http post request and send additional
            //parameters within request body
            else if ($trg.hasClass("jsonPOST")) {
                HELPER.makeRequest({
                    type: "POST",
                    contentType: 'json',
                    data: {
                        age: '28',
                        songs: ['Namus Belasi', 'Tamirci Ciragi', 'Parka'],
                        group: 'Dervisan'
                    },
                    url: url,
                    success: function (arg) {
                        HELPER.updateView(arg);
                    }
                });

            }
            // if the anchor element has jsonPost class
            //make a http GET request and send additional
            //parameters within request url
            else if ($trg.hasClass("jsonGET")) {
                HELPER.makeRequest({
                    type: "GET",
                    contentType: 'json',
                    data: {
                        age: '30',
                        song: 'Unutamadigim'
                    },
                    url: url,
                    success: function (arg) {
                        HELPER.updateView(arg);
                    }
                });

            }
            return false;
        }
    });
});

Updating View and Making Ajax Calls with jQuery

The Helper javascript object has two helper methods which are MakeRequest and UpdateView. The MakeRequest method accepts an object which has additional properties and functions like url, data,contentType. The second method UpdateView simply updates the area within the element with id main. But because the main div also contains the footer div we simply filter the footer div and then update the rest of the main with a simple jquery selector.

The jQuery selector is :
$("#main > :not(#footer)");
as higlighted in line 8.
The MakeRequestmethod of the helper class when the document loaded. After the document loaded we bind a click event to the menu div (id = menu). So :
    <>liWhenever a click event occurs inside the menu div we check the target if it is an anchor element.
  • If it is an anchor tag we check it is class properties
  • if the anchor element has html class we invoke an Http Post request. if it has jsonGet class we invoke an Http Get and send extra data within the querystring, if it has jsonPost class we incoke an Http Post and send extra data within the request body.
When success callback functions are called it pass the recevied response to the UpdateView method in the Helper object. After the UpdateView item receives the input it checks the type of the argument. If it is a string the method just updates the view with the passed item. If the received input is an object the method iterates over the properties of the object and generates an ul element and updates the view with this ul. And finally here is the TestMethod ActionMethod that will handle the incoming request.
public ActionResult TestMethod(string name, string surname)
       {
           if (Request.IsAjaxRequest()) // Check if it is an ajax request
           {  
               //check if the response is requested in json format
               if (Request.ContentType.Contains("application/json"))
               {
                  //check if the request is made using HTTP GET or HTTP POST
                   string httpMethod = Request.HttpMethod;
 
                   if (httpMethod == "GET")
                   {
                       //Get additional parameters added with jQuery from QueryString
                       var collection = Request.QueryString;
 
                       return Json(collection.Cast<string>().
                                   Select(p => new { Key = p, Value = collection[p]}),
                           JsonRequestBehavior.AllowGet);
                   }
                   else if (httpMethod == "POST")
                   {
                       //Get additional parameters from Request body
                       using (var sr = new StreamReader(Request.InputStream))
                       {
                           //fetch the request body into a string variable
                           var requestBody = sr.ReadToEnd();
 
                           //create a name-value pair object from request body
                           var collection = HttpUtility.ParseQueryString(requestBody);
 
                           //return Json Message
                           return Json(collection.Cast<string>().
                               Select(p => new { Key = p, Value = collection[p] }));
                       }
                   }
               }
 
               //response format is not json so return partial view in html format
               //pass a string as the model data
 
               return PartialView("SimplePartialView",
                   "This message is generated from partial view in html format".Split(' '));
 
           }
           else
           {
               //request is not made by XmlHttpRequest object
               //return normal view
               
               return View("The request is made without ajax".Split(' '));
           }
       }
Now when we receive the request
  • First we check if it is an ajax request if it is not we simply pass the model object to the TestMethod View
  • If the request is an ajax request we check the response type that the user requested if the contentType is html we render a partialview names SimplePartialView.
  • If it an ajax request with contentType of json we check the HttpMethod of the request. If the httpmethod is GET we read posted values (which also contains the ones that are added via jQuery besides the ones in the request url as in the masterpage) and send it back to the client in json format. If the http method is POST we read the additional data from the request body and send them to the client on json format.
Of course you can have an additional actionmethod that will receive only the request made with Http Post using the HttpPost attribute like below:
[HttpPost]
public ActionResult TestMethod()

What happens if the user has disabled javascript or using an older browser

Our application will still be running. Because we used Html.ActionLink instead of Ajax.ActionLink if the javascript is disabled our jQuery code will not be called. So for example if the user click Json(Post) link it will receive an html response instead of json because we check if the request is an ajax request in TestMethod with the isAjaxRequest extension method and if it is not we simply render the TestMethod view. Here is the TestMethod view TestMethod.aspx
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage" %>
 
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
TestMethod
</asp:Content>
 
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
 
   <% Html.RenderPartial("SimplePartialView"); %>
 
</asp:Content>
and here is the SimpleViewPartialView.ascx view:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<string>>" %>
 
<% foreach (var item in Model){ %>
   <li>
      <%= item %>
   </li>
 <% } %>

Hope it helps.

Tuesday, May 29, 2012

How to install syntax highlighter on Blogger Dynamic Views

The new blogger dynamic view template does not allow to change html template so it does not allow you to add custom script. So in order to use syntax highlighter on blogger dynamic view pages we need to insert necessary scripts and style sheets manually. I wrote a small script for this task. It need to be added at the end of every post. It first checks if SyntaxHighlighter is available on the page if so the "higlight" method is called. If SyntaxHighlighter is not available the script starts to load the necessary scripts and stylesheets provided by the arr variable (you can add another brush or stylesheet to the array to be included to the page). The script load all items serially using a callback function. The callback function is called on the onload of the script element but in order to have a callback after a stylesheet is loaded we need use a small trick (as detailed here). We create an image element and set its src attribute to the stylesheets source. The img elements tries to load by using its src value and fails and trigger the onerror event and we use the onerror event for the callback function. While the script need to be included for every post it loads the resources only once by a setting a global variable (window.SyntaxHighlighterLoading). So in order to make this work put the script below in a js file then add that script file at the end of every post. Sample usage :
<script src='https://raw.github.com/Yasintrm/js/master/loader.js'></script>
Hope it helps.
(function () {
    var arr, counter = 0,
        doc = document,
        len, load, callBack, url = 'http://alexgorbatchev.com/pub/sh/current/';
  
    if (window.SyntaxHighlighter && !window.SyntaxHighlighterLoading) {
        SyntaxHighlighter.highlight();
    } else if (!window.SyntaxHighlighterLoading) {
        window.SyntaxHighlighterLoading = true;
        arr = [url + 'scripts/shCore.js',
               url + 'scripts/shBrushCSharp.js',
               url + 'scripts/shBrushJScript.js',
               url + 'styles/shCore.css',
               url + 'scripts/shBrushXml.js',
               'https://raw.github.com/scottdensmore/ObjectiveCSyntaxHighlighter/master/scripts/shBrushObjC.js',
               url + 'styles/shCoreDefault.css'];
      
        len = arr.length;
      
        load = function (src, callBack) {
            var isJs = src.lastIndexOf('.js') !== -1,
                head = doc.getElementsByTagName('head')[0],
                el, img;

            if (isJs) //load javascript
            {
                el = doc.createElement('script');
                el.type = 'text/javascript';
                el.src = src;
                el.onload = callBack;
                head.appendChild(el);
            } else //load css
            {
                el = doc.createElement('link');
                el.href = src;
                el.type = 'text/css';
                el.rel = 'stylesheet';
                head.appendChild(el);
                //not all browser support onload event on link elements - for example safari mac 5.1.7--
                //in order to simulate load event on link elements use a known trick
                img = doc.createElement('img');
                img.onerror = callBack;
                img.src = src;
            }
        };
      
        callBack = function () {
            if (++counter === len) {
                SyntaxHighlighter.highlight();
                window.SyntaxHighlighterLoading = false;
            } else {
                load(arr[counter], callBack);
            }
        };
      
        load(arr[counter], callBack);
    }
}());

Hope it helps.

Monday, May 28, 2012

Asp.net Ajax : How to send complex objects from javascript


Asp.net Ajax : How to send complex objects from javascript

In this post I will show :
  • How to call Asp.Net Page Methods using jQuery
  • How to call page method with multiple arguments
  • How to send complex objects with jQuery
  • How to handle different types of objects within the same page method
  • How to Return Complex Objects from Page Method

How to call Asp.Net Page Methods using jQuery

First let's define a simple page method that will handle the requests.
[System.Web.Services.WebMethod]
public static string TestMethod(string data)
{
  return "Data received at " + DateTime.Now.ToString();
}
now we can use the $.ajax method of jQuery to make calls to the page method we defined. Lets look how can we send a simple string:
// send a simple string
var data2Send = '{"data":"this is a simple string"}';
$.ajax({
    type: "POST",
    url: 'Default.aspx/Testmethod',
    data: data2Send,
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    success: function (arg) { //call successfull
    },
    error: function (xhr) {
        //error occurred
    }
});

There are two important rule here : First we define a simple page method to make requests to. now we can use the $.ajax method of jQuery to make calls to the page method we defined. Lets look how can we send a simple string:

we see that firebug already show the parameter as a json value. If the data is not a valid json object Asp.Net will throw an exception
{"Message":"Invalid JSON primitive:","StackTrace":..."
Second the parameter name of the Asp.Net Page Method must be same with the property name of json object we send to the server.

If parameter names are different Asp.Net will throw another exception. For example if we send
{"test":"this is a simple string"}
json object to TestMethod we will receive the exception below :
{"Message":"Invalid web service call, missing value for parameter:data"}
which states the page method was waiting for a json argument with a property named data but could not find one. On the other hand if we send a json object containing multiple property, Asp.Net will check the parameter to see if it contains a property with the same name as its parameter name. In our example it will search the object for a property name data. So if we send
{"test":"this is a simple string","data":"test"}
our page method will pick the 'data' property and will not take other properties of the json object into consideration.

How to call page method with multiple arguments

if we want to call a page method with multiple arguments all we need to construct a json object with the same property names as the parameter names of the page method. Say we have an Asp.Net page method like this :
[System.Web.Services.WebMethod]
public static string TestMethod(string data, string arg)
{
   return "Data received at " + DateTime.Now.ToString();
}

Now in order to call this page method from javascript first we need to define a json object with properties named "data" and "arg" then make a request with "jQuery.ajax method".
// send an object with multiple parameters
var data2Send = '{"data":"this is a simple string", "arg":"hello"}';
 
$.ajax({
    type: "POST",
    url: 'Default.aspx/Testmethod',
    data: data2Send,
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    success: function (arg) { //call successfull
    },
    error: function (xhr) {
        //error occurred
    }
});

Here is the result :

How to send complex objects with jQuery

After some basic information about how can we send simple types to page methods with jQuery now its time to see how we can send complex objects. First lets define a relatively complex javascript object.
var data = {
    name: 'Cem',
    surname: 'Karaca',
    birthDate: '05.04.1945',
    groups: [{
        name: 'Kardaslar',
        Year: 1970,
        members: ['Unol Buyukgonenc', 'Seyhan Karabay', 'Huseyin Sultanoglu', 'Fehiman Ugurdemir']
    },
    {
        name: 'Dervisan',
        Year: 1974,
        members: ['Ugur Dikmen', 'Sefa Ulastir', 'Hami Barutcu']
    }],
    links: ['http://en.wikipedia.org/wiki/Cem_Karaca', 'http://progressive.homestead.com/Cem_Karaca.html']
};

Now first we need to convert this javascript object to an json object. Although we can do that without using an additional library its better to use one as it turns out that converting manually can be error prone.
If you use Asp.Net ajax library there are already two methods that deal with javascript serialization. You can serialize and deserialize javascript objects with Asp.Net Ajax library with the following code :
var data = {
    name: 'cem',
    surname: 'karaca'
}
//serialize object to json
var jsonData = Sys.Serialization.JavaScriptSerializer.serialize(data);
 
//deserialize json to javascript object
var obj = Sys.Serialization.JavaScriptSerializer.deserialize(jsonData);

The other option is to use Dougles Crockford's json library. Here how we can serialize and deserialize objects with this library
var data = {
    name: 'cem',
    surname: 'karaca'
}
//serialize object to json
var jsonData = JSON.stringify(data);
 
//deserialize json to javascript object
var obj = JSON.parse(jsonData);

I will use second library for the example. First lets define another page method to handle the complex type that we will send using jQuery.
[System.Web.Services.WebMethod]
public static string ComplexTypes(object data)
{
  return "Data received at " + DateTime.Now.ToString();
}

and here is the javascript part of the code
// construct a complex object
var complexObj = {
    name: 'Cem',
    surname: 'Karaca',
    birthDate: '05.04.1945',
    groups: [{
        name: 'Kardaslar',
        Year: 1970,
        members: ['Unol Buyukgonenc', 'Seyhan Karabay', 'Huseyin Sultanoglu', 'Fehiman Ugurdemir']
    },
    {
        name: 'Dervisan',
        Year: 1974,
        members: ['Ugur Dikmen', 'Sefa Ulastir', 'Hami Barutcu']
    }],
    links: ['http://en.wikipedia.org/wiki/Cem_Karaca', 'http://progressive.homestead.com/Cem_Karaca.html']
};

//our page method has a single parameter named 'data'
//so we have to construct a json object with a 'data' property 
//and complexObj will be the value part
//so our json object must be in form {"data":complexObj}
//construct a new javascript object with a property named data
//which contains the complexObj as value
 
var obj = { data: complexObj };
 
var data2Send = JSON.stringify(obj);//convert obj to json
 
//call the ComplexTypes page method with
$.ajax({
    type: "POST",
    url: 'Default.aspx/ComplexTypes',
    data: data2Send,
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    success: function (arg) { //call successfull
    },
    error: function (xhr) {
        //error occurred
    }
});

Here is the request data that we send from client to server
and here is the data parameter that we receive in page method.

Asp.net processed the posted data and all of its properties as a dictionary object. Now although it is easy to work with dictionaries in .Net it is not very useful for complex objects like the one we posted.Fortunately Asp.Net provides another way to parse the received data.
What we need to create a new class with the same structure as the passed data and change the paremeter type of the page method from object to the new class we create. Again the structure of the javascript object we send to the server :
var data = {
    name: 'Cem',
    surname: 'Karaca',
    birthDate: '05.04.1945',
    groups: [{
        name: 'Kardaslar',
        Year: 1970,
        members: ['Unol Buyukgonenc', 'Seyhan Karabay', 'Huseyin Sultanoglu', 'Fehiman Ugurdemir']
    },
    {
        name: 'Dervisan',
        Year: 1974,
        members: ['Ugur Dikmen', 'Sefa Ulastir', 'Hami Barutcu']
    }],
    links: ['http://en.wikipedia.org/wiki/Cem_Karaca', 'http://progressive.homestead.com/Cem_Karaca.html']
};

and here is the new class we constructed to handle the posted data.
public class MyData
       {
           public class Group
           {
               public string Name { get; set; }
               public int Year { get; set; }
               public List<string> Members { get; set; }
           }
           public string Name { get; set; }
           public string SurName { get; set; }
           public string BirthDate { get; set; }
           public List<Group> Groups { get; set; }
           public string[] Links { get; set; }
       }

and finally here is the new signature of the page method with the parameter type of 'MyData '.
[System.Web.Services.WebMethod]
public static string ComplexTypes(MyData data)
{
   return "Data received at " + DateTime.Now.ToString();
}
and here is the result :

We have successfully parsed the posted data as a strongly typed object.

How to handle different types of objects within the same page method

There can be times when we want to or have to use the same page methods for all objects posted from javascript. In order to achieve this we need to send objects as string data to our page method. Then we will use the free Json.Net to convert our json formatted string data to .net objects. First lets look at the javascript code :
//Send Complex Type1 as string to Json.Net
//define and object that will be sent to server
var simpleObj = {
    name: 'Cem',
    groups: ['Apaslar', 'Kardaslar']
};
 
//define a javascript object which has a  property named data
//the value of data property must be string 
//so we need to convert our obj
data2Send = JSON.stringify({
    data: JSON.stringify(simpleObj),
    type: 'type1'
});
 
$.ajax({
    type: "POST",
    url: 'Default.aspx/MultipleTypes',
    data: data2Send,
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    success: function (arg) {
        //print the server response
        $result.html(arg.d);
    }
});

The important thing is to convert the object to string type and also using another type to differentiate the object in the server. Now lets look at the server side code.
[System.Web.Services.WebMethod]
public static string MultipleTypes(string data, string type)
{
    switch (type)
    {
        case "type1":
            {
                var type1 = new
                {
                    name = String.Empty,
                    groups = new List<string> {}
                };
                var result = Newtonsoft.Json.JsonConvert.
                    DeserializeAnonymousType(data, type1);
 
            }
            break;
        case "type2":
            {
                var type2 = new { text = String.Empty };
                var result = Newtonsoft.Json.JsonConvert.
                    DeserializeAnonymousType(data, type2);
            }
            break;
    }
 
    return "Data received at " + DateTime.Now.ToString();
}

Our page method has two parameter data and type. The data parameter will contain the json formatted javascript object and the type parameter will contain the type information that will be used to differentiate the structure of the posted data. The JsonConvert.DeserializeAnonymousType method of Json.Net library takes a json formatted string and a anonymous type that has the same structure of the json data and return a .net object. Here is the result :




As seen from the result we handled different types of objects that are sent from javascript within the same page method easily using Json.Net library.

How to Return Complex Objects from Page Method

It's very easy to return complex object from Asp.Net page method. All we need to do is to define a page method that has return type of object and construct an anonymous type.Here is the C# code :
[System.Web.Services.WebMethod]
public static object ReturnComplexType()
{
    return new
    {
        data = "Hello from server",
        time = DateTime.Now.ToString(),
        array = new string[]{"", "Yart", "Cem"}
    };
}

Also note that in order to call this page method we have to construct an empty javascript object as the value for the data property of the $.ajax method. Here is the javascript code to call "ReturnComplexType" page method.
$.ajax({
    type: "POST",
    url: 'Default.aspx/ReturnComplexType',
    data: {},
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    beforeSend: function () {
        $result.html("Sending Data...");
    },
    success: function (arg) {
        $result.html(arg.d);
    }
});

and here is the received object from javascript.


As you can see it is very easy to send and receive complex objects using Asp.Net with jQuery. Also because the json format is used as the data interchange format the page is very responsive and server footprint is very low. Hope it helps.