2013-05-10 3 views
1

그래서 저는 javascript + api 포럼 소프트웨어를 개발 중입니다. 내 요점은 오프라인에서 포럼을 읽을 수 있도록하는 것입니다. 여기에는 HTML5의 오프라인 저장소가 포함됩니다. 특히 IndexedDB는 미래에 가장 유망한 것으로 보이기 때문에 사용하고 싶습니다. 데이터를 가져 오거나 일시적으로 저장하는 데 좋은 서비스/공장을 갖지만 IDDB는 크게 손상되었습니다. 아무도 이것에 대해 어떻게 가야하는지에 대한 조언을 갖고 있습니까?Angular.js와 Indexed DB를 사용할 수있는 좋은 방법이 있습니까?

편집 또한 호스트 된 버전을 원하는 사용자는 cloud9입니다. 백엔드는 구글의 클라우드 스토리지 인 경우

var angular = angular || {}; 
(function(w){ 
    w.localStorage = w.localStorage||{}; 
    w.indexedDB = w.indexedDB || w.mozIndexedDB || w.webkitIndexedDB || w.msIndexedDB || null; 
    w.IDBTransaction = w.IDBTransaction || w.webkitIDBTransaction || w.msIDBTransaction || null; 
    w.IDBKeyRange = w.IDBKeyRange || w.webkitIDBKeyRange || w.msIDBKeyRange || null; 
})(this); 

angular.module("JSForumServices",[],function($provide){ 
    $provide.factory('ForumStorage',(function(){ 
     var service = { 
      post:null, 
      thread:null, 
      board:null, 
      cache:{}, 
      pending:{} 
     }; 
     var fetch = (function(baseFunction,path,callback){ 
      if(path in service.pending) 
       return service.pending[path]; 
      var r=baseFunction(); 
      service.pending[path] = r; 
      var ajaxRequest = new XMLHttpRequest(); 
      var cancelled = false; 
      var dateRegex = 
       /^(?:(Sun|Mon|Tue|Wed|Thu|Fri|Sat),\s+)?(0[1-9]|[1-2]?[0-9]|3[01])\s+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+(19[0-9]{2}|[2-9][0-9]{3})\s+(2[0-3]|[0-1][0-9]):([0-5][0-9])(?::(60|[0-5][0-9]))?\s+([-\+][0-9]{2}[0-5][0-9]|(?:UT|GMT|(?:E|C|M|P)(?:ST|DT)|[A-IK-Z]))(\s+|\(([^\(\)]+|\\\(|\\\))*\))*$/; 
      ajaxRequest.onreadystatechange = (function(){ 
       var readyState = ajaxRequest.readyState; 
       if(readyState==4&&(!cancelled)){ 
        // Store the copy locally! 
        // Also, initiate the callback. 
        // This way if the storage fails, 
        // The application continues to work 
        // As expected. 
        var data = JSON.parse(ajaxRequest.responseText); 
        for(var k in data) 
         r[k] = data[k]; 
        service.cache[path]={obj:r,modified:new Date()}; 
        delete service.pending[path]; 
        callback(r); 
       } 
       else if((path in service.cache)&&readyState>=2){ 
        var oldDate = service.cache[path].modified; 
        console.log("Cache'd copy for",path,"exists.",oldDate.toString()); 
        var isMoreRecent = false;//Is the server-copy more recent? 
        var serverModifiedString = ajaxRequest.getResponseHeader("Last-Modified"); 
        console.log(serverModifiedString); 
        var match = dateRegex.exec(serverModifiedString); 
        var serverModified = new Date(); 
        serverModified.setDate(parseInt(match[2],10)); 
        serverModified.setMonth({ 
         "jan":0, 
         "feb":1, 
         "mar":2, 
         "apr":3, 
         "may":4, 
         "jun":5, 
         "jul":6, 
         "aug":7, 
         "sep":8, 
         "oct":9, 
         "nov":10, 
         "dec":11 
        }[match[3].toLowerCase()]); 
        serverModified.setYear(parseInt(match[4],10)); 
        serverModified.setHours(parseInt(match[5],10)); 
        serverModified.setMinutes(parseInt(match[6],10)); 
        serverModified.setMilliseconds(parseInt(match[7],10)); 
        isMoreRecent = serverModified > oldDate; 
        if(!isMoreRecent&&(path in service.pending)){//sometimes this code may be slower than network speeds? Just to be safe, I guess. 
         cancelled=true; 
         var oldObject = service.cache[path].obj; 
         for(var key in oldObject) 
          r[key]=oldObject[key]; 
         console.log("using a cache'd value.",r); 
         callback(r); 
         delete service.pending[path]; 
         ajaxRequest.abort();//No need to waste more bandwidth! 
        } 
       } 
      }); 
      ajaxRequest.open("GET",path,true); 
      ajaxRequest.send(); 
      return r; 
     }); 
     var ObjectFetch = (function(base,constr){ 
      var ret = (function(id,cb){ 
       cb = cb||(function(){}); 
       return fetch(constr,base+id+".json",cb); 
      }); 
      return ret; 
     }); 
     service.post = ObjectFetch("./post/",(function(){ 
      return { 
       id:"???", 
       author:"???", 
       content:"", 
       date:"" 
      }; 
     })); 
     service.thread = ObjectFetch("./thread/",(function(){ 
      return { 
       id:"???", 
       title:"???", 
       posts:[], 
       tags:"" 
      }; 
     })); 
     service.board = ObjectFetch("./board/",(function(){ 
      return { 
       id:"???", 
       title:"???", 
       threads:[], 
       tags:"" 
      }; 
     })); 
     service.forum = ObjectFetch("./",(function(){ 
      return { 
       name:"Javascript Forum Software.", 
       boards:[] 
      }; 
     })); 
     if(window.indexedDB!==null){ 
      var postFetch = service.post; 
      var threadFetch = service.thread; 
      var boardFetch = service.board; 
      (function(dbengine){ 
       var boardsLoaded = false; 
       var threadsLoaded = false; 
       var postsLoaded = false; 
       var req = dbengine.open("forum",1); 
       req.onupgradeneeded = (function(e){ 
        var db = e.target.result; 
        db.createObjectStore("post",{ 
         keyPath:"id" 
        }); 
        db.createObjectStore("thread",{ 
         keyPath:"id" 
        }); 
        db.createObjectStore("board",{ 
         keyPath:"id" 
        }); 
       }); 
       req.onsuccess = (function(e){ 
        service.database = e.target.result; 
        var loadData = service.database.transaction([ 
         'board', 
         'thread', 
         'post'],'readwrite'); 
        loadData.onsuccess = (function(ee){ 
         var transaction = ee.target.result; 
         transaction.objectStore("board").openCursor().onsuccess=(function(e){ 
          var cursor = e.target.result; 
          if(cursor===null){ 
           boardsLoaded = true; 
           return; 
          } 
          var id = cursor.key; 
          var obj = cursor.value; 
          var lastModified = new Date(); 
          if("lastModified" in obj){ 
           lastModified = obj.lastModified; 
          } 
          service.cache["./board/"+id.toString().toLowerCase()+".json"]={ 
           obj:obj, 
           modified:lastModified 
          }; 
         }); 
         transaction.objectStore("thread").openCursor().onsuccess=(function(e){ 
          var cursor = e.target.result; 
          if(cursor===null){ 
           threadsLoaded = true; 
           return; 
          } 
          var id = cursor.key; 
          var obj = cursor.value; 
          var lastModified = new Date(); 
          if("lastModified" in obj){ 
           lastModified = obj.lastModified; 
          } 
          service.cache["./thread/"+id.toString().toLowerCase()+".json"]={ 
           obj:obj, 
           modified:lastModified 
          }; 
         }); 
         transaction.objectStore("post").openCursor().onsuccess=(function(e){ 
          var cursor = e.target.result; 
          if(cursor===null){ 
           postsLoaded = true; 
           return; 
          } 
          var id = cursor.key; 
          var obj = cursor.value; 
          var lastModified = new Date(); 
          if("lastModified" in obj){ 
           lastModified = obj.lastModified; 
          } 
          service.cache["./post/"+id.toString().toLowerCase()+".json"]={ 
           obj:obj, 
           modified:lastModified 
          }; 
         }); 
         service.post = (function(id,cb){ 
          console.log("DDDDDAFF"); 
          var trans = service.database.transaction(["post"],"readwrite"); 
          trans.onsuccess = (function(e){ 
           var req = e.target.result.objectStore("post").get(id); 
           req.onsuccess = (function(ee){ 
            cb(req.result); 
           }); 
           req.onerror = (function(ee){ 
            console.log("HAAAA?!"); 
            postFetch(id,(function(post){ 
             e.target.result.objcetStore.save(post); 
             cb(post); 
            })); 
           }); 
          }); 
          trans.onerror = (function(e){ 
           console.log("Error with IDDB:",e); 
           threadFetch(id,cb); 
          }); 
         }); 
         service.thread = (function(id,cb){ 
          var trans = service.database.transaction(["thread"],"readwrite"); 
          trans.onsuccess = (function(e){ 
           var req = e.target.result.objectStore("thread").get(id); 
           req.onsuccess = (function(ee){ 
            cb(req.result); 
           }); 
           req.onerror = (function(ee){ 
            threadFetch(id,(function(post){ 
             e.target.result.objcetStore.save(post); 
             cb(post); 
            })); 
           }); 
          }); 
          trans.onerror = (function(e){ 
           console.log("Error with IDDB:",e); 
           postFetch(id,cb); 
          }); 
         }); 
         service.board = (function(id,cb){ 
          var trans = service.database.transaction(["board"],"readwrite"); 
          trans.onsuccess = (function(e){ 
           var req = e.target.result.objectStore("board").get(id); 
           req.onsuccess = (function(ee){ 
            cb(req.result); 
           }); 
           req.onerror = (function(ee){ 
            boardFetch(id,(function(post){ 
             e.target.result.objcetStore.save(post); 
             cb(post); 
            })); 
           }); 
          }); 
          trans.onerror = (function(e){ 
           console.log("Error with IDDB:",e); 
           boardFetch(id,cb); 
          }); 
         }); 
        }); 
       }); 
      })(window.indexedDB); 
     } 
     return service; 
    })); 
}); 

angular.module('JSForum',["JSForumServices"]).config(
    ['$routeProvider', 
    function($routeProvider){ 
    $routeProvider.when('/',{ 
     templateUrl:"forum.html", 
     controller:ForumController 
    }); 
    $routeProvider.when('/board/:id',{ 
     templateUrl:"board.html", 
     controller:BoardController 
    }); 
    $routeProvider.when('/thread/:id',{ 
     templateUrl:"thread.html", 
     controller:ThreadController 
    }); 
    $routeProvider.otherwise({redirectTo:"/"}); 
}]); 

function ThreadController($scope,$routeParams,ForumStorage){ 
    $scope.id = $routeParams.id.toString().toLowerCase(); 
    $scope.thread = null; 
    ForumStorage.thread($scope.id,(function(thread){ 
     $scope.thread = thread; 
     $scope.$apply(); 
     var callback = (function(p){ 
      return (function(post){ 
       $scope.thread.posts[p] = post; 
       $scope.$apply(); 
      }); 
     }); 
     for(var i=0;i<thread.posts.length;i++) 
      if(typeof(thread.posts[i].id) == 'undefined') 
       ForumStorage.post(thread.posts[i],callback(i)); 
     $scope.$apply(); 
    })); 
} 

function BoardController($scope,$routeParams,ForumStorage){ 
    $scope.id = $routeParams.id.toString().toLowerCase(); 
    $scope.board = null; 
    ForumStorage.board($scope.id,(function(board){ 
     var callback = (function(p){ 
      return (function(thread){ 
       $scope.board.threads[p] = thread; 
       $scope.$apply(); 
      }); 
     }); 
     $scope.board = board; 
     console.log("Using board:",$scope.board); 
     for(var i=0;i<board.threads.length;i++) 
      if(typeof(board.threads[i].id)=='undefined') 
       ForumStorage.thread(board.threads[i],callback(i)); 
     $scope.$apply(); 
    })); 
} 

function ForumController($scope,ForumStorage){ 
    $scope.name = localStorage.forumName||"Forum"; 
    $scope.boards = []; 
    ForumStorage.forum("forum",(function(forum){ 
     document.title = $scope.name = localStorage.forumName = forum.name; 
     $scope.boards = forum.boards; 
     var callback=(function(p){ 
      return (function(o){ 
       $scope.boards[p] = o; 
       $scope.$apply(); 
      }); 
     }); 
     for(var i=0;i<$scope.boards.length;i++){ 
      if(typeof($scope.boards[i].id) == 'undefined') 
       ForumStorage.board(forum.boards[i],callback(i)); 
     } 
     $scope.$apply(); 
    })); 
} 
+0

당신은 작동하는 바이올린 (fiddle)을 세우거나 "majorly broken"이 의미하는 것을 정교 할 수 있습니까? 그것은 많은 코드를 통해 읽을 수 있습니다. – Langdon

+0

아직 아무 것도하지 않는다는 것을 의미합니다. 그럴 때 그 행동은 일관성이 없었습니다. 어쨌든 [여기] (https://c9.io/tyler-elric/phoenix/workspace/index.html)는이를 실행하는 Cloud9입니다. –

답변

-1

, 당신은 내 오픈 소스 데이터베이스 라이브러리 그것은 색인화에 캐시 http://dev.yathit.com/api-reference/ydn-db/storage.html을 사용하여 BLOB 저장소에 지속 할 수 있습니다. 포럼 게시물 URI 경로가 레코드의 기본 키로 사용됩니다. LastModified 헤더 값은 IndexedDB에 유지되고 조건부 HTTP 요청에 사용됩니다. blob 저장소는 키의 오름차순 (URI 경로)으로 만 쿼리 할 수 ​​있으므로 포럼 게시물 URI 경로가 생성되어 마지막 게시물이 가장 작습니다. 이렇게하면 가장 작은 알려진 키를 marker으로 지정하여 새 게시물을 쿼리 할 수 ​​있습니다.

예는 https://bitbucket.org/ytkyaw/ydn-auth/src/master/examples/note-app/note-app.js (포럼이 아니라 간단한 메모 앱)에서 찾을 수 있습니다. 새 레코드를 게시하기 위해 signed url을 생성하는 백엔드 서버가 필요합니다.

+0

나는 그것을 좋아한다! IndexedDB 캐싱은 쉽게 꺼내고 다시 사용할 수 있습니까? 아니면 상당히 뿌리깊 은가? –

+0

귀하의 질문에 확신이 없습니다. 캐싱은 선택 사항입니다. –

+0

: 1 ... OOP. ??? –