2013-04-04 9 views
10

Moja dyrektywa konfiguracja jest następująca:

<div data-directive-a data-value="#33ff33" data-checked="true"> 
    <div data-directive-b></div> 
</div> 
  • Używam transkluzja celu zapewnienia directiveB zostanie wygenerowana.
  • directiveA ma pole wyboru, które ma na celu zmianę pewnej wartości za każdym razem, gdy jest zaznaczone.
  • ta wartość musi być dostępna w zakresie directiveA i directiveB.

Udało mi się to zrobić, ale tylko przez odniesienie $$prevSibling - czy istnieje lepszy sposób?

Oto kod: http://jsfiddle.net/janeklb/yugQf/ (w tym przykładzie, klikając pole wyboru jest po prostu znaczy "czysty" wartość)

-

Nieco bardziej głębokość: The 'treść' z directiveA (to, co jest w nim translatowane) nie zawsze jest directiveB. Inne dyrektywy, takie jak directiveB, również tam się znajdą. "Typy" directiveB będą zawsze używane w ciągu directiveA.

+2

polecam korzystania wymaga definicji w dyrektywie i przekazać do kontrolera nadrzędnego kontrolera dziecięcej –

+1

Dlaczego te wytyczne w ogóle rozważają oni nic nie robić? Jeśli są one tylko ilustracją problemu, czy te dyrektywy * zawsze * byłyby używane razem lub mogłyby być potencjalnie używane osobno?Czy chcesz, aby dyrektywa B była translatowana zawsze w odniesieniu do zakresu dyrektywyA zamiast nowego dziecka rodzica? Dlaczego dyrektywa A ma zasięg izolowania, ale dyrektywa B w ogóle nie deklaruje nowego zakresu? –

+1

@JoshDavidMiller Dodałem nieco więcej głębi do powyższego opisu. W odniesieniu do żadnego zadeklarowanego zakresu na 'B' i zakresu izolatu na' A' - nie mam tego skonfigurowanego w ten sposób z jakiegokolwiek powodu w szczególności. Wiem tylko, że muszę przekazać pewne dane z DOM do dyrektywy (dyrektyw), a najlepszym sposobem wydaje się być 'scope: {xxx: '@'}' – jlb

Odpowiedz

12

Aby uniknąć zbytniego łączenia elementów za dużo, unikałbym używania $$prevSibling. Najlepsze rozwiązanie, ponieważ oczekuje się, że twoje komponenty w wersji directiveB będą używane w komponentach directiveA, to użycie require.

.directive('directiveB', function() { 
    return { 
    require: '^directiveA', 
    scope: true, 
    link: function (scope, element, attrs, directiveA) { 
     scope.obj = directiveA.getObj(); 
    } 
    }; 
}) 

^require wskazuje, że gdzieś na element niniejszej dyrektywy lub na dowolnym elemencie nad nim w hierarchii DOM jest dyrektywa zwana directiveA i chcemy zadzwonić metod na swoim kontrolerze.

.directive('directiveA', function() { 
    return { 
    // ... 
    controller: function ($scope) { 
     // ... 
     this.getObj = function() { 
     return $scope.obj; 
     }; 
    } 
    }; 
}) 

Więc teraz w directiveB można użyć ng-model="obj.attr".

Istnieje wiele różnych wariantów, ale biorąc pod uwagę ogólne pytanie, uważam, że jest to najlepsze podejście. Oto zaktualizowany Fiddle: http://jsfiddle.net/yugQf/7/.

+0

W twoim skrzypce, izoluj właściwości zakresu 'attr' i' value' nie są używane i dlatego można je usunąć. –

+0

@MarkRajcok - Masz rację. Przybyli z Fiddle, które zamieścił, i wskazał w odpowiedzi na moje pytanie na jego stanowisko, że chciał je, ale nie są używane. Usunąłem je tak samo, aby uniknąć nieporozumień. Jeszcze raz dziękuję panu za wskazówkę! –

+0

Zegarek $ w dyrektywie B nie jest potrzebny. Ponieważ getObj() zwraca obiekt, wystarczy przypisać go do zakresu dyrektywy B, ponieważ będzie to odniesienie do obiektu w dyrektywie A. Zatem wszystko, co jest potrzebne w funkcji łącza, to 'scope.obj = directiveA.getObj();'. W tym [Fiddle] (http://jsfiddle.net/mrajcok/ffmC6/) użyłem 'obj2' w dyrektywie B, aby jaśniej zademonstrować koncepcję. Również w powyższym przykładowym kodzie dla dyrektywy B masz 'scope: true', ale twoje skrzypce go nie ma. Które wolisz/zamierzasz? (po prostu ciekawy, działa tak czy inaczej) –

6

@Josh wspomniano w swojej odpowiedzi, że

Najlepszym rozwiązaniem, ponieważ swoimi directiveB -jak komponentów oczekuje się zużyć w ciągu directiveA składników jest użycie require.

byłem bawiąc się tym i uważam, że kontroler na directiveA jest tylko rozwiązaniem (tak +1 Josh). Oto co zakresy wyglądać użyciu PO za skrzypce (. Odwrócić brązową strzałkę i masz $$ previousSibling zamiast $$ nextSibling) scopes picture

Inne niż $$previousSibling, zakres 004 ma ścieżki do izolacji zakres 003.Zauważ, że zasięg 004 jest zasięgiem transcluded, który tworzy directiveA, a ponieważ directiveB nie tworzy nowego zakresu, ten zakres jest również używany przez directiveB.

Ponieważ obiekt, który chcesz udostępnić z directiveB, jest tworzony w kontrolerze directiveA, nie możemy również używać atrybutów do udostępniania danych między dyrektywami.


Tworzenie modelu wewnątrz dyrektywy, a następnie udostępnianie tego modelu na zewnątrz jest raczej nietypowe. Zwykle będziesz chciał zdefiniować swoje modele poza swoimi dyrektywami, a nawet poza swoimi kontrolerami (listen for a few minutes to Misko). Usługi są często dobrym miejscem do przechowywania modeli/danych. Kontrolery powinny zazwyczaj odwoływać się do części modeli, które muszą być rzutowane na widok, z którym są powiązane.

Dla uproszczenia, zamierzam zdefiniować model na kontrolerze, wtedy dyrektywy będą miały dostęp do tego modelu w normalny sposób. Dla celów pedagogicznych, directiveA nadal będzie używać zakresu izolowania, a directiveB utworzy nowy zakres podrzędny, używając scope: new, jak w odpowiedzi @ Josha. Ale każdy typ (izoluj, nowe dziecko, bez nowego zakresu) i kombinacja będą działać, teraz, gdy mamy model zdefiniowany w zakresie nadrzędnym.

Ctrl:

$scope.model = {value: '#33ff33', checkedState = true}; 

HTML:

<div ng-controller="NoTouchPrevSibling"> 
    <div data-directive-a data-value="model.value" data-checked="model.checkedState"> 
     <div data-directive-b></div> 
    </div> 

Z innych powodów pedagogicznych, zdecydowałem się przejść dyrektywyświadectwo dwie cechy modela jako oddzielne atrybuty, ale cały model/obiekt mógł również zostać przekazany. Ponieważ dyrektywa B tworzy zakres podrzędny, nie musi przekazywać żadnych atrybutów, ponieważ ma dostęp do wszystkich właściwości zakresu nadrzędnego/kontrolera.

Dyrektywy:

app.directive('directiveA', function() { 
    return { 
     template: '<div>' 
      + 'inside parent directive: {{checkedState}}' 
      + '<input type="checkbox" ng-model="checkedState" />' 
      + '<div ng-transclude></div>' 
      + '</div>', 
     transclude: true, 
     replace: true, 
     scope: { 
       value: '=', 
       checkedState: '=checked' 
      }, 
    }; 
}); 
app.directive('directiveB', function() { 
    return { 
     template: '<div>' 
      + '<span>inside transcluded directive: {{model.checkedState}}</span>' 
      + '<input type="text" ng-model="model.value" />' 
      + '</div>', 
     replace: true, 
     scope: true 
    }; 
}); 

Celownik:

scopes

pamiętać, że zakres dziecko directiveB użytkownika (006) dziedziczy dołączany zakresie dyrektywyświadectwo'S (005).

Po kliknięciu na pole i zmieniając wartość w polu tekstowym:

scopes after interaction

Zauważ, że kątowe uchwyty aktualizację izolować właściwości zakresu. Normalny JavaScript prototypal inheritance zapewnia zakresowi dziecinnemu dyrektywy B dostęp do model w zasięgu kontrolera (003).

Fiddle

+0

+1 Bardzo ładna ilustracja! Zawsze podoba mi się diagramy dziedziczenia zawarte w odpowiedziach dotyczących zakresu. –

+0

+1 Dziękuję za diagramy Mark - Przyjąłem odpowiedź Josha, ponieważ był pierwszy, ale twój wkład jest bardzo doceniany. – jlb

+1

@jlb, Josh podał prawdziwą odpowiedź, więc zdecydowanie zasługuje na akceptację. Myślałem o twoim (doskonałym) pytaniu jeszcze dziś rano. Martwi mnie, że jesteśmy zmuszeni do używania rozwiązania 'require'. Dodam kilka dodatkowych przemyśleń (i diagramów) do mojej odpowiedzi (kiedy dostanę trochę czasu). Jednakże, aby dać ci szybki przegląd: stworzenie modelu wewnątrz dyrektywy, a następnie udostępnienie go na zewnątrz jest raczej nietypowe. Bardziej typowy sposób polega na tym, że kontroler odwołuje się do modelu, który można następnie udostępnić wszystkim dyrektywom potomnym/potomnym. –

Powiązane problemy