SQL에서 WHILE 문에서 테이블 변수 사용시 이상한 점..

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (4/6/2012 7:16:58 PM) Viewing : 3136

SQL Query 작성시 저의 경우는 커서 대신 테이블 변수를 사용합니다.

커서보다 월등히 빠르죠..^^;

어쨋든 글의 요지는 이게 아니고.

오늘 이상한 점을 알게 되었습니다.

WHILE 문에서 테이블 변수를 선언하게 되면..

상식적으로 볼때 루프문 안에 있기 때문에 루프 안에서 선언된 변수의 수명은 그 루프의 안에서만 유효합니다.

그런데 이놈은 그렇지 않더라고요.

아래 예제를 봅시당.

 DECLARE @Index int
 DECLARE @Cnt int
 DECLARE @TMP TABLE (ID INT IDENTITY(1,1) PRIMARY KEY, UserId uniqueIdentifier, Role varchar(512))
 INSERT INTO @TMP
 SELECT UserId, '' FROM SstUser
 SET @Cnt = @@ROWCOUNT
 SET @Index = 1

 WHILE @Index <= @Cnt
 BEGIN
  DECLARE @UserId uniqueIdentifier
  SELECT @UserId = UserId FROM @TMP WHERE ID = @Index

  DECLARE @I INT
  DECLARE @C INT
  DECLARE @R VARCHAR(128)
  DECLARE @T TABLE (Id int IDENTITY(1,1) PRIMARY KEY, RoleName varchar(128))
  
  INSERT INTO @T
  SELECT B.RoleName FROM AccountsUsersInRoles A 
   JOIN AccountsRoles B ON A.RoleId = B.RoleId
  WHERE UserId = @UserId
  ORDER BY RoleName
  
  SET @C = @@ROWCOUNT
  SET @I = 1 
  SET @R = ''
  
  WHILE (@I < @C)
  BEGIN
   DECLARE @RoleName varchar(64)
   SELECT @RoleName = RoleName FROM @T WHERE Id = @I
   IF @R <> ''
    SET @R = @R + ','
   SET @R = @R + @RoleName
   SET @I = @I + 1
  END
  
  UPDATE @TMP
  SET Role = @R
  WHERE ID = @Index
  
  SET @Index = @Index + 1
 END

 SELECT A.*, b.Role FROM SstUser A JOIN @TMP B ON A.UserId = B.UserId
 ORDER BY Name
END

9행에 WHILE문을 사용하고 17행을 보면 DECLARE로 테이블 변수를 while 문 내부에서 선언합니다.

이 테이블 변수 @T의 경우 While문 안에서만 유효해야 하는데 실행해 보면..계속 살아 있습니다..^^;;

identity가 적용 되어 있으므로 첫 행의 id는 1이고 다음 루프에서도 id는 1이어야 되는데..그냥 append 됩니다..

상식적으로 이해는 안되나.. 구글링 해보니..원래 그렇더군요..쿨럭..

왜 그럴까요....왜 이렇게 만들었을까요..궁금할 따름이네요..^^;

어쨋든 위 코드는 정상적으로 구동이 되지 않습니다.

아래 처럼 바꿔야 합니다.

 DECLARE @Index int
 DECLARE @Cnt int
 DECLARE @TMP TABLE (ID INT IDENTITY(1,1) PRIMARY KEY, UserId uniqueIdentifier, Role varchar(512))
 INSERT INTO @TMP
 SELECT UserId, '' FROM SstUser
 SET @Cnt = @@ROWCOUNT
 SET @Index = 1

 DECLARE @I INT
 DECLARE @C INT
 DECLARE @R VARCHAR(128)
 DECLARE @T TABLE (Id int IDENTITY(1,1) PRIMARY KEY, RoleName varchar(128))
 SET @I = 1 

 WHILE @Index <= @Cnt
 BEGIN
  DECLARE @UserId uniqueIdentifier
  SELECT @UserId = UserId FROM @TMP WHERE ID = @Index
  
  INSERT INTO @T
  SELECT B.RoleName FROM AccountsUsersInRoles A 
   JOIN AccountsRoles B ON A.RoleId = B.RoleId
  WHERE UserId = @UserId
  ORDER BY RoleName
  
  SET @C = @I + @@ROWCOUNT
  SET @R = ''
  
  WHILE (@I < @C)
  BEGIN
   DECLARE @RoleName varchar(64)
   SELECT @RoleName = RoleName FROM @T WHERE Id = @I
   IF @R <> ''
    SET @R = @R + ','
   SET @R = @R + @RoleName
   SET @I = @I + 1
  END
  
  UPDATE @TMP
  SET Role = @R
  WHERE ID = @Index
  
  SET @Index = @Index + 1
 END

 SELECT A.*, b.Role FROM SstUser A JOIN @TMP B ON A.UserId = B.UserId
 ORDER BY Name

마지막 업데이트 : (4/6/2012 7:17:57 PM)

TAG : SQL 



Trackback 보기 (0)
댓글 보기 (0)
댓글 쓰기

ASP.NET MVC에서의 AuthorizeAttribute의 문제점.

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (4/4/2012 1:28:09 PM) Viewing : 5220

ASP.NET MVC에서는 참 편리한 많은 필터들을 내장하고 있습니다.

그중에 권한에 관련한 AuthorizeAttribute 라는 놈이 있습니다.

메서드나 클래스에 이 어트리뷰트를 선언하면 Role과 UserName에 따라서 권한제어를 할 수 있습니다.

웹응용프로그램에서 이와 같은 일을 하기 위해서는 web.config 파일에 설정을 해 주어야 했습니다만..계층 구조도 맞게끔 설계해야 하고..여간 복잡하지 않았죠.

그러나 ASP.NET MVC 에서는 컨트롤러의 액션 메서드에서 위 어트리뷰트와 권한을 줄 UserName 혹은 Role을 주기만 하면 됩니다.

그러나..이 어트리뷰트 뭔가 문제가 있습니다.

내가 원하는 프로세스는 권한이 없으면 권한 없음이라는 페이지로 리다이렉트 시키는 것입니다.

그러나 이 어트리뷰트는 권한이 없는 사용자의 경우 로그인 페이지로 리다이렉트 시켜버립니다.

마치 인증이 안된것 처럼 말이죠..

만약 Login 액션 메서드에서 인증된 사용자이며 ReturnUrl이라는 파라메터에 url이 있으면 바로 해당 url로 리다이렉트 시키는 코드가 삽입되어 있다면..무한 루프에 빠지게 됩니다..ㅡ.ㅠ;

Authentication과 Authorization이 혼동을 일으키는 듯 합니다..^^;

MSDN을 살펴 보면.

권한이 없는 사용자가 Authorize 특성으로 표시된 메서드에서 액세스하려고 하면 MVC 프레임워크는 401 HTTP 상태 코드를 반환합니다.사이트가 ASP.NET 폼 인증을 사용하도록 구성되어 있으면 401 상태 코드는 브라우저가 사용자를 로그인 페이지로 리디렉션하도록 합니다.

라고 되어 있습니다.

그럼 이 401 에러 코드는 어디서 받을 수 있을까요??? @.@

먼저 AuthorizeAttribute를 reflector로 분해해 보면 아래와 같습니다.

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=true)]
public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
    // Fields
    private string _roles;
    private string[] _rolesSplit;
    private readonly object _typeId;
    private string _users;
    private string[] _usersSplit;

    // Methods
    public AuthorizeAttribute();
    protected virtual bool AuthorizeCore(HttpContextBase httpContext);
    private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus);
    protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext);
    public virtual void OnAuthorization(AuthorizationContext filterContext);
    protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext);
    internal static string[] SplitString(string original);

    // Properties
    public string Roles { get; set; }
    public override object TypeId { get; }
    public string Users { get; set; }
}

 

이 중에서 관심 있게 봐야 하는 메서드는 AuthorizeCore 와 HandleUnauthoziedRequest 와 OnAuthorization 입니다.

하나씩 살펴 보도록 하죠.

protected virtual bool AuthorizeCore(HttpContextBase httpContext)
{
    if (httpContext == null)
    {
        throw new ArgumentNullException("httpContext");
    }
    IPrincipal user = httpContext.User;
    if (!user.Identity.IsAuthenticated)
    {
        return false;
    }
    if ((this._usersSplit.Length > 0) && !this._usersSplit.Contains<string>(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
    {
        return false;
    }
    if ((this._rolesSplit.Length > 0) && !this._rolesSplit.Any<string>(new Func<string, bool>(user.IsInRole)))
    {
        return false;
    }
    return true;
}

먼저 AutorizeCore 메서드는 위와 같습니다.

8행은 인증 여부, 12행은 UserName이 어트리뷰트 선언시에 포함되는지 16행은 Role이 포함되었는지를 판단하여 true/false를 리턴합니다.

이 메서드가 주어진 값으로 true/false를 판단함을 알수 있습니다.

이제 OnAuthorization 메서드를 살펴봅니다.

public virtual void OnAuthorization(AuthorizationContext filterContext)
{
    if (filterContext == null)
    {
        throw new ArgumentNullException("filterContext");
    }
    if (this.AuthorizeCore(filterContext.HttpContext))
    {
        HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
        cache.SetProxyMaxAge(new TimeSpan(0L));
        cache.AddValidationCallback(new HttpCacheValidateHandler(this.CacheValidateHandler), null);
    }
    else
    {
        this.HandleUnauthorizedRequest(filterContext);
    }
}

이 메서드는 위의 AutorizeCore메서드가 true이면 어떤 작업을 하고..false 라면 HandleUnauthorizedRequest 메서드를 호출함을 알 수 있습니다.

이제 마지막으로 HandleUnauthorizedRequest 메서드를 살펴 봅니다.

protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
    filterContext.Result = new HttpUnauthorizedResult();
}

위 메서드는 HttpUnauthorizedResult 라는 클래스를 선언합니다. 헉헉..

그럼 이 놈은 또 뭘까요..

public override void ExecuteResult(ControllerContext context)
{
    if (context == null)
    {
        throw new ArgumentNullException("context");
    }
    context.HttpContext.Response.StatusCode = 0x191;
}

 

위와 같이 이 클래스는 Excute에 위처럼 정의해 놓은 ActionResult 클래스입니다.

0x191은 10진수로 401 입니다..

즉 상태코드를 401로 바꾸고 그냥 그대로 리턴합니다.

코드는 여기 까지 입니다.

즉 MSDN에서 말한 것처럼 401을 던지는 것은 맞는 듯 합니다.

그러나 실제로  HttpModule의 EndRequest레서 디버깅을 해보면..302로 떨어집니다.

(현재 Fiddler를 사용할 수 없는 환경이라 Fiddler는 못 보았네요..>.<)

302는 리다이렉트 입니다.

이미 302로 StatusCode가 변환 되어 내려 집니다..허곡..

그럼 401은 어디서 가로 챌수 있을까요...ㅡ.ㅠ;

 

여기서 부터 구글링을 열심히 해 보았습니다..

역시 리소스는 많은데..결국 근본적인 해결책(401을 가로채는 것)은 없어 보입니다.

많으 블로거들이 CustomerAuthorizeAttribute를 구현 하는 방법으로 우회 하더군요...

 

아..그런 것인가...그 거밖에 없는 건가...

그런가 봅니다..

    public class CustomAuthorizeAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }
            if (this.AuthorizeCore(filterContext.HttpContext))
            {
                HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
                cache.SetProxyMaxAge(new TimeSpan(0L));
                cache.AddValidationCallback(new HttpCacheValidateHandler(this.CacheValidateHandler), null);
            }
            else
            {
                String[] arr = filterContext.HttpContext.Request.AcceptTypes;
                switch (arr[0])
                {
                    case "text/html":
                        filterContext.Result = new RedirectResult("/Error/HasNotPermission");
                        break;
                    case "application/json":
                        filterContext.Result = JsonCreator.CreateJson((Controller)filterContext.Controller, false, "권한이 없습니다.","알림");
                        break;
                    default:
                        this.HandleUnauthorizedRequest(filterContext);
                        break;
                }
            }

        }

        private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
        {
            validationStatus = this.OnCacheAuthorization(new HttpContextWrapper(context));
        }
    }

위 코드는 그러한 예가 되겠습니다.

17행의 경우 요청에 따라.. 그 결과를 달리 보여 주는 루틴입니다.

저는 ajax를 자주 써서 말이죠..ajax로 서버 콜 하고 json 형식으로 리턴 해주고..클라이언트에서 이 json를 받아 화면에 뿌려주고..뭐 이런 방식 말입니다.

그러한 경우에느 메시지로 보여야 겠지요..

그렇지 않은 경우에는 /Error/HasNotPermission ( 해당 컨트롤러에 정의되어 있어야 합니다..>.<) 로 이동 시킵니다.

이 상입니다..


마지막 업데이트 : (4/4/2012 1:28:09 PM)

TAG : MVC 



Trackback 보기 (0)
댓글 보기 (0)
댓글 쓰기

knockoutjs 사용시 참고 사항.

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (3/27/2012 5:13:23 PM) Viewing : 3068

좀 긴 시간 동안 이놈을 살펴 보았습니다.

직접 적용도 해보았고요..

공식 문서에도 나와 있지 않은...ㅡ.ㅡ;;;;; 일종의 팁 같은 것을 정리해 봅니다...계속 추가 될 수 있습니다.

1. ko.applyBindings() 함수는 한페이지에 하나만 사용되어야 합니다.

즉 한 페이지에서의 다중 모델을 사용 해서는 안됩니다.

만약에 다중 모델을 사용하기 위해서는 다음과 같은 방법으로 합니다.

var Customer = function(){
    CustomerName = ko.observable();
};

var Partner = function(){
   PartnerName = ko.observable();
};

var ViewModel = function(){
    Customer = new Customer();
    Partner = new Partner();
    .....
};

ko.applyBindings(new ViewModel());

2. mapping

mapping의 경우에는 기본적으로 http://knockoutjs.com/documentation/plugins-mapping.html 를 참고 하면 됩니다만..

문제가 좀 있네요..^^;

이 매핑 플러그인은 viewModel <-> json 혹은 javascript object 로 상호 변환을 해주는 일을 합니다.

저의 경우에는 ajax를 이용해 server로 부터 json 형식으로 받은 데이터를 viewModel로 매핑 하는 경우에 사용 했네요.

위 링크에 있는 예를 적용하면..

//ajax callback data
viewModel.Customer = ko.mapping.fromJS(callbackData);

이렇게 사용을 하게 되겠지만..

이렇게 사용을 하게 되면..자동적으로 UI가 업데이트 되지 않습니다..

디버깅을 해보면 viewModel에 분명..데이터가 매핑은 되었으나..화면에 변함이 없으니..ㅡ.ㅡ;;;

위 링크의 도움말 문서에 보면 Mapping from multiple sources 라고 있습니다. 즉..

아래와 같이 사용합니다.

ko.mapping.fromJS(callbackData, {}, viewModel.Customer);

첫번째 매개변수는 원본 데이터이고 두번째 매개변수는 mapping options 이며 세번째 매개변수는 대상 데이터가 되겠습니다.

3. data-bind는 앞에서부터 바인딩 합니다.

예를 들면... 아래와 같은 라디오 버튼이 있습니다.

라디오 버튼의 data-bind attribute는 2개의 속성이 바인딩 되어 있습니다.

value 라는 바인딩 속성과 checked 라는 바인딩 속성인데요.. value 바인딩 속성은.. value 태그에 PartnerId 값을  넣겠다 라는 의미가 되겠고요.

checked 라는 바인딩 속성은 viewModel의 DepositPartner 라는 속성(혹은 함수)의 리턴값이 value와 같다면 check를 하겠다라는 의미가 되겠습니다.

즉 위 태그 에서 cheched 라는 바인딩 속성은 value라는 바인딩 속성에 종속적이 된거죠. 그러므로 value가 바인딩이 되지 않는다면 checked도 바인딩을 할 수 없습니다.(value 값이 없기에.)

위 태그는 정상 작동을 하지만 value와 checked의 순서를 바꾸게 되면.. 작동을 하지 않습니다...순서대로 바인딩 하기 때문이지욤..

4. 이벤트의 호출 방법.

클릭 이벤트는 click 이라는 바인딩 속성을 사용합니다.

여러가지 사용법이 있지만.. 매개변수를 전달 해 주고자 하는 경우에는  다음과 같이 합니다.

첫번재 매개변수($data)는 현재 개체입니다.현재 라는건  element가 아니라 viewModel입니다.

두번째 매개변수는 사용자 지정 매개변수입니다. 즉..0을 넘기겠다라는 의미가 되죱..

받는 경우에는 다음과 같습니다.

        self.depositScheduleClick = function (index, data, event) {
            var $cur = $(event.target);
            var partner = null;
            ...
            return false;
        };

첫번째 매개변수 부터 사용자 지정 매개변수입니다. 위의 예라면 0이 되겠네요.

그리고 두번째 매개변수(엄밀히 말하면 끝에서 두번째 매개변수가 맞습니다.)는 viewModel 개체가 됩니다.

마지막으로 세번째 매개변수(엄밀히 말하면 마지막 매개변수가 맞습니다.)는  event가 되겠네요.

jquery의 click 핸들러에서 넘겨지는 바로 그 event 입니다.

현재 클릭한 대상의 element를 알고자 한다면 위 예제 처럼 event.target을 이용합니다.

 5. 라디오버튼의 클릭 이벤트

라디오버튼의 경우 checked 바인드를 사용합니다만.. 필요에 따라서 버튼의 클릭 후 그 값을 가지고 어떤 제어를 해야 하거나 하는 경우가 있습니다. 바인드를 위해서는 checked를 넣어야 하겠지만..이는 클릭후 그 값을 가져와서 제어를 할 수는 없습니다.

그렇다고 하여 checked 바인드와 click 바인드를 같이 쓰게 되면..click이벤트는 일어 나지만..버튼이 checekd가 되지 않습니다..

기본적으로 radio에는 click 바인드가 허용되지 않는 듯..합니다.

위 작업을 하기 위해서는 다음과 같이 처리 합니다.

value에 제품 아이디를 바인드 하였습니다. 이 checked 바인드에 $parent가 있는 걸로 보아..이 라디오버튼은 내부에 존재 하는 군요..(data-bind: foreach)

모델은 다음과 같이 선언합니다.

        var viewModel = {
            ProductList: ko.observableArray(),
            SelectProduct: ko.observable()
        };
        viewModel.SelectProduct.subscribe(function (newValue) {
            $('#ProductId').val(newValue);
        });

        ko.applyBindings(viewModel);

SelectProduct에 바인딩을 하고 subscribe 함수를 사용하였습니다.

document에 의하면... if you want to register your own subscriptions to be notified of changes to observables, you can call their subscribe function 라고 되어 있네요..^^;

 

어쨋든..nweVaue에는 선택한 라디오버튼의 value값이 들어 오게 됩니다..이게 이 값으로 작업을 하시면 되겠네요..

 6. foreach 문에서의 index

있을 법도 한데..없습니다.ㅡ.ㅠ;

ASP.NET MVC(Spring 에서도)에서는 컬렉션데이터를 Controller에서 받을 수 있습니다.

컬렉션을 Congroller에 넘기기 위해서는 인덱싱을 해야 합니다.

예를 들어 List<Product> 형식의 Products 속성에 바인딩을 하기 위해서는 view 단계에서

products[0].Name, products[0].Code 등등의 형식으로 표현을 해야 하지요..

그렇게하면 Controller에서 Products 라는 속성에 자동 바인딩 됩니다.

KO 에서 foreach로 바인딩 하는 경우에 이 인덱서를 정의 할 수 없습니다.

OTL....

차기 버전(2.1)에서는 구현 된다는 희소식이 있군요.. 아직 베타입니다만..

어쨋든 기대해 봅니다.  


마지막 업데이트 : (4/6/2012 1:06:22 PM)

TAG : MVC jquery knockoutjs 



Trackback 보기 (0)
댓글 보기 (0)
댓글 쓰기

knockoutjs library

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (3/21/2012 11:14:31 AM) Viewing : 2246

ASP.NET MVC 와 jquery 조합은 상당히 매력적입니다.

ASP.NET MVC가 4.5까지 나왔나요.. 4.0인가...여튼..전 아직도 2.0을 씁니다..@.@

업그레이드 할 매리트가...별로 보이지 않네요..(관심이 없어서인지도...)

이 사이트도 그렇지만 모든 Action은 비동기로 처리 하곤 합니다.

GET으로 모델일 바인딩 하는 View 페이지를 빼고는 말이죠..

이 사이트에는 별로 없지만..ajax를 이용하여 CRUD 실행 하는건 많이 쓰죠...

java spring에서도 많이 썼습니다...(뭐 클라이언트 코드인데..서버 언어가 중요하겠습니까..ㅡ.ㅡ;;;)

 

어쨋든 서버의 Model을 json으로 클라이언트로 내려 보내게 되는 것이죠..

그렇게 되면..하나의 object로 리턴을 받을 수 있습니다.

 

이것을 스크립트(jQuery) 로 바인딩 하고자 한다면..상당한 날코딩이 필요하게 됩니다.

바인딩 할 각각의 element에 id 를 주거나..하여 select 할 수 있는 어떤 값을 주어야죠..

그런 후 $(selector).val() 처럼 넣거나...이와 비슷하게 값을 입력 하곤하지요..

게다가 이 값이 List 형식이라면....

사람마다 차이가 있긴 하지만..

저의 경우엔  var $tr = '<tr><td>..... 어떤 값들... </td></tr>'

형식으로 만들고 append 하죠..게다가 여기 안에 있는 element에 이벤트라도 걸어 줘야 한다면...아...

엄청 복잡해 집니다..@.@

 

물론 코드 정리도 힘들어 지고요...다시 볼라 치면..머리 아프게 되죠..

 

그래서...저는 우선 template 을 찾아 보았습니다.

우선 jquery 공식 템플릿인 jQuery.template 이 있습니다.

그런데 이건...MS에서 내놓은(?) 공식 템플릿 같은데(MSDN 매거진엥서 본듯도 하고요...기억이 가물가물..)...사용법이 좀..거시기 한듯 합니다..ㅡ.ㅡ;

눈에 딱 들어 오지도 않고요... 게다가 베타이고..(전 개인적으로 정식버전..아니면..안써요..ㅎㅎ 베타 시로...)

 

그러다가..우연히 knockoutjs 라는 놈을 알게 되었습니다.

이거 멋지네요..

이틀 정도 시간을 내어서 Document를 싸그리 읽어 보았습니다..

제가 원했던..것이 다 있네요..

model binding 이라던지 template 기능 이라던지 말이죠..

오 괜찮다 해서 다운 받을려고 링크를 클릭했더니..

스티브샌더슨 이라는 아저씨 이름이 나오네요..

흠..스티브샌더슨...아주 귀에 익은데....

프로 ASP.NET MVC 프레임워크: Taeyo's Choice  라는 책을 쓴 아저씨군요..ㅡ.ㅡ;

저도 이책을 보고.. MVC를 시작 했습죠..ㅎㅎㅎ

이 아자씨 모하나 했더니..요거 만들고 있었네..@.@

 

어쨋든..상당히 매력적인 라이브러리인듯 합니다.

http://frends.kr/topics/category/research/knockout-js/

위 링크에서 문서 3개 정도는 번역이 되어 있네요..비기너 분들은 참고 하시고요..

 

일단 지금 작업중인 프로젝트에 적용을 해봐야 겠습니다..백견이 불여일타....

 

 

 

 

 

 


마지막 업데이트 : (3/21/2012 11:14:31 AM)

TAG : MVC jquery knockoutjs 



Trackback 보기 (0)
댓글 보기 (0)
댓글 쓰기

jquery plguin - jquery.flot.js

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (2/10/2012 5:26:10 PM) Viewing : 9453

음 이번에 소개할 플러그인은 차트 플러그인 입니다.

차트 플러그인도 상당히 많은 편인데...저는 이것을 추천 합니다.

http://code.google.com/p/flot/

상당히..예쁘고..기능도 다양 합니다..^^;

부가 기능을 사용하기 위해서는 몇가지 플러그인을 더 다운로드 받아야 합니다.

저는 jquery.flot.selection.js 와 excanvas.js 를 더 사용합니다.

examples 에 보시면 간단한 기본 사용 예제를 보실 수 있으며 API 도움말 도 함께 제공 합니다.

대부분 간단한 선그래프나 막대 그래프를 사용할 터인데..

이 플러그인을 쉽게 사용하기 위한 또 다른 보조 플러그인 하나 만들어 보았습니다

/*
var month = new Array('201201','201202','201203','201204');
var list = [{"201201":"10","201202":"20","201203":"30","201204":"40"},{"201201":"33","201202":"23","201203":"13","201204":"13"}];
var labelArray = new Array('A회사','B회사');
*/ 
(function($) {
 $.fn.drawChart = function(labelArray, xAxisArray, dataMap, options) {
  var opts = $.extend( {}, $.fn.drawChart.defaults, options);
  
  var $placeHolder = $(this);
  $placeHolder.data('label', labelArray);
  $placeHolder.data('xAxisArray', xAxisArray);
  $placeHolder.data('dataMap', dataMap);
  

  var sourceData = createGraphData(labelArray, xAxisArray, dataMap);
  var plot = viewLineChart($placeHolder, sourceData);

  $placeHolder.bind("plotselected", function(event, ranges) {
   plot = $.plot($placeHolder, sourceData, $.extend(true, {}, opts, {
    xaxis : {
     min : parseInt(ranges.xaxis.from),
     max : parseInt(ranges.xaxis.to)
    }
   }));
  });
  var previousPoint = null;
  $placeHolder.bind("plothover", function(event, pos, item) {
   if (item) {
    if (previousPoint != item.datapoint) {
     previousPoint = item.datapoint;

     $("#tooltip").remove();
     var x = item.datapoint[0];
     var y = item.datapoint[1];
     showTooltip(item.pageX, item.pageY, "X-axis = " + x.toString() + "<br/>Selected Value" + " = " + y);
    }
   } else {
    $("#tooltip").remove();
    previousPoint = null;
   }
  });

  function createGraphData(labelArray, xAxisArray, dataMap) {
   var data = [];
   for ( var j = 0; j < dataMap.length; j++) {
    var subData = [];
    for ( var i = 0; i < xAxisArray.length; i++) {
     subData.push( [ xAxisArray[i], dataMap[j][xAxisArray[i]] ]);
    }
    data.push( {
     label : labelArray[j],
     data : subData
    });
   }
   return data;
  }

  function viewLineChart($placeHolder, sourceData) {
   var p = $.plot($placeHolder, sourceData, opts);
   return p;
  }

  function showTooltip(x, y, contents) {
   $('<div id="tooltip">' + contents + '</div>').css( {
    position : 'absolute',
    display : 'none',
    top : y + 5,
    left : x + 5,
    border : '1px solid #fdd',
    padding : '2px',
    'background-color' : '#fee',
    opacity : 0.80
   }).appendTo("body").fadeIn(200);
  }
 };
 $.fn.redrawChart = function() {
  var $placeHolder = $(this);
  if ($placeHolder.data('label') != null) {
   $placeHolder.drawLineChart($placeHolder.data('label'), $placeHolder.data('xAxisArray'), $placeHolder.data('dataMap'));
  }
 };
 $.fn.drawChart.defaults = {
   series : {
    lines : {
     show : true
    },
    points : {
     show : true
    }
   },
   grid : {
    hoverable : true
   },
   selection : {
    mode : "x"
   },
   xaxis : {
    tickDecimals : 0
   },
   legend:{
    position: 'ne'
   }
  };
})(jQuery);

사용법은 다음과 같습니다.

var xAxisArray = new Array('201201','201202','201203','201204');
var list = [{"201201":"10","201202":"20","201203":"30","201204":"40"},{"201201":"33","201202":"23","201203":"13","201204":"13"}];
var labelArray = new Array('A회사','B회사');
  
$('#divAmountPerManGraph').drawChart(labelArray, xAxisArray, list);

xAxisArray 변수의 경우 x축이 되겠습니다.

list 변수가 데이터인데..키값 쌍으로 되어 있습니다. 키값이 바로 x축과 연결이 되지요.. list 변수를 보면 알겠지만..총 3개의 그룹이 있습니다. 이 그룹의 갯수는 labelArray 와 같아야 겠지요.

labelArray라는 것은 Serise의 이름이 되겠습다 legend 영역에 렌더됩니다.

그리고 플러그인에서 redrawChart() 라는 메서드를 하나 더 만들었는데..이 메서드는 차트에서 selection 기능(확대 기능입니다.)을 사용후에 원상복구 시켜 주는 메서드 입니다..

메서드의 네번째 변수는 options인데 기본값을 라인입니다.. 다른 차트를 사용하고자 한다면..

도움말을 참고 하여 options 개체를 선언하여 넣으면 되겠네요..

이외에 온갖 잡다하고 다양한 기능들을 가지고 있습니다..^^;

도움말을 참고해서 보는 것도 좋겠지요.

그리고..마지막으로 위에 list 변수와 같은 값을 스크립트로 받기 위해서는 ajax로 서버에서 데이터를 받아야 하는데..

자바의 경우 List 형식으로 받으면 됩니다.

닷넷의 경우 List 형식으로 받으면 되겠네요..^^;

 

 

 

 


마지막 업데이트 : (2/10/2012 5:35:05 PM)




Trackback 보기 (0)
댓글 보기 (0)
댓글 쓰기



<< < 1 2 3 4 5 6 7 8 9 10 > >>