2014-09-05 3 views
1

이것이 내가 Firebase 데이터 스토어에서 실시간으로 주연/좋아하는 (커뮤니티의 경우) 작업을 수행하게 된 원인입니다. 그것은 엉망이고 확실하게 나는 몇몇 근본을 놓치고있다.관찰 목록 및지도, Firebase, 오 마이. 이 혼란을 어떻게 개선 할 수 있습니까?

여기 내 요소는 각각 Map community으로 표시된 List communities에 저장된 커뮤니티를 가져옵니다. 여기

getCommunities() { 
    // Since we call this method a second time after user 
    // signed in, clear the communities list before we recreate it. 
    if (communities.length > 0) { communities.clear(); } 

    var firebaseRoot = new db.Firebase(firebaseLocation); 
    var communityRef = firebaseRoot.child('/communities'); 
    // TODO: Undo the limit of 20; https://github.com/firebase/firebase-dart/issues/8 
    communityRef.limit(20).onChildAdded.listen((e) { 
     var community = e.snapshot.val(); 

     // snapshot.name is Firebase's ID, i.e. "the name of the Firebase location", 
     // so we'll add that to our local item list. 
     community['id'] = e.snapshot.name(); 
     print(community['id']); 

     // If the user is signed in, see if they've starred this community. 
     if (app.user != null) { 
     firebaseRoot.child('/users/' + app.user.username + '/communities/' + community['id']).onValue.listen((e) { 
      if (e.snapshot.val() == null) { 
      community['userStarred'] = false; 
      // TODO: Add community star_count?! 

      } else { 
      community['userStarred'] = true; 
      } 
      print("${community['userStarred']}, star count: ${community['star_count']}"); 

      // Replace the community in the observed list w/ our updated copy. 
      communities 
      ..removeWhere((oldItem) => oldItem['alias'] == community['alias']) 
      ..add(community) 
      ..sort((m1, m2) => m1["updatedDate"].compareTo(m2["updatedDate"])); 

      communities = toObservable(communities.reversed.toList()); 
     }); 
     } 

     // If no updated date, use the created date. 
     if (community['updatedDate'] == null) { 
     community['updatedDate'] = community['createdDate']; 
     } 

     // Handle the case where no star count yet. 
     if (community['star_count'] == null) { 
     community['star_count'] = 0; 
     } 

     // The live-date-time element needs parsed dates. 
     community['updatedDate'] = DateTime.parse(community['updatedDate']); 
     community['createdDate'] = DateTime.parse(community['createdDate']); 

     // Listen for realtime changes to the star count. 
     communityRef.child(community['alias'] + '/star_count').onValue.listen((e) { 
     int newCount = e.snapshot.val(); 
     community['star_count'] = newCount; 
     // Replace the community in the observed list w/ our updated copy. 
     // TODO: Re-writing the list each time is ridiculous! 
     communities 
      ..removeWhere((oldItem) => oldItem['alias'] == community['alias']) 
      ..add(community) 
      ..sort((m1, m2) => m1["updatedDate"].compareTo(m2["updatedDate"])); 

     communities = toObservable(communities.reversed.toList()); 
     }); 

     // Insert each new community into the list. 
     communities.add(community); 

     // Sort the list by the item's updatedDate, then reverse it. 
     communities.sort((m1, m2) => m1["updatedDate"].compareTo(m2["updatedDate"])); 
     communities = toObservable(communities.reversed.toList()); 
    }); 
    } 

우리가 다시 대체 별을 전환 : 그것은 변경된 별의 결과에 따라 각 지역 사회지도 카운트 변경하고 사용자의 별표 상태 및 다른 재미로 그 목록을 여러 번 재 작성해야 우리가 영향을받는 지역 사회지도의 수를 업데이트하고, 따라서 그것을 반영하기 위해 목록을 다시 작성으로 관찰 된 지역 사회는 몇 번 나열

toggleStar(Event e, var detail, Element target) { 
    // Don't fire the core-item's on-click, just the icon's. 
    e.stopPropagation(); 

    if (app.user == null) { 
     app.showMessage("Kindly sign in first.", "important"); 
     return; 
    } 

    bool isStarred = (target.classes.contains("selected")); 
    var community = communities.firstWhere((i) => i['id'] == target.dataset['id']); 

    var firebaseRoot = new db.Firebase(firebaseLocation); 
    var starredCommunityRef = firebaseRoot.child('/users/' + app.user.username + '/communities/' + community['id']); 
    var communityRef = firebaseRoot.child('/communities/' + community['id']); 

    if (isStarred) { 
     // If it's starred, time to unstar it. 
     community['userStarred'] = false; 
     starredCommunityRef.remove(); 

     // Update the star count. 
     communityRef.child('/star_count').transaction((currentCount) { 
     if (currentCount == null || currentCount == 0) { 
      community['star_count'] = 0; 
      return 0; 
     } else { 
      community['star_count'] = currentCount - 1; 
      return currentCount - 1; 
     } 
     }); 

     // Update the list of users who starred. 
     communityRef.child('/star_users/' + app.user.username).remove(); 

    } else { 
     // If it's not starred, time to star it. 
     community['userStarred'] = true; 
     starredCommunityRef.set(true); 

     // Update the star count. 
     communityRef.child('/star_count').transaction((currentCount) { 
     if (currentCount == null || currentCount == 0) { 
      community['star_count'] = 1; 
      return 1; 
     } else { 
      community['star_count'] = currentCount + 1; 
      return currentCount + 1; 
     } 
     }); 

     // Update the list of users who starred. 
     communityRef.child('/star_users/' + app.user.username).set(true); 

    } 

    // Replace the community in the observed list w/ our updated copy. 
    communities.removeWhere((oldItem) => oldItem['alias'] == community['alias']); 
    communities.add(community); 
    communities.sort((m1, m2) => m1["updatedDate"].compareTo(m2["updatedDate"])); 
    communities = toObservable(communities.reversed.toList()); 

    print(communities); 
    } 

우리가 때 응용 프로그램을 다시 지역 사회의 목록을 얻을 수있는 다른 막무가내도있다 .changes 왜냐하면 우리는 앱과리스트 초기에 app.user를로드하기 만하기 때문이다. 그리고 이제 우리는 우리가 필요로하는 사용자를 가지고있다. 적절한 별을 켭니다. 그래서 내 첨부()과 같습니다

attached() { 
    app.pageTitle = "Communities"; 
    getCommunities(); 

    app.changes.listen((List<ChangeRecord> records) { 
     if (app.user != null) { 
     getCommunities(); 
     } 
    }); 
    } 

이, 내가 그냥 별을 받고있을 수 및 업데이트가 영향을받는 각 지역 사회지도 한 후 관찰 된 커뮤니티 목록을 다시 채우기 말했다 보인다, 그러나 그것은 그것의 이상입니다.

전체 일 : https://gist.github.com/DaveNotik/5ccdc9e74429cf87d641

  1. 나는이 모든지도/목록 관리를 향상시킬 수있는 방법

    , 예를 들어, 커뮤니티지도를 변경할 때마다 전체 커뮤니티 목록을 다시 작성해야합니까? 나는 그것을 다르게 생각해야 하는가?

  2. 이 모든 Firebase 쿼리는 어떻게됩니까? 확실히 더 좋은 방법이 있지만, 실시간으로 유지하려면 많은 작업이 필요하고, 요소가 연결되고 분리되기 때문에 매번 getCommunities()를 실행해야합니다. OOP 방식으로 객체를 만들지 않으면 요소가 첨부 될 때마다 항상 관찰해야합니다. 나는 그 기초를 놓치고있다.

  3. 이 app.user는 app.user (우리는 별을로드하려고 함)를 갖기 전에 목록을로드하는 경우를 처리합니다. 더 나은 방법이 있습니까?

  4. 기타 어리석은가요?

큰 질문입니다. 제가 앞으로 나아갈 때 올바른 접근법을 다룰 수 있도록 도와 주셔서 감사합니다!

+0

응용 프로그램의 전체적인 그림을 설명해 주시겠습니까? 사용자의 관점에서 어떻게 작동해야하며 여러 사용자가 동시에 앱을 사용하는 동안 어떤 일이 일어나야합니다. – grohjy

+0

예. 사람들이 참여하고 항목을 게시 할 수있는 커뮤니티. 항목은 여러 커뮤니티에 살 수 있습니다. 사용자는 커뮤니티와 항목에 별표를 표시하고 좋아할 수 있습니다. 모든 항목이 실시간이므로 아이템을 좋아하니 연결된 모든 클라이언트의 업데이트 된 수를 반영해야합니다. 여러 곳에서 로그인 한 경우 별표는 로그인 한 모든 곳에서 반영되어야합니다. 아이디어를 얻으려면 http://mycommunity.org를 참조하십시오. –

답변

3

난 당신이 서버 데이터베이스와 실시간 동기화 응용 프로그램의 데이터를 유지하려는 경우, 선택할 수있는 두 가지 방법이있다 생각한다.

1 폴링 (풀 방법 즉, 클라이언트가 서버에서 데이터를 가져옵니다)

응용 프로그램 설문 조사 서버에 업데이트 된 데이터를 요청합니다. 폴링은 자동 (예 : 60 초 간격) 또는 사용자 요청 (= 새로 고침) 일 수 있습니다. 짧은 자동 간격으로 인해 서버에 부하가 많이 걸리고 긴 간격으로 실시간 느낌을 잃게됩니다.

2 전이중 (푸시 방법 ie.서버가 클라이언트에 데이터를 푸시 할 수 있음)

응용 프로그램과 서버간에 전이중 연결이되어 서버는 데이터 또는 클라이언트가 사용할 수있는 데이터 알림을 보낼 수 있습니다. 그런 다음 클라이언트는 데이터를 검색할지 여부를 결정할 수 있습니다.

이 방법은 순 트래픽과 서버로드를 최소화하고 실시간 업데이트를 제공하기 때문에 현대적인 방법입니다.

firebase는 이러한 종류의 업데이트로 자랑하지만 전적으로 dublex이거나 폴링의 영리한 방법인지는 잘 모르겠습니다. Websocket 프로토콜은 실제 전이중 연결이며 다트 서버가 지원합니다. 서버에서


업데이트 된 데이터를 포함 할 수 있습니다 :

1 기본적으로 서버가 전체 데이터 세트 (= 초기 쿼리)를 전송하고 서버가 "알고"하지 않습니다

전체 데이터 세트 아무것도 업데이트 된 데이터 알맞은 작은 데이터 세트를 가지고 있다면 이것은 가장 쉬운 방법입니다. 많은 경우에 큰 데이터 집합 사이에 매우 작은 데이터 집합이 있으므로이 방법이 유용 할 수 있습니다.

2 만

서버가 수정 타임 스탬프, 즉 기반으로 데이터 집합을 보낼 수있는 새로운 데이터를 포함한 데이터 세트. 데이터베이스의 레코드가 변경 될 때마다 업데이트 타임 스탬프가 저장되고이 타임 스탬프에 따라 쿼리를 필터링 할 수 있습니다. 즉, 애플리케이션은 데이터를 마지막으로 업데이트 한 시점을 알고 새로운 데이터를 요청합니다.

3은 레코드를

서버가 업데이트 된 데이터를 추적 애플리케이션에 보낸다 변경. 변경 사항이 발생하면 레코드별로 레코드를 전송하거나 더 큰 청크를 보내도록 서버에서 데이터를 수집 할 수 있습니다. 이 방법을 사용하려면 각 클라이언트에 올바른 데이터를 보내려면 연결된 모든 클라이언트를 추적하는 서버가 있어야합니다. 클라이언트에 인증 프로세스를 추가하면 즉. 모든 데이터가 모든 사람에게 전송 될 수있는 것은 아니며 매우 복잡 할 수 있습니다.

가장 쉬운 방법은 업데이트 된 데이터에 대해 메소드 번호 2를 사용하는 것입니다.


마지막 일 ...

데이터를 수신 무엇을 할까?

1 핸들 모두 같은 새로운

응용 프로그램이 업데이트 된 데이터를 수신하는 경우, 그것은/모든 목록과지도 명확를 파괴하고 다시/새로운 데이터로 리필됩니다. 이것에 대한 일반적인 문제는 사용자가 페이지에서 현재 위치를 잃거나 데이터 사용자가 점프하는 것입니다. 애플리케이션이 어떤 이유로 든 이전 데이터를 수정하거나 확장 한 경우 모든 변경 사항이 손실됩니다. 사용자가 새로 고침을 요청하면이 방법이 정상적으로 작동합니다.

2 업데이트 변경된 데이터 만

응용 프로그램은 결코 그냥 새로운 수신 된 데이터로를 업데이트 초기 목록 또는 맵을 클리어하지 않습니다. 일반적으로 특정 요구 (예 : 특정보기)를 위해 쿼리 된 데이터에서 새로운 결합 된 맵을 생성합니다. 결합 된지도에는 특정보기에 표시하려는 모든 정보 (초기 쿼리에 필드에 대한 데이터가없는 경우에도 기본값)가 포함되어 있으며 새 값을 업데이트하기 만하면됩니다.

업데이트 된 정보에 목록에 새 회원이 필요한 경우 끝에 추가하면됩니다.

업데이트 된 정보가 목록에서 삭제해야하는 경우 추가 필드 "활성"을 사용하여 목록 /지도를 필터링하는 것이 좋습니다. 필터링을 사용하면 참조 사항 등을 잃지 않습니다.

데이터를 정렬하거나 필터링해야하는 경우보기 또는 사용자 요청으로 수행해야합니다. 기본적으로 데이터는 애플리케이션에 저장되고 필요에 따라 업데이트됩니다. 사용자가 특정 방식으로 데이터를 볼 필요가있을 때, 뷰는 데이터를 적절한 방식으로 표시해야합니다. 이를 모델 - 컨트롤러 - 뷰 (model-controller-view)라고하며, 주요 아이디어는 데이터를 뷰와 분리하는 것입니다.

이 긴 답변은 여러분의 질문에 답하지 못해서 죄송합니다.하지만이 도전을 더 작은 덩어리로 줄이려고했습니다. 여러 번이 청크 사이의 인터페이스를 볼 수 있으며 이러한 인터페이스를 사용하여 코드를 멋지게 디자인하고 구성 할 수 있습니다.

+0

감사합니다 @ grohjy! 거기에 좋은 통찰력. ViewModels를 사용하여 사물을 단순화하고 향상시키는 솔루션을 발견했습니다. 곧 답변으로 게시하려고합니다. –