2012-12-18 3 views
9

나는 상속 된 Ember 응용 프로그램을 리팩토링하고 있는데, 비 mvc 무질서가 꽤 있습니다. 나는 가능한 한 모듈화 된 것을 유지하려고 노력하고 있으며 코드 중복을 방지하기 위해 다양한 UI 구성 요소를 여러 화면에서 재사용하기를 원합니다.Ember.js - 중첩 된/반복 된보기로 콘센트를 대상으로 지정하는 방법과 이러한 UI 레이아웃의 모범 사례는 무엇입니까?

아울렛이 최선의 방법이라고 생각됩니다.

지금 당장 UI가 여러 가지 요소를 표시하며 각 요소는 템플릿으로 구성된보기를 사용하여 렌더링됩니다.

{{#each item in controller}} 
     {{view App.ItemThumbView}} 
{{/each}} 

이보기의 오른쪽 사이드 바는 선택에 따라 변경되는 콘센트입니다. 항목을 선택하면 템플릿 처리 된 하위보기에서 편집 작업 목록을 표시하고 싶습니다. 선택하면 중첩 된 콘센트를 통해 적절한 편집 UI가 나타납니다. 말이 - -

본질적

+---------------------------------------------------+ 
|  Parent Pane 
| 
| +------------------------------+ +----------+ 
| | Device Section    | | Sidebar | 
| |        | | [Outlet] | 
| | +--------+ +---------+  | |   | 
| | | Dev 1 | | Dev 2 |  | |   | 
| | |[outlet]| | [outlet]|  | +----------+ 
| | +--------+ +---------+  | 
| +------------------------------+ 
+--------------------------------------------------+ 

중첩 뷰는 모두 동일한 제어기를 공유하지만 해당 콘센트 선택된보기를 연결할 수 있어야한다. 콘센트 연결에 대한 나의 초기 시도는 결코 표시되지 않습니다. 코드가 전혀 실패하지 않으므로 컨트롤러가 숨겨진 콘센트를 업데이트합니다.

Ember에서 중첩 된보기의 올바른 콘센트를 어떻게 타겟팅합니까? (기껏해야, 사이드 바 콘센트에 충돌 할 수는 있지만 중첩 된 장치 템플릿 내의 콘센트는 안 맞을 것 같습니다.) 그리고 이것은 처음부터 엠버에서 컨텍스트 메뉴를 구현하기위한 합리적인 구조입니까?

* 나의 현재 설정에 설명 , 각 장치 아이템은 동일한 템플릿을 이용하여 렌더링된다. 이 옵션을 선택하면 사이드 바 콘센트가 일부 장치의 메타 정보로 업데이트되고 선택한 장치보기는 해당 콘센트를 편집 메뉴에 연결합니다. 한 번에 하나의 장치 항목에만 '편집'콘센트가 연결됩니다.

여기 콘센트를 사용하는 것이 맞습니까? 아니면 필요에 따라 편집 메뉴를 표시하기 위해 조건부 논리를 템플릿에 놓아야합니까?

업데이트 질문의 모범 사례 부분을 재 작성 :

아울렛 디커플링 구성 요소 및 미래 교정 잠재적보기 스팅에 좋은 것 같다. 하지만 지금은 중첩 된보기를위한 올바른 콘센트에 액세스하는 것이 약간 번거로운 것 같습니다. 또한, 템플릿 내에서 조건 적으로 중첩 될 구성 요소를 항상 알고있는 경우에는보기를 하드 코딩하는 것이 가장 쉬운 방법입니다. 예 :

// within template used for individual result-list items 
{{#if condition }} 
    {{view reusableSubView}} 
{{/if} 

여기서 일을하는 데 선호되는 방법은 무엇입니까? 항상 연결되어 있지 않은 콘센트를 만드는 데 오버 헤드가 있습니까?

답변

13

좋아, 시간이 지나고 이것으로 놀아 본 후에는 중첩 된 뷰 바로 위의 레벨에 명명 된 콘센트를 조건부로 포함하는 것이 가장 좋습니다.

템플릿 내부를 대상으로 신뢰할 수있는 단일 콘센트가 필요하고 런타임에 프로그래밍 방식으로 콘센트의 이름을 지정하는 좋은 방법이없는 것 같습니다.

또한 타겟팅하려는 콘센트는 라우터, 컨트롤러 및 화면의 현재 상태를 렌더링하는 데 사용되는 렌더링 된 템플릿의 중첩에서 상위 템플릿의 어딘가에 있어야합니다. 콘센트가 부모 루트 객체에 의해 배치되지 않은 경우에는 해당 노드를 리프 노드 루트에서 대상으로 지정할 수 없습니다.

내가 예와 함께 설명 보자

내가 원래 내 질문을하기 때문에

, 우리의 UI는 사람들의 목록에 장치 목록에서 변경,하지만 원칙은 동일합니다.

나는 사진이있는 사람 목록을 가지고 있으며 결과 옆에 더 많은 정보를 표시하기 위해 클릭 할 수 있습니다.

<script type="text/x-handlebars" data-template-name="people" > 
    <h3>People in this group</h3> 
    <div class="peeps"> 
     {{#each controller}} 
      {{ view App.PersonView }} 
      {{#if selected }} 
        <div class='too-personal'> 
        {{ outlet veryPersonalDetails }} 
        </div> 
      {{/if}} 
     {{/each}} 
    </div> 
</script> 

와 같이 좀있어 사람 템플릿 :

내가 좋아하는 뭔가를 보이는 사람들이 템플릿을

<script type="text/x-handlebars" data-template-name="person" > 
     {{#linkTo person this}} 
      <div data-desc="person-item" class="item"> 
       <div class="portrait"> 
        <img class="avatar" {{bindAttr src="gravatarUrl"}}/> 
       </div> 
       <div class="statuslabel"><span {{bindAttr class=":label statusLabel"}}>{{statusText}}</span></div> 
       <div class="cTitle">{{fullName}}</div> 
      </div> 
     {{/linkTo}}  </script> 

그리고 세부 사항은 추가 정보 및 편집 옵션 템플릿

<script type="text/x-handlebars" data-template-name="person/details" > 
various edit options  
</script> 

사람 결과를 클릭하면 라우터에서 url을 선택합니다 업데이트 및 스텁을 세부 정보 템플리트에서 상위 사용자 템플리트로 가져올 수 있습니다. 개별 루트로

App.Router.map(function() { 
... 
    this.resource('people', { path: '/people'}, function() { 
     this.resource('person', { path: '/:person_id' }, 
      function() { 
       this.route('details'); 
      } 
     ); 
    }); 

}); 

: 내 라우터는이 같은 설정 내가 원래 PersonView 내에서 콘센트를 시도

App.PeopleRoute = Ember.Route.extend({ 
    model: function() { 
     return App.People.find(); 
    }, 
    setupController: function(controller, model) { 
     controller.set('content', model); 
    } 
}); 

App.PersonRoute = Ember.Route.extend({ 
    model: function(params) { 
     return App.People.peepsById[params.person_id]; 
    }, 

    setupController: function(controller, model) { 
     this._super(controller, model); 
     var person = model; 
// this block ensures that only one person has a selected status at a time 
// ignore the overly-verbose code. I'm cleaning it up tomorrow 
     var ls = App.People.lastSelected; 
     if (ls) { 
      ls.set('selected', false); 
     } 
     person.set('selected', true); 
     App.People.lastSelected = person; 
     controller.set('content', model); 
    }, 

    renderTemplate: function() { 
     // person is actually stored in router 'context' rather than 'model' 
     // omg! 
     var x = this.controllerFor('personDetails').set('content', this.get('context')); 
     this.render('person/details', { // render person/details 
      into:'people', // into the people template 
      outlet: "veryPersonalDetails", // at the veryPersonalDetails outlet 
      controller: x // using the personDetails controller for rendering 
     }); 

// additional rendering in sidebar outlet for testing 
     this.render('person/details',{ 
      into:'people', 
      outlet: "personDetails", 
      controller: x 
     }); 

     console.log("@@@ we did it!"); 
    } 
}); 

을하지만,이 내 사람 템플릿으로, 작동하지 않을 것입니다 실제로 사람 경로 내에서 렌더링되었습니다. 애플리케이션이 사람 경로에 도달 할 때까지 컨트롤러는 이미 모든 사람들을 목록에 렌더링했습니다. 목표로 삼고 싶은 사람은 오래 전에지나 갔다.

경로를 부모 템플릿과 원하는 자식 템플릿 사이의 중개자로 사용함으로써이 작업을 수행 할 수있었습니다.

마지막으로, 템플릿 내에서 중첩 된 뷰를 명시 적으로 선언할지 또는 콘센트를 사용할지에 대한 질문에 대해서는 콘센트가 훨씬 깨끗하다고 ​​믿습니다. 이 솔루션은 경로 주변에서 약간의 작업을 수행했지만 지나치게 복잡한 템플릿 객체를 사용하는 것보다 훨씬 바람직합니다. 이제는 렌더링과 관련된 경로 외에는 아무 것도 건드리지 않고 임의로 복잡한 템플릿 중첩을 수행 할 수 있습니다.

또한이 솔루션은 사용되지 않는 콘센트의 오버 헤드를 제거합니다. 나는 당신이 사용하고자하는 콘센트만을 가져야한다고 믿습니다. 빈 콘테이너 객체로 'dom'을 버리는 것이 아닙니다.