Primăria

În AngularJS, atunci când definim o componentă (sau o directivă), putem crea variabile de domeniu interior din atribute. API-ul pentru a face acest lucru este destul de alambicat:

bindings: {
attr1: '@',
attr2: '<',
attr3: '=',
attr4: '&'
}

Am obosit să plătesc prețul de a-mi înfășura creierul în jurul ei de fiecare dată când o folosesc, așa că în această postare vom diseca odată pentru totdeauna diferența dintre cele patru simboluri.

Specific, vom…

  • învățați cum să treceți șiruri de caractere (@)
  • învățați cum să treceți expresii dinamice (<)
  • învățați cum să prindeți ieșirea (&)
  • învățați cum să configurați douăway data bindings (=)
  • învățați cum să faceți toate cele de mai sus fără să folosiți niciunul dintre cele patru
  • învățați de ce < este mai bun decât celelalte trei

Citerea unui atribut ca text

Să începem cu @, cel mai simplu dintre cele patru, deoarece citește pur și simplu atributul ca text. Cu alte cuvinte, transmitem un șir de caractere componentei.

Să presupunem că avem această componentă:

app.component("readingstring", {
bindings: { text: '@' },
template: '<p>text: <strong>{{$ctrl.text}}</strong></p>'
});

Și o redăm astfel:

<readingstring text="hello"></readingstring>

Atunci iată ce obținem:

Utilizarea @ creează o variabilă interioară populată cu conținutul șirului de caractere al atributului numit. S-ar putea spune că servește ca o configurație inițială a componentei.

Evaluarea unui atribut ca expresie

Mai interesantă este nevoia de a evalua un atribut ca expresie și de a-l reevalua ori de câte ori se schimbă expresia. Intrare dinamică!

Vrem să putem face acest lucru…

<dynamicinput in="outervariable"></dynamicinput>

…și să trecem evaluarea lui outervariable în dynamicinput.

Înainte de AngularJS 1.5, singura sintaxă pe care o aveam pentru acest lucru era =:

app.component("dynamicinput",{
bindings: { in: '=' },
template: '<p>dynamic input: <strong>{{$ctrl.in}}</strong></p>'
});

Inconvenientul lui = era că crea o legătură de date bidirecțională, chiar dacă aveam nevoie doar de una unidirecțională. Acest lucru însemna, de asemenea, că expresia pe care o transmitem trebuie să fie o variabilă.

Dar cu AngularJS 1.5 avem <, ceea ce înseamnă o legătură de date unidirecțională. Acest lucru ne permite să folosim orice expresie ca intrare, cum ar fi un apel de funcție:

<dynamicinput in="calculateSomething()"></dynamicinput>

Implementarea componentei ar fi exact la fel, cu excepția schimbării = cu <.

Capturarea ieșirii

Este timpul să întoarcem lucrurile – cum capturăm ieșirea dintr-o componentă? Vedeți mica aplicație de mai jos – butoanele sunt redate într-un copil, iar atunci când se face clic pe ele dorim să actualizăm valoarea exterioară în mod corespunzător.

Aici intervine &. Acesta interpretează atributul ca pe o declarație și îl înfășoară într-o funcție. Componenta poate apoi să apeleze acea funcție în voie și să populeze variabilele din declarație. Ieșire către părinte!

Dacă html-ul nostru exterior arată astfel…

Outer value: {{count}}
<output out="count = count + amount"></output>

…atunci o implementare a output folosind & ar putea arăta astfel:

app.component("output",{
bindings: { out: '&' },
template: `
<button ng-click="$ctrl.out({amount: 1})">buy one</button>
<button ng-click="$ctrl.out({amount: 5})">buy many</button> `
});

Rețineți cum transmitem un obiect cu variabilele de populat. Această sintaxă alambicată înseamnă că, pentru a folosi o componentă cu o ieșire, trebuie să știm două lucruri:

  • numele atributului (atributelor) de folosit
  • numele variabilelor care vor fi create în mod magic.

Pentru că & este atât de alambicat, mulți folosesc = pentru a face ieșirea. Prin trecerea variabilei care trebuie manipulată…

Outer value: {{count}}
<output out="count"></output>

…apoi pur și simplu modificăm acea variabilă în interiorul componentei:

app.component("output",{
bindings: { out: '=' },
template: `<div>
<button ng-click="$ctrl.out = $ctrl.out + 1;">buy one</button>
<button ng-click="$ctrl.out = $ctrl.out + 5;">buy many</button>
</div>`
});

Acest lucru nu este foarte drăguț totuși:

  • facem din nou o legătură de date în două sensuri, chiar dacă avem nevoie doar de un singur sens
  • s-ar putea să nu dorim să salvăm ieșirea, ci pur și simplu să acționăm asupra ei

O soluție mai drăguță decât toate cele de mai sus este să folosim < pentru a crea ieșirea prin trecerea unui callback!

Creăm callback-ul în controlerul exterior…

$scope.callback = function(amount){
$scope.count += amount;
}

…și îl transmitem componentei:

<output out="callback"></output>

Componenta îl apelează acum pur și simplu în mod corespunzător:

app.component("output",{
bindings: { out: '<' },
template: `
<button ng-click="$ctrl.out(1)">buy one</button>
<button ng-click="$ctrl.out(5)">buy many</button>`
});

Mult similar cu &, dar fără magia întortocheată!

Ca o paranteză interesantă, acest model este exact modul în care funcționează ieșirea dintr-o componentă în React.

Legătură de date în două direcții

Acesta este locul în care = este de obicei lăsat să strălucească ca un poster boy AngularJS. Luați această aplicație:

Dacă o redăm astfel…

Outer: <input ng-model="value">
<twoway connection="value"></twoway>

…atunci putem implementa twoway folosind = astfel:

app.component("twowayeq",{
bindings: { connection: '=' },
template: `inner: <input ng-model="$ctrl.connection">`
});

Admitem că este ușor, dar rețineți – este destul de rar să avem nevoie de o legătură de date bidirecțională. Adesea, ceea ce doriți de fapt este o intrare și o ieșire.

Ceea ce ne aduce la modul în care putem implementa legătura bidirecțională folosind doar <! Dacă creăm din nou o funcție callback în controlerul exterior…

$scope.callback = function(newval){
$scope.value = newval;
}

…și trecem atât valoarea, cât și callback-ul…

<twoway value="value" callback="callback"></twoway>

…atunci componenta poate fi creată astfel:

app.component("twowayin",{
bindings: {
value: '<',
callback: '<'
},
template: `
<input ng-model="$ctrl.value" ng-change="$ctrl.callback($ctrl.value)">
`
});

Am realizat legarea datelor în două direcții, dar încă aderăm la un flux de date unidirecțional. Mai bine karma!

Lăsând simbolurile în urmă cu totul

De fapt, cele patru simboluri sunt doar niște scurtături. Putem face tot ceea ce fac ele fără ele.

Aplicația de trecere a șirurilor de caractere…

…pe care am redat-o astfel…

<readingstring text="hello"></readingstring>

<readingstring text="hello"></readingstring>

…ar putea fi implementată prin accesarea serviciului $element:

app.component("readingstring", {
controller: function($element){
this.text = $element.attr("text");
},
template: '<p>text: <strong>{{$ctrl.text}}</strong></p>'
});

Sau cu o directivă, folosind attrs care sunt trecute la link:

app.directive("readingstring", function(){
return {
restrict: 'E',
scope: {},
link: function(scope,elem,attrs){
scope.text = attrs.text;
},
template: '<p>text: <strong>{{text}}</strong></p>'
};
});

Aplicația de intrare dinamică….

…redată astfel…

<dynamicinput in="outervariable"></dynamicinput>

…ar putea fi implementată prin utilizarea unui apel .$watch în domeniul de aplicare părinte:

app.component("dynamicinput",{
controller: ($scope,$element) => {
let expression = $element.attr("in");
$scope.$parent.$watch(expression, newVal => $scope.in = newVal);
},
template: '<p>dynamic input: <strong>{{in}}</strong></p>'
});

Aplicația de ieșire…

…redată astfel…

<output out="count = count + amount"></output>

…ar putea fi implementată prin apelarea $scope.$apply în domeniul părinte:

app.component("output",{
controller: ($scope,$element,$timeout) => {
let statement = $element.attr("out");
$scope.increaseBy = by => {
$timeout(function(){
$scope.$parent.$apply(`amount = ${by}; ${statement}`);
});
}
},
template: `
<button ng-click="increaseBy(1)">buy one</button>
<button ng-click="increaseBy(5)">buy many</button>`
});

Nu este exact același lucru ca &, deoarece acum am poluat și domeniul părinte cu o variabilă amount, dar totuși, arată destul de bine conceptul.

În sfârșit, aplicația bidirecțională…

…redată ca și cu =

<twoway connection="value"></twoway>

…ar putea fi implementată prin setarea unui $watch atât în domeniul de aplicare părinte cât și în domeniul de aplicare copil:

app.component("twoway",{
controller: ($scope,$element,$timeout) => {
let variable = $element.attr("connection");
$scope.$parent.$watch(variable, newVal => $scope.inner = newVal;
$scope.$watch('inner', (newVal='') => $timeout( () => {
$scope.$parent.$apply(`${variable} = "${newVal}";`);
}));
},
template: `inner: <input ng-model="inner">`
});

Aceasta este o mică înșelăciune, deoarece acum presupunem că valoarea legată este întotdeauna un șir de caractere, dar, esența este încă acolo!

Încheiere

Sperăm că această călătorie a fost educativă și că @, <, = și & se simt acum mai puțin intimidante.

Și că ați observat cum < le bate în fund pe toate celelalte! Poate face totul, ceea ce poate face și =, dar < arată mult mai bine când o face.

Ambele sunt oarecum stângace pentru citirea șirurilor de caractere (< are nevoie de un șir într-un șir, iar = are nevoie de o variabilă proxy), dar asta e destul de ușor de făcut vanilie, așa că @ nu ar trebui să fie prea încrezut.

De asemenea, & poate merge să se rotească pe un băț.

Lasă un răspuns

Adresa ta de email nu va fi publicată.