ASP.NET MVC에서 Checkbox의 매핑과 바인딩 그리고 getUrlVars()와 knockoutjs에서의 사용법

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (2012-04-12 오전 9:39:18) Viewing : 55

제목이 좀 길군요..^^;

ASP.NET에서 check박스와 radio는 조금 골칫거리입니다.

radio는 언젠가 기회가 있겠지요..^^;

오늘은 checkbox의 사용법에 대해서 알아 보고자 합니다.

ASP.NET MVC에서는 CheckboxList 같은게 존재하지 않습니다. 뭐 필요하다면 만들수도 있겠지요..^^;

어쨋든 Html에서 사용법은 아래와 같습니다.

<% List<CommonCode> marketTypeCodes = CachedData.GetCodeListInCodeGroup("시장구분"); %>
<% foreach (CommonCode item in marketTypeCodes)
    { %>
<label>
    <input type="checkbox" name='MarketTypeCode' value='<%= item.CodeId %>' data-bind="checked:MarketTypeCode" />
    <%= item.Code%>
</label>
<% } %>

위 코드를 보시면 foreach 문을 사용해서 checkbox를 렌더하는 걸 보실 수 있습니다.

그러면 html에는 MarketTypeCode 라는 name을 가진 checkbox가 여러개가 렌더 될것입니다.

웹 사용자가 이 체크 박스를 Check 하여 서밋을 하게 되면

Browser는 파라메터를 MarketTypeCode=1&MarketTypeCode=2&MarketTypeCode=3 과 같은 형식으로 보내게 됩니다.

이 데이터를 Controller에서 받기 위해서는 Controller의 매개변수를 String[] marketTypeCode 로 선언해야 합니다.

즉 MVC 매핑 엔진은 같은 이름의 key 값을 배열로 만들어 Controller에 전달 하게 됩니다.

서버에서 배열로 값을 받았으니 나름 작업을 하면 될테이고..

이것이 검색과 같은 페이지라면 검색 조건을 유지 시키기 위해 이 값으로 다시 html 을 바인딩 해야 하겠지요?

View 에서 Model 을 지정하MarketTypeCode=1였다면

this.Html.xxxFor 라는 메서드로 정의된 element 들은 자동적으로 바인딩이 됩니다. (MVC 멋지죠..^^;)

그런데 이게 Checkbox는 예외입니다.

Checkbox가 View에 여러 개가 존재할 경우 앞서 말했던 바와 같이 이 개체의 형식은 Array 라서 this.Html.xxxFor 메서드로 표현 할 수가 없습니다.

그래서 jquery 를 이용해서 바인딩을 하곤 하지요..

querystring으로 View를 바인딩 하기위해서는 querystring을 분석하여 key,value로 값을 가져올 수 있어야 하는데 이 도움을 주는 것이 getUrlVars() 라는 플러그인입니다.

http://draeton.github.com/jquery/javascript/library/2011/05/08/jquery-geturlvars.html

위 링크에서 받으 실 수 있습니다..^^;

$.getUrlVar('MarketTypeCode') 혹은 $.getUrlVars()['MarketTypeCode'] 와 같이 사용하여 값을 받을 수 있는 거죠..

그러나 앞서 말한바와 같이 checkbox는 querystring으로 여러개의 같은 key값이 존재하는 형태로 넘어 옵니다.

MarketTypeCode=1&MarketTypeCode=2&MarketTypeCode=3

위 처럼 말이죠. 이 상태에서 $.getUrlVar('MarketTypeCode') 메서드를 호출하면 3 이라는 값만 떨어 집니다.

위 플러그인의 코드를 보시면 알겠지만 그냥 대입해버리기 때문에 마지막꺼만 전달됩니다.

그래서 조금 수정을 해보았습니다.

    getUrlVars: function () {
        var vars = [], hash;
        var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
        for (var i = 0; i < hashes.length; i++) {
            hash = hashes[i].split('=');
            var hasKey = false;
            for (var j = 0; j < vars.length; j++) {
                if (vars[j] == hash[0]) {
                    hasKey = true;
                    break;
                }
            }
            if (!hasKey) {
                vars.push(hash[0]);
                vars[hash[0]] = hash[1];
            }
            else {
                var arr = [];
                if (typeof (vars[hash[0]]) == 'string') {
                    arr.push(vars[hash[0]]);
                }
                else {
                    for (var k = 0; k < vars[hash[0]].length; k++) {
                        arr.push(vars[hash[0]][k]);
                    }
                }
                vars[hash[0]] = [];
                for (var k = 0; k < arr.length; k++) {
                    vars[hash[0]].push(arr[k]);
                }
                var hasData = false;
                for (var k = 0; k < vars[hash[0]].length; k++) {
                    if (vars[hash[0]][k] == hash[1]) {
                        hasData = true;
                        break;
                    }
                }
                if (!hasData) {
                    vars[hash[0]].push(hash[1]);
                }
            }
        }
        return vars;
    }

위 코드는 주저리 주저리 길지만..요지는 간단합니다.

키가 존재하지 않으면 기존처럼 , 만약 키가 존재한다면 배열을 만들어서 리턴해주는 형식... 입니다.

이 수정된 플러그인으로 $.getUrlVar('MarketTypeCode')  를 호출하게 되면 [1,2,3] 으로 값이 리턴됩니다. ^^;

자 이제 이 값을 바인딩 하면 되겠습니다.

순수 jQuery로 해도 되겠고 knockoutjs 를 사용한다면 다음과 같이 하면 되겠네요.

if ($.getUrlVars()['MarketTypeCode']) {
    var arr = $.getUrlVars()['MarketTypeCode'];
    for (var i = 0; i < arr.length; i++) {
        viewModel.MarketTypeCode.push(arr[i]);
    }
}

 참 쉽죠잉~~~

참 쉽죠 


마지막 업데이트 : (2012-04-12 오전 9:39:18)

TAG : MVC jquery knockoutjs 



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

SSIS Foreach 컨테이너 사용하기

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (2012-04-10 오후 2:39:04) Viewing : 45

제가 DTS 를 처음 접해본게 2005년쯤 된거 같습니다.

SQL 2000에는 DTS가 있었죠.. 스크립트 태스크에는 VB 스크립트만 허용되었던.. 그 옛날..

SQL 2005가 나오면서 SSIS 라고 이름이 바뀌었고 .NET Framwork 안에 쏘옥 숨었습니다.

그러나 VB.NET만 지원 하는 반쪽짜리 였죠..

SQL 2008에 이르러서야 C#을 지원하게 되었습니다만...

저는 2005년 다녔던 회사에서 이직후에 DTS는 가끔 썼지만 SSIS는 새로 공부해야 하는 압밥감으로 ㅎㅎ 보지 않았었죠.

최근에 프로젝트를 진행하면서 각종 데이터 이전 작업때문에 다시 보게 되었습니다.

DTS에서도 있었는지 가물가물합니다만..- 아마도 없었던듯...

컨테이너 라는 놈이 있는데 이중에 For 컨테이너와 Foreach 컨테이너가 있습니다.

컨테이너 라는 놈은 처음 써 보았기에..기록을 남깁니다..ㅎㅎ

MSDN 에 설명이 나오긴 했는데...이해하기가 어렵더군요..ㅡ,.ㅡ;

그래도 SSIS 를 사용해 보고자 하는 분들은 필독 해보십시오.

일단 목표는 아래와 같습니다.

부서명을 변경하는데 그 해당되는 목록을 가져와서 그 목록 안에서 루프를 돌면서 그 값으로 어떠한 쿼리를 해서 그 결과로 업데이트를 하는 것입니다.

어렵지 않아요..^^;

먼저 목록을 가져오기 위해 SQL 실행 태스크를 제어흐름 디자이너에 옮긴 후 더블클릭을 합니다.

편집을 해 봅시다.

아 그전에 연결 관리자에서 연결을 만들어야 합니다.

연결은 기본적으로 ADO.NET 프로바이더를 이용하시면 좋습니다. 상황에 따라서는 OLE를 이용해야 하는 경우도 있긴 하지만..기본으로는 ADO.NET 을 이용하시길 권해요.

어쨋든 연결을 만든 후에 조금 전에 연 SQL 실행 태스크에서 SQLStatment 에 쿼리를 입력합니다.

대상이 되는 목록이 되는 쿼리가 되겠습니다. 이는 목록(DataSet)으로 받아야 합니다.

우측 중간쯤에 결과 집합에 전체 결과 집합을 선택하십시오.

혹시 이 쿼리에 매개변수가 필요하다면 왼쪽 매개변수매핑을 클릭하고 세팅하면 됩니다만..저는 필요가 없군요..^^;

왼쪽에서 세번째 탭인 결과 집합을 클릭하면 아래와 같은 화면이 나옵니다.

결과 이름은 0 입니다.- 첫번째 탭인 일반에서 결과 집합을 전체 결과 집합으로 선택한 경우에는 반드시 이 값은 0이어야 합니다.

변수 이름은 목록을 받을 것으로 만드세요 형식은 object 입니다.

디버깅 해보면..이 변수값에 예상 했던 대로 DataSet 형식의 값이 들어 있음을 알 수 있습니다.^^;

이제 Foreach 컨테이너를 제어흐름디자이너에 옮깁니다.

더블 클릭하면 아래 편집 윈도우가 활성화 됩니다.

컬랙션 탭을 클릭합니다.

컬랙션 종류는 여러개가 있습니다만.... 자세한 건 위 링크의 MSDN 도움말을 보시고..(봐도 이해가 안되던...1 人)

Enumerator 에 Foreach ADO 열거자를 선택합니다. 그리고 ADO 개체 원본 변수에

이전 SQL 실행 태스크에서 지정하였던 DataSet 형식의 바로 그 변수를 선택합니다.

Foreach 내부에 전달 해 줄 변수를 선언합니다.

이 변수는 SQL 실행 태스크에서 정한 쿼리를 실행하게 되면 나오는 그 결과값(DataSet)에서 몇번째 컬럼의 값을 변수로 쓸 것인가를 할당 하는 것입니다.

즉 위 예를 보시면 첫번째 컬럼은 PROJ_NO 라는 변수에 대입되고 네번째 컬럼은 TEAM_CD 라는 변수에 대입됩니다.

이 2개의 변수는 Foreach 컨테이너 내부에서 사용될 것입니다.

Foreach 컨테이너 내부에 있는 첫번째 SQL 실행 태스크입니다.

이 태스크에서는 주어진 변수로 어떠한 값을 조회 하여 그 결과를 다른 변수에 저장 하는 일을 합니다.

기본적으로는 앞서 만든 SQL 실행 태스크와 같습니다만 결과 집합을 보시면 단일 행 으로 되어 있습니다.

이 쿼리에는 매개변수가 필요 합니다.  매개변수는 매개 변수 매핑 탭에서 정의 합니다.

매개 변수 매핑 탭에서 Foreach 컨테이너에서 넘겨준 TEAM_CD 라는 변수를 입력 매개변수로 사용합니다.

이 값은 쿼리에서 @TEAM_CD 라는 이름으로 사용됩니다.

다음은 결과 집합입니다.

이 쿼리는 2개의 컬럼을 SELECT 하는 쿼리입니다.

결과 이름에 그 컬럼의 인덱스를 넣어 줍니다. ( 컬럼의 이름이 아니라 인덱스입니다.)

이 결과는 각각 우측에 있는 DivCode와 DivName 이라는 변수에 매핑 됩니다.

이제 마지막으로 SQL 실행 태스크를 하나 더 만듭니다.

이 태스크는 업데이트를 담당합니다.

이 태스크는 매개 변수 매핑에 앞 태스크에서 생성한 2개의 매개변와 Foreach 컨테이너가 전달해준 변수를 사용합니다.

 

이상으로 Foreach 컨테이너를 사용하는 방법을 알아 보았습니다.

 참 쉽죠잉~~~


마지막 업데이트 : (2012-04-10 오후 2:39:04)

TAG : SSIS 



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

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

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (2012-04-06 오후 7:16:58) Viewing : 33

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

마지막 업데이트 : (2012-04-06 오후 7:17:57)

TAG : SQL 



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

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

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (2012-04-04 오후 1:28:09) Viewing : 48

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 ( 해당 컨트롤러에 정의되어 있어야 합니다..>.<) 로 이동 시킵니다.

이 상입니다..


마지막 업데이트 : (2012-04-04 오후 1:28:09)

TAG : MVC 



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

knockoutjs 사용시 참고 사항.

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (2012-03-27 오후 5:13:23) Viewing : 75

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

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

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

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)에서는 구현 된다는 희소식이 있군요.. 아직 베타입니다만..

어쨋든 기대해 봅니다.  


마지막 업데이트 : (2012-04-06 오후 1:06:22)

TAG : MVC jquery knockoutjs 



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



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