YUI 3 응용 프로그램에 대한 레시피

년 | 2시 52분에서 2011년 4월 1일는 Satyam에 의해 오전 발달 | 18 코멘트

YUI 3 모듈 주위에 애플 리케이션을 구축할 수 있도록 설계되었습니다. 나는 잘 그의 프레 젠 테이션에 니콜라스 Zakas에 의해 설명되고 있기 때문에 모듈이 무엇인지 논의하지 않습니다 확장 가능한 자바 스크립트 애플 리케이션 아키텍처 . 난 단지이 모듈을 빌드하는 방법에 충실합니다. 내가 무슨 말을할지의 대부분은 여러 가지 다른 대안과 함께 온라인 문서에서 찾을 수 있습니다, 결국 그 좋은 문서의 요점은 다음과 같습니다 일을 모든 가능한 방법에 대해 알려. 이것이 많은 사람간에 그 일을 단 한 가지 방법, 조리법이다 이유입니다. 니콜라스 '에서 알 수 있듯이 단순한 문서가 아닌 책이기 때문에 그것은 또한 아닌 여러 레이어로서 함께 smallish 응용 프로그램을 가정합니다.

모듈을 식별

첫 번째 단계는 우리가 필요한 모듈을 식별하는 것입니다. 제목 표시줄, 메뉴 표시줄, 컨텐츠, 측면 패널 또는 다른 무엇이든 거기에있을 : 좋은 접근 방법은 개별 섹션으로 응용 프로그램 화면의 디자인을 깔끔히 시작하는 것입니다. 그런 다음 라이브러리가 제공하는 일을 좀 봐. 예를 들어, YUI 3에는 메뉴가 없지만,이 노드 MenuNav 플러그인 정렬되지 않은 중첩된 목록 <UL> 요소로 만들어진 기본적인 메뉴 구조를 취하고 활성 메뉴로 그들을 내었습니다. 또는 확인 할 수 있습니다 YUI 갤러리를 기본 구성 요소가. 여러분이 직접 작성해야하는 그 레이아웃에 상자가 어디 어쨌든, 결국 그러니 그런 일을하게 놔두지, 지점에 도달합니다.

저도 같은 이름으로 자체 파일 및 자체 디렉토리에 각각의 모듈을 놓는 것이 좋습니다. 따라서, weather 모듈에있을 것입니다 weather/weather.js . 당신의 모듈은 몇 가지 스타일링, 일부 CSS와 이미지 파일을 필요로 할 가능성이 있기 때문에 이에 대한 이유는 그것이 쉽게 내장된 로더가 쉽게 찾을 수있는 곳에 당신이 경우에 그들을 배치하면 기본 스타일 시트에 대해 만들어 에있을 것입니다 weather/assets/skins/sam/weather.css 함께 기타 자산, 이미지 등등과 함께. 이것은 당신이 이미 어쨌든 일이 이렇게 할 YUI 작성기를 사용하지 않는 가정이지만 다른 이야기입니다. 폴더 이름의 assetsskins 더 많거나 적은 단정입니다 sam 그러나 아직 명확하지 않다. 그것에 대한 기본 값입니다 skin 그 디자이너 샘 린드의 이름을 따서 명명 YUI, 함께 제공된 기본 스킨이기 때문에 로더의 속성입니다. 이것이 수 있듯이, 여러분은 자신의 스킨에 이름을 올려놓은 무료이며 skin 속성을 로드할 수 있지만 간단하게하기 위해, 기본으로 돌아가 보자 YUI를 알려줄 수 있습니다.

모듈 파일 템플릿

이것이 내가 순간에 설명 할게 내가 더 자주 사용하는 파일 구조이다 :

  / * jslint devel : 사실, undef : 사실, newcap : 사실, 엄격한 : 사실, maxerr : 50 * / 
 / * 전역 YUI * / 
 / ** 
  * 모듈 이름이 모듈은 ㅋ ㅋ ㅋ을 만듭니다 
  * @ 모듈은 모듈 이름이 
  * / 
 YUI.add ( '모듈 이름이'함수 (Y) { 
     "엄격한을 사용"; 
     모듈에서 사용 / / 편리한 상수와 단축키 
     VAR 랭 = Y.Lang, 
         CBX = 'contentBox' 
         BBX = 'boundingBox'
         NAME = 'XXXX'; 

 
     / ** 
      * XXXX 클래스 않습니다 .... 
      * @ 클래스 XXXX 
      * @ 확장합니다 위젯 
      * @ WidgetParent 사용 
      * @ 생성자 
      * @ CFG {오브젝트} 구성 속성 
      * / 
     Y.Xxxx = Y.Base.create ( 
         이름, 
         Y.Widget, 
         [Y.WidgetParent가] 
         { 
             / / 여기에 인스턴스 멤버 
         } 
         { 
             특별히 여기 / / 정적 멤버 : 
             ATTRS : { 
             } 
         } 
     ); 

 
 }, '0 0.99 ', { 
     필요 : [ '위젯', '위젯 - 부모'], 
     skinnable : 진실 
 }); 

댓글의 첫 번째 두 줄은의 이익을위한 JSLint , 정말 권장 자바 스크립트 검증 도구입니다. 당신을 가야한다면 웹 버전 , 옵션을 설정하는 박스가 있습니다. 그 옵션 상자의 아래쪽에있는이 파일 자체로 이러한 옵션을 인코딩하는 방법을 볼 수 있습니다. 당신은 YUI 빌더를 사용하는 경우 그것은 당신을 위해 JSLint를 실행하고 당신을 위해이 옵션을 설정하지만, 원한다면 당신은 여전히​​ 개별 파일에 대해 그들을 무시할 수 있습니다.

문서 주석은 YUI API 문서 빌더위한 것입니다. 당신이 그 API를 워드 프로세서에 대한 초기 템플릿을 포함한다면 그것은 두통의 적은 결국 당신이 그들을 채울 것입니다. 응용 프로그램이 성장하고 모든 것을 기억할 수없는 것처럼, 당신이 그들이 필요합니다.

이제 코드의 첫 번째 실제 라인 온다 YUI.add() 는 성명. 이것은 YUI 로더 모듈과 정보를 다른 여러 가지의 이름과 내용을 알 수있는 방법입니다. 모듈 이름은 대개 단어 사이의 모든 소문자 및 하이픈으로 지정됩니다. 이들은에서 보는 이름이다 API 문서 왼쪽 상단 대부분의 색인에. 당신은 규칙이 엄격히되지 볼 수 있습니다, 몇몇 모듈 이름은 어떤 하이픈이 없습니다. 어쨌든, 이것은 한 당신은 일관되게 그것을 사용으로 대부분으로 귀하에게 달려 있습니다.

의 두 번째 인수 YUI.add() 문장은 통상라는 하나의 인수, 수신 함수 Y . 그 함수는 모듈의 본문을 포함하고 Y 여러분이 요청한 모든 다른 YUI와 갤러리 모듈을 찾을 수있는 것입니다 YUI의 sandboxed 인스턴스에 대한 참조입니다. 그 코드 상자의 아래쪽에 점프, 당신의 인자의 나머지 볼 수 있습니다 .add() 메서드 버전 ( '0.99′ 아직 그런 단계가 아닌)과이 모듈에 대한 구성, AA 시리즈의 가진 개체를 속성. 여기,이 모듈이 필요하다는 얘기 로더 widgetwidget-parent 하고 피부를 가지고 있습니다. 리스트 widget 이후 중복 widget-parent 이미 필요로 widget 하지만, 그걸로 자신을 방해하지 로더는 두 모듈을로드하지 않으며, 하나의 의존성을 떨어뜨 나중에 지점에있다면, 당신은 체크하지 않아도됩니다 전부 상태와 함께 로더 계약을하게 : 다른 가정을 위해 당신이 만든했을 수도 있습니다. 당신을위한 API 문서에있는 모든 옵션의 목록을 확인할 수 있습니다 .addModule() 로더의 방법을.

함수의 본문 내에서 우선이다 “use strict”; 선언. 이것은이 시점에서 당신이 미래에 발생할 수있는 모든 플랫폼과 호환성을 보장하기 위해 안전한 편이 당신을두고 ECMAScript 5 표준을 준수하는 코드입니다. 나이가 통역의 경우이 선언은 아무것에 할당되지 않고 무시되는 문자열보다 더 아무것도 없다. “use” 선언은 함수의 범위를 가지고 있으며 그것은 오직 당신이 정의하는 모듈에 영향을 미치는 있도록 파일의 상단보다 함수 본문 내에 배치하는 것이 안전합니다. 당신이 파일의 상단에 그것을 배치하면 그것은 또한 나중에 로드할 다른 JavaScript 파일에 적용하고, 그 중 많은 ES5을 준수하지 않을 수도 있습니다.

그렇다면 자바 스크립트 상수의 개념이 없기 때문에 전용 사용료에 상수 단축키와 상수를, 나와. 상수는 종종 다른 언어에있는대로 우리는 모든 대문자와 밑줄에 이름을 지정합니다. 상수를 사용하는 두 가지 좋은 이유, 특별히 문자열 상수가 있습니다. 먼저 동일한 문자열을 여러 번 쓸 때, 당신은 그들 중 하나를 잘못 입력 수도 있다고하고 버그 팝업 때만 확인할 수 있습니다. 당신이 상수를 사용하는 경우 그것이 정의되지 않은 것입니다 때문에 당신이 상수 이름을 잘못 입력하면 JSLint는 경고가 표시됩니다. 두 번째 이유는 문자열 리터럴 수 없어있을 때 상수 이름 압축할 수 있기 때문에 YUI 압축기 더 나은 일을 할 수있다는 것입니다. 명명된 상수를위한 좋은 후보자 구성 속성 및 이벤트의 이름입니다.

같은 단축키, Lang 위한 Y.Lang 그들이 덜 입력할 수 있기 때문에 또한, 좋은, 통역사가 (각각의 점이 객체 멤버에 새로운 검색을 의미합니다) 평가할 적은을 가지고 있으며, 그들은 YUI 압축기에 의해 압축 할 수 있습니다.

클래스에 대한 API 문서 주석 후, 우리는 실제 선언을 해보세요. 우리의 재산으로 우리의 클래스를 선언해야 Y 따라서 Y.Xxxx . 나의 제안은 사용하는 것입니다 Y.Base.create() 여기에 그림과 같이 그것을 만드십시오. 단지에서 파생된 클래스 만들 수 있습니다 Base (그리고 Widget 도의 하위 클래스입니다 Base )하지만 다른 방법으로 그것을해야하는 비정상적인 수 있도록 그 말은 당신이 사용 모듈의 대부분을 커버합니다. 첫번째 인자는 모듈의 이름은 NAME 에 대해 설명하는 속성을 Base 구성 요소입니다. 통상, NAME 속성은 클래스 이름의 낙타 케이스 버전입니다. 이 이름은 이벤트 (같은 콜론 앞에 부분에 대한 접두사로 사용됩니다 “io:success” 에서 생성된 CSS의 클래스 이름) Widget 클래스 (예 : “yui3-xxxx-content” )와의 기본 구현을위한 toString() , 어떤 당신은 종종 디버거에 의해 흔적으로 볼 수 있습니다. 나 여기 상수의 값을 사용하여 NAME 의 클래스 정의하기 NAME 속성을.

두 번째 인수는 extents 클래스입니다. 당신은 가끔 하나를 사용합니다 Y.Base 더 사용자 인터페이스도지지 않습니다 유틸리티 모듈, Y.Widget UI를 가질 것이다 분들을 위해 Y.Plugin.Base 플러그인 또는에서 파생된 다른 클래스 Y.Base 등 언제나처럼을 당신은 이미 사용하여 만들 수도 Y.Base.create() .

세 번째 인수는 사용할 확장자를 가지고 있습니다. 확장 누구의 속성과 메서드가 클래스에 혼합했을하려는 클래스입니다. 확장을위한 좋은 후보자이다 ArrayList 대한 Base 또는 모든 Widget-Xxxx 에 대한 submodules Widget . Attribute , EventTargetPluginHost 로 이미 섞여 온 Base 당신은 항상 거기에있는다는 것은이 세 사람은 믿을 수 있습니다. 확장 기능은 살펴보면, 매우 강력하다 위한 소스 코드 Overlay , 당신은하지만 아무 것도 없습니다 볼 수있는 Widget 과 확장. 세 번째 인수는 배열 수 있도록 몇 가지 확장 요소로 혼합이 될 수 있습니다.

마지막으로, 우리는 실제 코드에 도착. 넷째와 다섯째는 인수가 인스턴스와 클래스의 정적 멤버를 포함하는 객체 리터럴 모두입니다. 인스턴스 멤버는 클래스에 가지 속성과 메서드가 prototype , 각 인스턴스 복사를 주로하여 레퍼런스 있어야한다는 사람 this . 정적 멤버는 인스턴스에 의해 공유됩니다 것들이다.

구성 속성

이러한 정적 멤버 중 가장 중요한 것입니다 ATTRS 속성입니다. 이것은 구성 클래스가 갖는 속성이 나열되어 있습니다. 예를 들어, 수있게 우리가라는 구성 속성을 갖고 싶어라고 value 숫자 값을 보유하고 초기에 0으로 설정합니다. 다섯 번째 인자 내에서, 우리는 이처럼 그것을 선언한다 :

  ATTRS : {
     값 : {
         값 : 0,
         유효성 : Lang.isNumber
     }
 } 

우리의 속성의 수를 나열할 수 ATTRS 속성 및 각은 여기 나타났으 중 두 여러 옵션으로 구성할 수 있습니다. 당신은에있는 옵션의 나머지 부분에 대해 읽을 수 addAttr() 의 메소드 Attribute . 볼 수 있듯이, 나는 사용한 적이 Lang 전 모듈 선언의 시작 부분에서 선언된 바로 가기를. validator 부울을 확인하고 반환하는 값을 걸리는 함수 여야합니다. 모든 Y.Lang.isXxxx 방법은 정확히 그들이 직접 사용할 수 있습니다 그렇게 해. 더 정교 validators, setters 또는 getters 들어, 함수를 정의해야합니다. 제가 문자열 함수의 이름을 제공하는 것이 좋습니다, Attribute 실제 함수에 함수 이름을 해결을 담당한다. 예를 들어, 전을 정의할 수있다면, 말, validCodes 중 하나의 유효한 코드 또는 유효한 코드의 배열을 취할 수 속성이 있지만 항상 배열을 반환해야, 제가 할 것이다 :

  ATTRS : {
     validCodes : {
         세터 : '_setValidCodes'
     }
 } 

우리는 선언해야 _setValidCodes 의 네 번째 인수에 다른 인스턴스 멤버를 따라 방법을 Y.Base.create() :

  _setValidCodes : 함수 (값) {
     {(! Lang.isArray (값)) 경우
         값은 = [값];
     }
     값을 반환;
 } 

그것은 setters, getters하고 있지만 같은 validators의 가장 사소한 별도의 인스턴스 함수를 선언하고 있도록 최선 Attribute 실제 함수 호출로 함수 이름을 해결합니다.

일반적으로 보조 효과를 생산하지 않는, 위의 그림과 같이, 가치를 정상화하는 데 setters를 사용합니다. 모든 구성 속성 변경에서 특성을 방지할 수있게하기 전에 이벤트로 변경 이벤트 전후 발사됩니다. 어떠한 보조 효과, 아니 세터를 생성하기 위해 이러한 변경 이벤트를 사용합니다. 그때까지 당신은 다른 코드 (설정) 변경 이벤트 이전에 가입하고 그것을 취소했을 수도 있기 때문에 아무 것도 설정되지 않도록 속성을 예방할 없다 는걸 알고 있기 때문에 이후에 이벤트가 가장 좋습니다.

당신은 속성이 어느 정도 엄격한 당신의 검사기와 세터를 정의하는 방법에 따라 만들 수 있습니다. 당신이 유효성 검사기가 매우 제한하게되면 속성에만 유효 값을 수락 매우 엄격한 것이다,이 경우에는 세터 필요가있을 수도 있습니다. 반면에, 당신은 허용 무언가로받은 값을 마사지하는 세터에서 완전히 전혀 유효성 검사기를 사용하고 의존하지 않을 수 있습니다. 예를 들어, 여러분은이 두 가지 중을 가질 수 :

  유효성 : Y.Lang.isBoolean은, / /​​ 속성이 엄격히 부울을 허용하도록
 세터 : 부울, / /​​ 속성이 어떤 가치를 받아들이고 가지고 만드는가 부울로 변환시켜 버립니다 

Setters도 validators로 검색할 수 있습니다. Setters는 속성에 할당되는 그러나 그들은 또한 되돌릴 수 값으로 반환합니다 Y.Attribute.INVALID_VALUE 검사기 그것을 거부했던 것처럼, 속성이 변경되지 떠날 것입니다.

전 구성 속성을 정의할 때 나는 종종 그것을 상수를 정의, 모듈의 상단 (따라시 어느 제가 장소 CBX , BBX 예를 들면 및 단축키) :

  VAR VALUE = '값'
     VALID_CODES = 'validCodes'; 

기회는 내가 모듈 내에서 이러한 구성 속성을 여러 번 사용했는데 이것이 좀 문제를 저장할 것입니다. 속성을 선언했을 때 그러나, 조심, 그 상수를 사용하지 않는, 이러 지마 :

  ATTRS : {
     / / ***이 *** / / 그러지 마세요
     값 : {
         값 : 0,
         유효성 : Lang.isNumber
     }
 } 

이렇게하면이라는 속성 넣기 VALUE 대신 value . 이것은 자바 스크립트 문제가 아니라 YUI 하나입니다. 또한, 조심하지의 대한 넘은 구성 기본 클래스 또는 확장 중 하나를 이미 선언된 속성. Widget 이미 (참조 선언된 속성들을 잔뜩 가지고 테이블을 )하고 거의 추가하지 않을 있지만 boundingBox , 자신을 속성이 쉽게 잊지 수도 visible , disabled , height 또는 width 이미 정의되어 있습니다. 귀하의 용도가 뭔지 일치하면 Widget 들을 위해 사용하는, 모두 잘 될 것입니다. 그래서 당신이 그들 중 하나의 정의를 변경할 수있다. Y.Base.create() 무슨 말에 대한 기본값을 변경하려면, 그래서 기본 클래스의 기능과 확장 기능의 구성 속성의 정의를 병합 기존의 속성, 당신은 하위 클래스에서 다시 속성을 선언하면됩니다.

당신이 배열 또는 개체와 속성을 초기화하는 뜻 경우주의하십시오. 개체 (및 배열 개체입니다)는 참조에 의해 전달되며이 개체와 속성을 초기화하는 경우, 그들은 모두 같은 객체를 가리키는 끝낼 수 있었을 것이므로 (배열에서 항목을 제거하거나 속성을 추가 그것의 일부를 변경했을 때 객체)가. 한 번에 인스턴스를 모두 변경 끝내고 Base 그러나 안전하게 개체와 배열 리터럴에 속성을 초기화하실 수 있습니다 일부 내부 논리를 가지고 있습니다. 초기화 값은 리터럴 개체 또는 배열이있다면 다음 Base 그것을 복제합니다. 사용 valueFn 옵션을 또는 다른 개체의 클래스 이니셜 라이저에서 초기화합니다.

기타 정적 멤버

당신의 다섯 번째 인수에 정의할 수 있습니다 두 개의 다른 정적 멤버가 있습니다 Y.Base.create() . 당신이 플러그인을 만드는 경우, 당신은 절대적으로 선언해야 NS 당신이 플러그인이 작동하지 않습니다하지 않을 경우, 부동산을하고 자동으로 실패합니다. NS 속성은 언제든지 기존의 재산을 재정의하지 않도록 당신의 이름을 선택할 때 염두에 보관, 호스트 개체 내에서 플러그인을 저장할 프로퍼티 이름으로 사용됩니다 문자열로 설정해야합니다.

당신이 위젯을 만드는하고 프로 그레시브 향상을 지원하기 위하여 그것을 계획한다면, 사용 HTML_PARSER 정적 속성을. 이것은 기존의 HTML과 CSS3 선택자 중 하나 또는 그 가치를 생산할 것입 기능을 파싱에서 설정 구성 속성의 이름을 따서 명명 속성을 포함하는 개체로 설정됩니다. 참조 그레시브 향상 도구 사용자 가이드에 있습니다.

당신은 또한 당신의 클래스를 사용하여 개발자에 의해 사용되는 값을 제공 할 수 있습니다. 파일의 맨 위에 선언된 상수는 모듈 자체가 외부에서 전혀 보이지 않습니다. 여러분이 공공 상수를 제공하려면이 할 수있는 장소입니다. 이러한 예는입니다 HEADER , BODYFOOTER 의 상수 WidgetStdMod (그것을 사용하는 당신이 실제로 정규화된 이름을 사용해야 Y.WidgetStdMod.BODY 등)을.

인스턴스 멤버

의 네 번째 인수 Y.Base.create() 들어갈 것이다 속성과 메서드가 prototype 만든 클래스의. 보통, 우리는 나중에 첫번째 속성과 메서드를 선언합니다. 내가이에 대한 이유가 없으니까요, 주문 실제로 관계가, 자바 스크립트도 YUI도는 이렇게 할 필요하지만 쉽게 소스 파일에서 물건을 찾을 수 있습니다. 인스턴스 속성 초기화에서 즉시 만들 수 있지만, 내가 명시적으로 그들을 선포하고이를 초기화 좋습니다. 각 속성은 API 문서에 코멘트를 앞에한다.

등록은 보통 개인이 될 것이며, 그 이름은 밑줄로 접두어. 객체의 공용 인터페이스가 구성 속성이 아닌 속성을 통해 노출되는 경우 그것은 가장 좋습니다. 속성은 매우 바보입니다, 구성 속성 validators, 유형 변환 (세터 경유)하고 보조 효과 (변경 이벤트를 통해) 생산과 당신이 모든 기능을 원하는 것을 찾을 때까지 그것은 종종 그리 길지 않을 수 있습니다.

구성 속성과 마찬가지로, 그들은 모든 부분이 동일한 개체를 가리키는 백업, 객체 또는 배열에 속성을 초기화하지 않으며 당신이 곤경에 실행됩니다. 그것은 개체를 잡고있는 속성을 설정하는 것이 좋습니다 null . 당신은 아직 자신의 가치를 모르는 경우에도로 설정, 속성 unset 떠나 지마 null 대신. 나중에, 디버깅하면 속성 설정 undefined 보통 오타, 오류를 가리 킵니다.

기본 인스턴스 방법

당신은 우리의 하위 클래스에 대한 모든 생성자를 선언하지 않은 것으로 파악했을 수도 있습니다. Base 라는 메서드 호출 후 모듈의 초기화를하지와 initializer , 그것이 존재한다면, 그건 당신이 모든 목적을 위해, 그래서 인스턴스 때받은 동일한 인자로 것을 고려할 수 있습니다 initializer 귀하의 생성자입니다. 에서 파생된 모든 클래스 Base 일반적으로 생성되는 하나의 인수, 구성 속성을 포함하는 개체입니다. 걸릴 Base (또는 Widget 그것의 클래스이기 때문에, Base )이 인수를 읽고 호출하기 전에 구성 속성을 설정 initializer . 내용은 Widget 이있을 경우 HTML_PARSER 속성은, 그것은 또한 처리한 것이며 마크업에서 읽을 속성 값은 잘으로 설정됩니다.

initializer 방법은 여러 가지 작업을하고 있습니다. 첫째, 객체 또는 배열로 초기화해야하는 속성을 설정해야합니다. 그렇다면이 클래스가 생성됩니다 모든 이벤트를 게시합니다. EventTarget 당신이 이벤트에 대한 기본 설정을 사용하여 처음 발행,하지만이 경우에, 당신이 어쨌든 그들을 선언 제안되지 않은 이벤트를 해고하실 수 있습니다. 이것은 함수 선언의 본문에 빠지는 건 좀 이상하게 보이는 경우에도 해당 이벤트에 대한 API 문서에 주석을 추가하는 좋은 장소이지만, 그렇게하는 것이 더 좋은 곳은 없습니다.

받은 인자 initializer 다음에서 처리하지만, 가끔은 초기화에 사용되는 몇 가지 추가 옵션이 필요하고 그들을 위해 실제 특성을 유지하기 위해 신경 안되었을 것이다. 예를 들어, Base 속성을 허용 on , after , bubbleTargetsplugins (참고 Base 가 이들에 대한 구성 속성이 없습니다하지만). 마찬가지로 WidgetParent 걸리는 children 초기화에 속성 그러나 그 이름에 대한 구성 속성이 없습니다. initializer 방법은 그들을 처리하는 하나이다. 여러분의 클래스 인스턴스에 하나의 인수를 복용 결국되지만 따라서,이 하나의 인수가 필요 할지도 모든 정보를 수행할 수 있습니다.

JavaScript는 destructors에 대한 개념이 없습니다. Base 여러분 선언할 수 있도록하여이에 대한 보상 destructor 당신의 객체가 보았다 자원을 해방하는 코드를 넣을 수 방법. 아직도 폐기가 이전 객체를 파괴하는 책임이 있습니다 있도록 개체를 포기했을 때 이것은 부분적인 솔루션입니다, 자바 스크립트 인터프리터가 자동으로 호출하지 않지만, 적어도 당신은 소멸자가 될 것입니다 알아요.

해당 클래스의 사용자는 부르지 않을 initializerdestructor 직접. Base 필요할 때 그들을 호출합니다. initializer 객체가 인스턴스되었을 때 호출되며, destructor 클래스의 사용자는이 호출되면 호출됩니다 destroy 방법을.

종종 메모리 누수를 생산 것들 중 하나는 남겨진 이벤트 리스너입니다. Widget 경계 상자 요소에 포함된 사용자 인터페이스 요소에 연결된 모든 리스너를 분리하려고하지만, 그것은 어떤 다른 사람을 분리 수 없습니다. Base 전혀 이벤트 리스너를 분리 수 없습니다 . 이것이 내가 그걸 좀 도와 사용하는 코드입니다. 기타 개인, 인스턴스 속성이 함께 나는 선언 _eventHandles 속성 :

  _eventHandles : NULL, 

그런 다음에 initializer 방법, 나는 배열로 설정하십시오 :

  초기화 : 함수 (CFG) {
     this._eventHandles = [];
     / / ......
 } 

동일한에는 initializer (또한 관련 bindUI 그것이라면 Widget ) 나는 그때 일을하여 리스너를 첨부합니다 :

  this._eventHandles.push (this.after ( 'someAttributeChange', this._afterSomeAttributeChange,이)); 

다음으로 destructor 나는 :

  소멸자 : 함수 () {
     Y.each (this._eventHandles, 기능 (취급) {
         () handle.detach;
     });
 } 

그것에 여기 initializer 가 보조 효과를 (당신이 그것을 다를 수 있습니다 생성해야 속성에 대한 이벤트 리스너를 연결하는, bindUI 이 보조 효과가 UI를 다룰있는 경우). 내가 전에 말했듯이, 속성 세터 함수는 속성의 값을 정규화 처리한다. 그 설정 값을 저장 벗어나는 효과를 생산할 경우, 이들은 이벤트 리스너에 의해 처리되어야한다. 위 예제에서, 나는 방법 설정 _afterSomeAttributeChange 의 어떤 변화에 대해 들어 someAttribute 특성. 이벤트 리스너는 하나의 인수, 나는 보통 전화 이벤트 외관 받게됩니다 ev , 여러 속성 중 하나와 객체 newVal 설정되는 값을 포함합니다.

위젯 인스턴스 속성

있는 두 가지 중요한 특성 Widget 사용이 BOUNDING_TEMPLATECONTENT_TEMPLATE . 둘 다 처음으로 설정되어 있습니다 “<div></div>” 이 컨테이너 대부분의 위젯을 사용하는 다른 내에서 하나의 표준 구조를 생산하고 있습니다. 이것은 그러나, 모든 위젯에 적합하지 않을 수 있습니다 예를 들어, Button 위젯이 더에 의해 제공될 수 <span> (앵커 내에서 요소 <a> ) 요소 대신에 두 개의 중첩된 <div> S는. 사실, 당신이 가지고 신경 안 써도 contentBox 전혀 Widget 당신이 필요하지 않습니다. 당신이 원하는 어떤 마크업이 두 인스턴스 속성을 설정할 수 있습니다. 예를 들어, 위해 Button 클래스 내가 할 수도 있습니다 :

  BOUNDING_TEMPLATE : '<a>에서'
 CONTENT_TEMPLATE : NULL, 

CONTENT_TEMPLATE 위해 세트를 null 말할 것이다 Widget 당신이 원하지 않는 contentBox 전혀 있습니다. 이 경우 contentBox 로 구성 속성은 동일한 요소를 가리 킵니다 boundingBox 구성 속성 않습니다.

당신은 이러한 템플릿으로 위젯에 대한 전체 HTML을 넣어해서는 안, 두 가지 간단한 HTML 요소를 확인하고 관련 코드를 통해 별도의 마크업을 만들 renderUI (우리가 나중에 보자).

Widget 추가됩니다 id 등, 당신이 원하는 마크업에 속성과 그것이 사용하는 표준 클래스를 yui3-xxxx , yui3-xxxx-visible 또는 yui3-xxxx-disabled 여기서 xxxx 의 가치 NAME 소문자로 바뀌 속성입니다.

위젯 인스턴스 방법

Widget 여러 단계의 초기화를 나눕니다. 외에도 initializer 개체가 인스턴스 때 호출, 그리고 destructor 에서 불리는 destroy 에 의해 처리 두 방법 Base , Widget 추가 renderUI , bindUIsyncUI 때 순서대로 호출됩니다 빌딩 단계에 대한, Widgetrender 메소드입니다 했다.

renderUI 방법은 위젯의 기본 HTML 생산을 담당한다. 모두 boundingBoxcontentBox 이 시점에서 렌더링되었습니다. 진보적인 향상을 사용하는 경우 renderUI 첫번째 요소가 이미 페이지에 존재하는지를 확인한다. 우리가 사용한 적이있다면 HTML_PARSER 다음 해당 요소에 대한 참조를 가지고있는 구성 속성이 그때까지 설정이 아니라면되었을 것이다 재산을, 우리는 그들을 만들어야합니다.

이렇게하려면 가장 쉬운 방법은 (더 진보적인 향상을 가정하지 않음)를 사용하는 것입니다 Y.Node.create 같이 :

  renderUI : 함수 () {
     VAR cbx = this.get (CBX);
     cbx.append (Y.Node.create (Y.substitute (Y.Xxxx.TEMPLATE, CLASS_NAMES)));
 } 

이것이 내가 바로 설명 할게 많은 것들을, 가정합니다. 첫째,이 CBX 선언된 상수로서이 문서에서 첫 번째 코드 상자에 표시됩니다. 그렇다면 가정 Node 있는 로드된 Widget 은 안전하므로 사용하지만, 그것도 가정 Y.substitute 선택 사항입니다있는가 있습니다. 당신은 추가해야 할 'substitute'requires 하여 모듈의 목록입니다. 그렇다면 위젯에 대한 템플릿은 정적라는 변수에있을 것으로 예상 TEMPLATE (오른쪽 다른 정적 클래스 멤버를 따라 정의하기 위해 귀하에게 달려 ATTRS 등). 마지막으로 그것은 상수가 가정 CLASS_NAMES 어딘가에 선언했다.

난 보통 선언 CLASS_NAMES 같이 BBX 및 CBX (이 문서의 첫 번째 코드 상자 참조) 더불어 내 모듈 정의에 :

  VAR BBX = 'boundingBox'
     CBX = 'contentBox'
     NAME = '버튼'
     / / 다른 상수와 단축키 ....
     YCM =의 Y.ClassNameManager.getClassName,
     getClassName는 = 함수 () {
         VAR 인수는 = Y.Array (인자);
         args.unshift (이름);
         (이, 인수) YCM.apply를 반환하는) (toLowerCase.;
     }
     라벨은 = '레이블'
     다림질 = '누르면'
     ICON = '아이콘',
     CLASS_NAMES = {
         다림질 : getClassName (누르면)
         아이콘 getClassName (ICON)
         라벨 : getClassName (LABEL)
         noLabel : getClassName ( '아니오', 레이블)
     }; 

CLASS_NAMES 그때까지 만들어진 특성을 가진 개체를 포함하는 지속 될 것입니다 ClassNameManager (이 또한 함께 제공 온다 Widget ). 위의 코드에서, 내가 먼저 바로 가기 만드는 YCM 나중에 빠르게 액세스하기 위해, 그럼 함수 만들 getClassName , 모듈 정의 내에서만 액세스할 수있는 전용 기능을. 함수의 방법처럼 거의 작동하는 같은 이름Widget 하지만 내가 더 이상 정적 값을 정의하는 데 사용할 수있는 정적 함수입니다. 그래서 나는 나중에 할 정확히 무엇이며, 제가 만들 때 CLASS_NAMES 해당 속성으로 생성된 클래스 이름이있는 개체로합니다. 이것은 내가 쓸 수있는 TEMPLATE 과 같은 문자열을 :

  템플릿 : '<label class="{label}"> <input/>' 

그렇다면 지금 까진 매우 어리석은 것입니다. 나는 또한 다른 소스, 특히 구성 속성에서이 템플릿 값으로 병합하고 싶습니다. 이것이 내가 그것을 할 얻는 방법은 다음과 같습니다

  this.get (CBX). 추가 (Y.Node.create (Y.substitute (템플릿, CLASS_NAMES, Y.bind (기능 (키, 제안 ARG) {
     반품 (키 === '_'this.get (ARG) :? 권장);
 },이)))); 

그러 고는 세 번째 인수를 추가 Y.substitute , 기능. 보통 대한 자리 표시 Y.substitute 공간이있다면, 그것이 최대 키와 두 번째 선택적 인수되는 공간으로, 두 부분을 자리 표시를 나눌 것입니다 그러나, 곱슬 브래킷 사이로 묶인 문자로 만들어집니다. 세 번째 인수와 같은 현재와 같은 기능이다 때 유용하다. 함수는 세 가지 인수를 받게됩니다, 먼저 핵심이며, 두 번째는 여기 교체 개체에서 찾을 값입니다 CLASS_NAMES 있는 경우, 그리고 세 번째는 선택적 인수입니다. 따라서 위의 문장에서 이런 템플릿을 사용할 수 있습니다 :

  템플릿 : '<label class="{label} for="{_ id}"/> <input id="{_ id}" value="{_ value}" />' 

Y.substitute 찾을 {label} 과에서 검색 CLASS_NAMES . 그것을 찾아 얻을 것이다 'yui3-button-label' . 그런 다음 인자로 교체 함수를 호출합니다 'label' , 'yui3-button-label' 하고 undefined . 이후 key 같지 않음 '_' 그 두 번째 인수, 원래 클래스 이름에 값을 반환합니다. 그것에 도달하면 {_ id} 라는 속성에 대한 값이 없습니다 _CLASS_NAMES 그것이 인자로 교체 함수를 호출 않도록 '_' , undefined'id' . 통해 key 평등 '_' , 기능 가서의 값을 가져옵니다 'id' 속성을 지정해야합니다. 그것에 대해 다시 동일한 작업을 수행합니다 {_ value} 자리 표시 자.

상단에 선언된 모든 상수가 모듈 외부에서 어떤 코드에서 숨겨져 있지만 같은, 그들 중 일부가 표시되도록하는 것이 좋습니다 CLASS_NAMES . 정적 멤버 섹션에서 마지막 인자는 데, 그렇게 Y.Base.create , 당신은 가질 수 :

  CLASS_NAMES : CLASS_NAMES 

그런 다음 모든 클래스 이름을 가진 개체는 표시 될 Y.MyWidget.CLASS_NAMES .

난 당신이 위젯의​​ 콘텐츠를 만들 수있는 HTML 문자열로 가능 한한 많은 서식을 할 것이 좋습니다. 자바 스크립트에서 문자열 조작이 더 전에 전화 않도록 DOM에 액세스하는 것보다 훨씬 빠릅니다 Y.Node.create 그 문자열은 빨리 당신이 그것 해낼 꺼야.

어떤 위젯을 촉구 다음 인스턴스 메소드입니다 bindUI . 당신이 만든 모든 요소에 이벤트 리스너를 첨부 곳입니다 renderUI 의 가치 변화에 대해 예를 들어, 수신기를 <input> 상자의 TEMPLATE 위. 구성 속성에서 텍스트와 그것에 대한 가치는 항상 동기화에 보관한다. value 속성은 입력 상자에 코드를 이용하거나 사용자가 입력하여 중 변경될 수 있습니다. 그것이 외부 코드에서 온다면 그것이 텍스트 상자에서 유래하는 경우 텍스트 상자가, 상쾌해야, 그건 그렇지 않으면 당신은 무한 루프를 입력할 위험을 감수 해선 안됩니다 : 텍스트 상자에 변경 사항은 설정 value 입력한 다음 설정 속성 value 텍스트 상자에을하는 그때 변화와 세트 value 속성 등등. 이 사건을 처리하는 방법을 볼 수 있습니다. 우리는 합성에 리스너를 설정 valueChange 입력 상자에 이벤트. 우리가 추가해야 할려면 event-valuechange 에 모듈을 requires 이 모듈의 목록입니다.

  this._eventHandles.push (this._inputEl.after ( 'valueChange', this._afterInputChange,이)); 

우리는 객체에 저장된 텍스트 상자에 대한 참조가있는 가정 _inputEl . 리스너는이 일을 :

  _afterInputChange : 기능 (EV) {
     this.set (값, ev.target.get (값) {출처 ​​: UI});
 } 

여기서 우리가 상수를 가지고 가정 VALUEUI 로 선언 'value''ui' 각각. 우리는 단순히 속성 설정 value 입력 상자에서 읽은 값으로합니다. 그러나 세트 메서드에 세 번째 인수를 추가하는 : {source:UI} . set 방법은 누구의 속성 속성 변경 이벤트의 이벤트 외관에 혼합됩니다 세 번째 인수, 개체를 소요될 수 있습니다. 이것은 우리가 텍스트 상자에서 또는 외부 코드에서 설정되는 값 사이의 차이를 알 수있는 방법입니다. 에서는 bindUI 우리는이 청취자를 설정했을 :

  this._eventHandles.push (this.after ( 'valueChange', this._afterValueChange)); 

이것은 변화를위한 리스너입니다 value 해당 개체의 속성, 다른 하나는 가치의 변화였다 <input> 박스들은 결국 같은이라고, 그들은 둘 다 가치라는 것을 변화 듣고 하지만 같은 일이 없습니다. 보통, 속성 변경에 대한 청취자가로 설정된 initializer 하지만이 하나의 UI 요소에 영향을 미치는 때문에, 우리에 넣어 bindUI 우리가 텍스트 상자가 될 것이고 있도록. 리스너는 것이다 :

  _afterValueChange : 기능 (EV) {
     {(ev.source === UI) 경우
         반환;
     }
     this._inputEl.set (값, ev.newVal);
 } 

우리가 제일 먼저 확인하는 것입니다 source 이벤트를. 그것에서 유래하는 경우 UI 우린 그것을 무시합니다. Both the property name, source and its value, UI are arbitrary, those are the ones I used when setting the value attribute so those are the ones I check for in the listener, but any name/value would do just as well. Actually, Widget provides a constant for that, Y.Widget.UI_SRC , but it is kind of long so I would probably use a shortcut anyway.

Another tidbit: you can set attributes declared as read-only by using _set instead of set . The _set method is meant to be protected, to be used internally but, as we know, JavaScript knows nothing about security so _set is open to any but, at least, we try by declaring the attribute with readOnly:true and documenting it as such in the API docs.

Finally we declare syncUI . While the first two, renderUI and bindUI are going to be called once and only once, syncUI will be called at least once by Widget itself and you might call it several times afterwards. Its purpose is to refresh the UI to reflect the current state of the object. Since the state might change, the UI might need to be refreshed over time. However, I can't provide a simple recipe for handling this. For a simple UI, syncUI might refresh everything in the screen and be called every time anything changes. For more complex UIs refreshing the whole UI might take time and cause flickering so you might want to refresh only the bits and pieces you need. If so, you will have separate methods to refresh each of these parts and syncUI will call each of them just once. Moreover, as I've shown in the example for renderUI , I set the value of the textbox right there, though that should be done in syncUI .

In the more general case, you will have a function for each UI element that can be set separately. That function will be called once from syncUI , when initializing, and any number of times from the after attribute change event listener. For example, we could have:

 _valueUIRefresh: function (value) {
    this._inputEl.set(VALUE, value);
 } 

Which could be called from syncUI along other similar setters:

 syncUI: function () {
    this._valueUIRefresh(this.get(VALUE));
    // other such refreshers 
 } 

and by the after listener:

 _afterValueChange: function (ev) {
    if (ev.source === UI) {
        return;
     }
    this._valueUIRefresh(ev.newVal);
 } 

Communicating with others

Once you have the logic of one of your modules finished, you want it to interact with other modules on your page. If you've seen Nicholas Zakas video, you already know what tight and loose coupling is. Calling methods and setting attributes from one module to another means having those modules tightly coupled and it is the traditional way, so I won't talk about it since you know how to do it. The other way to do it is to fire custom events. Base already includes everything you need to do that.

First, in initializer , you publish the custom events you want everybody to find out about.

 initializer: function (cfg) {
    this.publish('eventName', { /*… options … */});
 } 

Normally, the name of the event will come from a constant, since you will use that same name every time you fire it and you don't want typos there.

Normally, when you have a reference to an object, such as:

 var myWidget = new Y.MyWidget({ /* .. attributes … */ }); 

you can listen to its events by doing:

 myWidget.after('eventName', this._eventNameListener, this); 

However, to do this, you need to have a reference to myWidget , which is not as tightly coupled as calling its methods directly but it is still quite tight: at least one module knows about the other or, perhaps, a supervisor module knows about both and sets the links in between them. Two options are important to get modules to communicate in between themselves, broadcast and emitFacade .

The first, broadcast , lets you set listeners for that event in other modules. When broadcast is left at 0, the default, you have to do as shown above. If you want the event to be listened to elsewhere, you will want broadcast set to 1, so events are broadcast within the same sandbox and sometimes 2, so they can go across sandboxes. In this context, a sandbox is what you get when you call:

 YUI().use( 'module1′, …, 'moduleN', function (Y) {
    // this is your sandbox
 }); 

You can have several such sandboxes in your page:

 YUI().use( 'module1′, …, 'moduleN', function (Y) {
    // this is your sandbox
 });
YUI().use( 'moduleX-1′, …, 'moduleX-N', function (Z) {
    // this is another sandbox
 }); 

If you set broadcast to 2, then an object in the second sandbox can listen to an event when fired in the first. You can see the details in the Event user guide . Lets just stick to the simple sandbox case.

To listen to an event fired from another module within the same sandbox you need to know the value of the NAME static property of that module and the name of the event. Remember, Y.Base.create takes, as its first argument, the value that it will use for its NAME property, thus, if you created a module in this way:

 Y.MyWidget = Y.Base.create(
    'xxxx',
    Y.Widget,
    // … and so on 

and then, in the initializer you published the 'help' event like this:

 initializer: function (config) {
    this.publish('help', {
        broadcast: 1,
        emitFacade: true
     });
 } 

To listen to that event in any other module within the same sandbox, you do:

 Y.after('xxxx:help', function (ev) { … }, this); 

Here, I am calling Y.after , not myWidget.after , I don't need to have a reference to the module firing the event. This is the same method used to listen to DOM events or other synthetic events such as 'valueChange' the only difference being the prefix, the part before the colon. Base already takes care to prefix all events with the value of the NAME property so you don't have to take care of that when publishing them. You can do so, you can even use something else as a prefix; if one such prefix is there, Base will respect it, but usually you just want the default, which Base provides.

You also want to set emitFacade because you will want to have a reference to the instance that fired the event, which the event facade provides in ev.target . But wait, if the listener module gets a reference to the firing module, don't they become tightly coupled once again? Not quite, as long as you don't preserve that reference in the listening module, the coupling will be volatile. Still, we can do better.

When firing the event we may add all the information the listener needs in the facade, like this:

 this.fire('help', {helpTopic: 'Event Broadcasting'}); 

Method fire takes the name of the event being fired (which Base will further prefix with the NAME of the class) and an object containing any number of properties which will be merged into the event facade. The listener then doesn't need to query the firing module for any information, all that might be needed is there. This is as loose as it gets. The listener simply knows that some module, and there may be many such modules, is asking for help on 'Event Broadcasting' and that is really all it needs to know. It doesn't even care which module asked for it. New modules may be added later and the help system will also work for them.

Events and Default Behaviors

The usual solution to changing the behavior of a class is to sub-class it so you can override one of its functions and do whatever it is you want to do instead. You can still do that. You can use Y.Base.create to define a module based on, say Y.Widget and then use Y.Base.create again using your new module as the base to change a particular behavior. For example, I might have:

 Y.MySimpleWidget = Y.Base.create(
    'simpleWidget',
    Y.Widget,
    [],
    {
        // instance members here, amongst them:
        renderUI: function () {
            this.get(CBX).append(Y.Node.create(' … whatever goes into the widget … ' ));
         }
     }
    {
        ATTRS: {
            // configuration attributes
         }
        // other static members
     }
 ); 

and then:

 Y.MyFancyWidget = Y.Base.create(
    'fancyWidget',
    Y.MySimpleWidget,
    [],
    {
        renderUI: function () {
            Y.MyFancyWidget.superclass.renderUI.apply(this, arguments);
            this.get(CBX).append(Y.Node.create(' … add some bells and whistles … ' ));
     }
    // Presumably the fancy version does not need any further static members so I skip the last argument
 ); 

MyFancyWidget improves over MySimpleWidget by adding some bells and whistles. This might be too much of a trouble in some cases, you might plan for a base class more flexible and easier to change. Custom events can help with that.

Imagine you have a class that has a sort function. The sort function takes a key and direction argument and is declared like this:

 sort: function (key, direction) {
     // sorting happens here
 } 

If you know that the behavior of that function might be changed in some circumstances, you might do the following. In the initializer method, you can have:

 initializer: function (config) {
    // amongst many other things:
    this.publish(SORT, {defaultFn: this._defSortFn});
 } 

Where SORT is a constant containing 'sort' . Then, you declare the sort function like this:

 sort: function(key, direction) {
    this.fire(SORT, {key:key, direction:direction});
 } 

The sort function simply transforms the standard function call into a fired event containing the same arguments. Though this is meant to provide alternatives, you still want the class to sort somehow, you do that through the default sort function:

 _defSortFn: function (ev) {
    var key = ev.key, direction = ev.direction;
    // same code as the original sort function
 } 

The class will do sort as before, the body of _defSortFn might be just the same as the original one, once you have read the key and direction arguments from the event facade, but any other piece of code can set a listener for that same sort event and change it, for example:

 myObjectThatSorts.on('sort', function (ev) {
    var key = ev.key, direction = ev.direction;
    ev.preventDefault();
    // now do your own sort
 }); 

By calling preventDefault I tell myObjectThatSorts not to call _defSortFn . I could do this conditionally and decide, based on whatever I want, whether I may leave the original sort go ahead or unconditionally stop it, as I did here. I might not even care to stop it ever, I might listen to the after event and simply flip an arrow somewhere in the UI to signal which way the sort went.

I may also alter the event facade. There is only one copy of the event facade that gets built when the event is fired and it is propagated through all before (on) listeners, to the default function and then to the after listeners until finally it is dropped. You can change the values of its properties at any point. Of course, it hardly matters any changes you might do after the default function is called but any changes done in the before (on) listeners will reach the default function, for example:

 myObjectThatSorts.on('sort', function (ev) {
    ev.direction = (ev.direction==='desc'?'asc':'desc');
 }); 

This would get the sort done upside down.

YUI_config

The easiest way to get your module on your page is to include it in its own <script> tag or in a script tag pointing to a combo URL (via creating a file on the server that is a manual concatenation of files or a combo service is the server supports one). Integrating custom modules into the Loader is a more advanced option, though it might improve performance. The important point in this case is to make sure the YUI.add() includes the requires: [...] in the last parameter, so use() will apply the module and its dependencies in the proper order.

For small applications, you will probably have everything loaded from the start as outlined above. However, for larger applications, you might not want everything loaded from the start since it can take too long. You can call use() more than once to request extra functionality as needed. However, having the Loader find out about each module's dependencies when it loads each is time consuming since it might take several sequential requests until it finally gets everything it needs. Instead, you can forewarn the Loader of your modules and their dependencies so, when the time comes, it knows how to deal with them and can load them all in parallel.

To do so, you need to add the module description and requirements to the tables that the YUI Loader uses to fetch modules. The easiest way is to build a yui_config.js file (or whatever you want to call it) that contains all those definitions. That file will look like this:

 YUI_config = {
    filter:'raw',
    //combine:false,
    gallery: 'gallery-2011.02.18-23-10′,
    groups: {
        js: {
            base: 'build/',
            modules: {
                'myWidget': {
                    path: 'myWidget/myWidget.js',
                    requires: ['widget', 'widget-parent', 'widget-child', 'widget-stdmod', 'transition'],
                    skinnable: true
                 }
                // other modules here
             }
         }
        // other groups here
     }
}; 

You include this file in a regular <script> tag in your HTML file before you issue the first YUI().use() statement. They replace those options you would otherwise place as the first argument to YUI().use() , as if you did YUI(YUI_config).use() , but YUI does it for you. You can use any of the options listed here .

The filter option can be set to 'min' for production code (the default so you would usually comment out), 'debug' for the fully expanded with log statements (which might overwhelm your console) and 'raw' for fully expanded without log statements, the last two used only in development. Likewise with the combine option, only used when you have really tough bugs and you want to find out what is going on and get lost in those huge combos. Then you put your gallery option, if you use any gallery modules, to freeze your gallery modules to a version you know it works.

The groups option is where you start describing your own modules. The first name, in this case js , can be anything, whatever you want to call your group of files. You could create one such group for each family of files in a common location. The first declaration in each group is the base location of the group of files relative to the home page or an absolute path. That is, basically, the criteria for grouping files, however, there are several more options, listed here .

Finally, in the modules section you start listing your modules. The key for each entry is the module name, the very same name that you have used as the first argument in the YUI.add in your file and the same that you will use in the module list when you issue the YUI().use() call in your application. Then you specify the location of the module file, relative to the previous base or the fullpath if located elsewhere, and the rest of the options that where at the very end of the YUI.add declaration and are listed here . The requires list can list YUI modules, gallery modules or modules of your own either within the same group or from other groups in your config file. Skins will be loaded automatically by setting skinnable:true if you locate them as I recommended at the beginning of this article.

To simplify things for myself, I created a Windows script file that builds the YUI_config options for me. It basically scans the folder with the module files and reads each of them and extracts the information from each YUI.add call by defining a fake YUI.add function that extracts the arguments for me. It makes plenty of quite simplistic assumptions but it works for me as it is, you use it at your own risk.

결론

YUI3 is very flexible and you can build your modules in many ways. This is no more than one way to do that; I don't always do it this way, sometimes, not often, I don't need all of what Base provides so Y.Base.create is of no use, but this works most of the time.

Share and extend: Bookmark with del.icio.us | digg it! | reddit!

18 Comments

  1. Thanks a lot for this article. It is difficult to find informations about skins in the official doc. It is now much clearer for me.

    Comment by plv — April 1, 2011 #

  2. Awesome article!

    Thank you, Satyam!

    Comment by John — April 1, 2011 #

  3. Wonderful article!

    Comment by Daniel Stockman — April 1, 2011 #

  4. “This is a JavaScript issue, not a YUI one.”

    That is NOT issue, but pretty normal behavior. :)

    Comment by John — April 3, 2011 #

  5. 기사에 대한 감사합니다! Cleared a lot of doubts that I had.

    I have one question though :) I don't quite understand the use of syncUI method. You have stated its use as “Its purpose is to refresh the UI to reflect the current state of the object”. Aren't you doing that in bindUI() method when you register afterValueChange events for widget's attributes to refresh UI?

    Comment by Vignesh — April 5, 2011 #

  6. i am just start to learn YUI3, i think this article will help me to understand it deeply.

    Comment by lily — April 5, 2011 #

  7. [...] mobile support, a “Simple” theme, a new kitchen-sink-like Widget Browser, and more A Recipe for a YUI 3 Application – Satyen Desai of the YUI team goes into detail about how to organize a YUI application WebGL [...]

    Pingback by JavaScript Magazine Blog for JSMag » Blog Archive » News roundup: iOS viewport fixes, Mobile Boilerplate, CommunityJS, Ender.js — April 8, 2011 #

  8. I've put it into Chinese.
    http://ued.taobao.com/blog/2011/04/14/a-recipe-for-a-yui-3-application/

    Comment by jayli — April 14, 2011 #

  9. @Vignesh:
    Regarding the syncUI method, the listeners you add in bindUI dont get fired during sync, because the object state at the time you bind the listeners has already been set, so there's no change in the state.

    I'll use pseudo code to help explain:
    var obj = new Y.Widget({attr1: 55});
    //attr1 state is now 55
    obj.render()
    So during render, since nothing changes, no change event is fired, hence why you may need to call the syncUI method.

    Ideally, you should have a _uiSetAttr1 method that is just called by the after change event, and can be called during sync that simply handles updating the ui in response to that attr changing.

    There are also properties that help make this glue wiring easier. Off the top of my head I don't remember if they're private still, but if you set them (I think it's either BIND_UI or BIND_UI_ATTRS and same for SYNC) all you have to do is set a method called _uiSetAttrname and list the ATTRS in that object, and it will automatically do all of that wiring for you.

    I hope that helps :)

    Comment by Nate Cavanaugh — April 16, 2011 #

  10. [...] Recipe for a YUI Application Building Reusable Widgets with YUI3 Alloy UI [...]

    Pingback by Build A Chrome App with YUI – (1) « Triptych — April 17, 2011 #

  11. Nate

    You are referring to property _UI_ATTRS which is an object with two properties, BIND and SYNC . As its initial underscore signals and the documentation states, they are private and unless you really know what you are doing, it is better keep your hands off them. (I mean 'you' in generic terms)

    Each of these has an array of attribute names which are already initialized by Widget so any changes need to be done carefully not to destroy their initial values.

    For those listed in the _UI_ATTRS.BIND array, an “after change” listener will be attached before our bindUI method which will call a method named like _uiSetXxxx , where xxxx is the name of the attribute.

    Likewise, configuration attributes named in _UI_ATTRS.SYNC will have this same _uiSetXxxx called right before our own syncUI method. Each of these _uiSetXxxx methods will receive the value of the attribute, either the initial value or the changed value.

    Indeed, this is a clever mechanism and it would be great if it was made public, or an alternative public equivalent were to be provided and thus supported in the long term. A formal statement by the YUI team regarding the long-term support for this mechanism would be welcome.

    Nevertheless, I should have mentioned it or suggested a similar custom approach. So, since the Pandora's box is open, let me explain it.

    Say you have an attribute “myAttr” which has an effect on the UI. You need to provide a method called _uiSetMyAttr (note the M in myAttr turned uppercase) which receives the new value to set and a second argument which might either be undefined (not there) or set to Widget.UI_SRC and affects the UI when the second argument is not set. You would define it in the instance member section like this:

    _uiSetMyAttr: function (value, src) {
    if (src === Y.Widget.UI_SRC) { return; }
    // set the UI element
    }

    To get the UI set initially, right before your own syncUI is called, push the name of the attribute into _UI_ATTRS.SYNC , usually in the initializer , like this:

    this._UI_ATTRS.SYNC.push("myAttr");

    To have it further called after any change in the attribute, push it also into _UI_ATTRS.BIND , like this:

    this._UI_ATTRS.BIND.push("myAttr");

    Thanks for the tip.

    Comment by Satyam — April 17, 2011 #

  12. Here's the enhancement request:

    http://yuilibrary.com/projects/yui3/ticket/2529439

    Comment by Satyen Desai — April 18, 2011 #

  13. Heya Satyam, thanks for the detailed writeup. We actually support this as a public attribute in our Alloy Component gallery module since it's so useful, but we allow it to be defined on the class level and handle automatically copying of the arrays so that if you pass in a custom set it is merged with a copy of the defaults instead of having to create a new array and concat it (or doing it in the initializer).

    The only downside I've found to the API (and it's a small one at that) are the times when I want other data from the event passed in (maybe the second or third arg would be good to have the event object being passed in).

    Btw, great article, thanks for consolidating this all here :)

    Comment by Nate Cavanaugh — April 21, 2011 #

  14. [...] Recipe for a YUI 3 Application 原文地址:http://www.yuiblog.com/blog/2011/04/01/a-recipe-for-a-yui-3-application/ 译文:使用YUI [...]

    Pingback by [译]使用YUI 3开发Web应用的诀窍 « Uedmagazine — April 22, 2011 #

  15. A correction to my previous comment :

    The extra attributes to monitor should be added to the _UI_ATTRS arrays via method concat() not push():

    this._UI_ATTRS.SYNC.concat("myAttr");
    this._UI_ATTRS.BIND.concat("myAttr");

    Using push() will add the new attribute to an array which is shared by Widget and all its subclasses while concat() will create a new copy for this particular subclass

    BTW: concat() allows several values to be concatenated at once:

    this._UI_ATTRS.BIND.concat("myAttr1","myAttr2","myAttr3");

    Comment by Satyam — May 14, 2011 #

  16. Sorry I missed once again in my correction . The way to add an attribute to be handled by Widget is this:

    this._UI_ATTRS = {
    BIND: this._UI_ATTRS.BIND.concat("myAttr"),
    SYNC: this._UI_ATTRS.SYNC.concat("myAttr")
    };

    Method concat() does not modify the array it applies to so the code in the previous comment does nothing. Sorry about the confusion.

    Comment by Satyam — May 14, 2011 #

  17. Hi Satyam. Great article! I'm in doubt with something and hope you could clarify this issue for me. You wrote: “Base cannot detach any event listeners at all”. But Base's destroy() method calls detachAll(), inherited from EventTarget, which according to the documentation, it should remove all listeners. 저는 뭐가 잘못 됐나요?

    Comment by alejandroci — May 25, 2011 #

  18. alejandroci ,
    You are right, the wording of that phrase is not right, it should have been 'Base cannot remove all events'. Indeed, it does remove some via detachAll() , which it inherits from EventTarget which comes along Attribute. Event Target is capable of removing all events created by itself via this.on and this.after .
    It cannot detach those subscribed to via Y.on , Y.after on events broadcast by other objects (subscribed via the " publisher : event " notation) or via a reference to some other object ( myPublisher.on() ).
    Widget can only detach events picked by delegation to its bounding box.
    So, YUI tries to keep it safe as much as possible with what it knows but it can't do it all, not with any reasonable amount of code. If you forget, YUI will cope as best as it can, but it is better if you do it yourself.

    Comment by Satyam — May 26, 2011 #

죄송합니다, 코멘트 양식은이 시간에 문을 닫습니다.

Hosted by Yahoo!

저작권 © 2006년부터 2012년까지 야후 주식 회사 판권 소유. 개인 정보 보호 정책 - 서비스 약관

에서 제공하는 워드 프레스 에서 야후! 웹 호스팅 .