Direktivy komponent AngularJS

Trochu pozadí... Naše aplikace začala dávno, když byl AngularJS v plenkách a jeho SPA funkčnost a směrování zůstaly hodně na přání. Začali jsme tedy používat AngularJS jen jako závazný rámec na MVC Views. Nyní se nám podařilo vytvořit hodně, než se angularjs stal ovladatelným jako SPA framework, ale do té doby by to znamenalo hodně práce, aby to bylo SPA, takže jsme to prostě přeskočili a Angular 2 (TS) byl hned za rohem .

O několik let později...

Čas plynul a vedení vidělo málo využití v aktualizaci rámců a udržování aktuálního stavu, prosazovalo nové funkce a funkce. Náš technický dluh se zvýšil. Nyní se nám konečně, po několika letech, podařilo přesvědčit vedení, že je potřeba určité úsilí, než se dostaneme do bodu, kdy nemůžeme pokračovat kvůli zastaralým rámcům a žádné podpoře v platformě.

A teď...

Součástí této migrace na Angular 8 a SPA je přechodný krok k vytvoření direktiv komponent AngularJS pro vše což znamená, že všechny ovladače, všechny směrnice jsou převedeny. Služby jsou služby, k jejich implementaci se používá pouze tovární metoda. Ten poslední byl naštěstí snadný převod.

Naše ovladače Angular 1 byly OBROVSKÉ. Jeden ovladač by mohl poskytovat data a žonglovat mezi zobrazením malého řídicího panelu, zobrazením seznamu a zobrazením podrobností... vše v jednom. A jeden velký (CS)HTML soubor.

Nyní, když přistupujeme ke směrnicím o součástech, jdeme opačně... co nejmenší a co nejvíce opakovaně použitelných součástí. Jednou z těchto komponent, které jsem právě vyrobil, je přepínací komponenta. V naší aplikaci máme něco, co se nazývá featuretoggles, což znamená, že majitelé produktů si mohou vybrat, které části vyvinuté aplikace chtějí aktivovat ve své vlastní instanci.

Nyní jsme používali holicí strojek k výběru částí, které se mají zobrazit, na základě těchto přepínačů funkcí, ale nyní jsou všechny holicí strojek přesunuty a přepracovány do koncových bodů API, které může klientská strana používat. Máme službu angularjs, která poskytuje data a jednoduché funkce pro vyhledávání a další pomocné funkce.

Až donedávna jsme však službu používali téměř ve všech komponentách, které máme, a znovu využívali funkce služby k nalezení, kontrole a výběru cest týkajících se toho, zda aktivovat podřízenou komponentu či nikoli na základě použitých přepínačů.

Nyní jsem to abstrahoval do jeho vlastní komponenty pomocí něčeho, co se nazývá transkluze v úhlovém zobrazení.

Takže, co je transkluze? Je to způsob, jak zajistit, aby vaše komponenta akceptovala obsah uvnitř svých značek a vyhradila umístění pro tento obsah ve vaší šabloně.

Příklad;

<toggle>
  <h1>Internal content in toggle-component</h1>
</toggle>
angular.module('myApp.shared').component("toggle", {
    transclude: true,
    template: `
          <div ng-if="$ctrl.error !== \'\'">{{$ctrl.error}}</div>
          <ng-transclude ng-if="$ctrl.sumToggle"></ng-transclude>
    `,
    ...
});

Pojďme si to tedy rozebrat pro případ, že jste se s direktivou komponent ještě nesetkali.

angular.module(název řetězce, závislosti řetězce[])

angular.module('myApp.shared')

To se zahákne do angular, což mu říká, že se chystáme zaregistrovat komponentu, která patří do modulu "myApp.shared" a také, že myApp.shared je definována jinde se svými základními závislostmi, protože ji zde neposkytujeme. Pokud bychom byli, byl by to sekundární parametr funkce obsahující pole řetězců reprezentujících další moduly, na kterých by byl tento modul závislý.

Naší konvencí je zaregistrovat je do bootstrapping skriptu angularApp.js.

komponenta (Název řetězce, Možnosti volby)

component("toggle", { ... })

Tím se zaregistruje komponenta, která bude pojmenována „přepnout“ do dříve definovaného modulu. Nyní můžete přistupovat k této komponentě jako k prvku s uvedeným názvem. Pokud byste to pojmenovali "featureToggle" (všimněte si případu velblouda), měli byste k tomu přístup jako . Camelcase to dělá tak, že při použití prvku sám vyžaduje kebab-case k jeho vyvolání. Jedna z vtípků hranatého. Sekundární parametr funkce komponenty jsou konfigurace pro tuto komponentu. Například transclude, vazby, šablona (nebo templateUrl), controller a mnoho dalších...

Dotkneme se pouze těchto, ale je jich více. Podrobnosti o nich naleznete v dokumentaci (dokumentech), na které se zde odkazuje.

Možnost komponenty:transclude

Oficiální dokumentace:https://docs.angularjs.org/api/ng/directive/ngTransclude

Transclude jsem dosud používal dvěma způsoby. Jeden, kde je uvedeno pouze "true", což znamená, že má v šabloně pouze jednu transclude direktivu.

Použil jsem to také jako vícenásobnou transkluzi, kde máme více cílů. Příkladem by bylo definovat například transkluzi záhlaví a transkluzi zápatí. Pak byste mohli nasměrovat obsah konkrétně pro tyto oblasti v komponentě, jako je tato

<my-component>
   <my-component-header>
       <h1>This is content in the header transclusion</h1>
   </my-component-header>
   <!-- content that my template itself inserts and not shown in the parent DOM/component binding the my-component component-directive -->
   <my-component-footer>
       <h3>This is a footer content</h3>
   </my-component-footer>
</my-component>

Fuj, je tam spousta "komponent", ale je to jen vzorek. Abyste toho dosáhli, neposkytli byste pouze „true“, ale objekt představující cíle ng-transclude. Pro výše uvedený příklad by to vypadalo takto

{
  transclude: {
    "header": "myComponentHeader",
    "footer": "?myComponentFooter"
  },
  template: `
    <ng-transclude ng-transclude-slot="header">
       <!-- this is where h1 ends up -->
    </ng-transclude>
    <div>
       <!-- content that is not visible in the parent component injecting this one -->
    </div>
    <ng-transclude ng-transclude-slot="footer">
       <!-- this is where h3 ends up -->
    </ng-transclude>
  `
}

Ale... s naším aktuálním příkladem jsme to zachovali jednoduše a v šabloně jsme použili pouze jednu transclude direktivu, a proto také potřebovali pouze "transclude:true"

Možnost komponenty:vazby

Oficiální dokumentace:https://docs.angularjs.org/guide/component#component-based-application-architecture

Všimněte si, že tam je napsáno „vázání“, nikoli „vázání“ nebo „vázání“, je to „vázání“. Vím to, a přesto se mi daří dělat překlepy a pak se divím, proč to nefunguje a při pokusu o přístup k hodnotám, které komponentě předávám, není definováno.

Pokud tedy definujete vazby jako takové

{
  bindings: {
    debug: "<",
    names: "<"
  }
}

Budete mít jednosměrnou vazbu pro atributy na vaši komponentu s názvy atributů "debug" a "names". Používá se takto;

<toggle debug="true" names="orders,!invoices"></toggle>

Takže nyní jsou vlastnosti vazby "magicky" dostupné řadiči komponenty prostřednictvím "this.debug" a "this.names". Vzhledem k tomu, že javascript je javascript, vždy odkládám „toto“ na jeho vlastní proměnnou „já“, na kterou se mohu odkazovat, i když jsem hluboko uvnitř vnořených bloků a rozsahů, takže „toto“ najednou není okno nebo dokument nebo takové, proto je ve svém kódu označuji jako „self.debug“ a „self.names“.

Zpětná volání funkcí můžete předat jako vazby, takže můžete vytvořit atribut při aktualizaci, ke kterému může nadřazená komponenta svázat funkci, a vy můžete jen volat tuto funkci uvnitř vaší nové komponenty. Jednou z takových funkcí by bylo, kdy provádíte určitou masáž dat na základě uživatelského vstupu a provádíte zpětné volání, které zachází s výsledkem tak, jak to vyžaduje nadřazená komponenta. To se blíží tomu, jak byste mohli použít obousměrnou vazbu, ale potom se podřízená komponenta neustále aktualizuje, i když ji nepoužíváte, pokud ji nedespawnujete pomocí ng-if. Každý z nich má své případy použití, takže se ujistěte, že si věci nejprve promyslete nebo upravte podle potřeby. Snažte se nevytvářet pevné závislosti z podřízených komponent na nadřazené komponenty a naopak. Mějte na paměti zásady SOLID.

Možnosti součásti:šablona

Nyní je to pravděpodobně nejjednodušší. Je to pouze řetězec HTML, který použijete jako šablonu pro vaši komponentu. Pokud to uděláte inline, výrazně se zvýší výkon. Můžete přiřadit možnost templateUrl adresu URL, ale poté to provede požadavek XHR na získání tohoto souboru html, když se komponenta načte. Pokud tedy máte mnoho komponent, může to chvíli trvat v závislosti na možnostech a omezeních prohlížeče. Některé prohlížeče umožňují pouze 8 souběžných požadavků XHR. Jen pro informaci.

Ještě horší je, pokud máte šablonu, která pouze přidává div s direktivou ng-include ukazující na soubor HTML. To vám dá to nejhorší z obou světů.

Pokud potřebujete svázat proměnné regulátoru s pohledem v šabloně, můžete k nim ve výchozím nastavení přistupovat s předponou $ctrl jako tak "$ctrl.debug" podobně jako "this.debug" nebo pro mě "self.debug" ", když je v samotném ovladači. Můžete změnit přiřazení $ctrl k něčemu jinému, pokud chcete používat možnost controllerAs. Ale raději to zjednoduším a zachovám výchozí $ctrl.

Možnosti součásti:řadič

Volba ovladače bere funkci jako svou hodnotu. Poskytnuté parametry funkcí by byly služby a poskytovatelé, které má společnost Angular k dispozici pro injektování na základě názvů parametrů. Takže pokud uděláte funkci ($http), vloží $http-provider nativní pro Angular a budete jej moci použít ve svém kontroléru jako jakoukoli jinou proměnnou předávanou jako parametr funkci v javascriptu. Dependency Injection ftw.

controller: function (toggleService) {
  var self = this;
  ///...
  this.$onInit = function () {
    self.togglesToFind = parseStringOrArray(self.names);
    toggleService.initialized.then(function (toggles) {
      for (var i = 0; i < self.togglesToFind.length; i++) {
        var item = self.togglesToFind[i];
        /// _redacted_ 
        var foundToggle = toggleService.findToggle(toggles.data, item);
        /// _redacted_ 
        if (i === 0) self.sumToggle = foundToggle;
        else self.sumToggle = self.sumToggle && foundToggle;
      }
    });
  };
}

Nyní je toggleService injektován pomocí angular do tohoto ovladače. Používáme jej v rámci události životního cyklu $onInit. Tato funkce, pokud je definována v ovladači, je volána pomocí angular při inicializaci komponenty. Takže toto je to pravé místo, kde budete masírovat vstupy do výstupů a pohledů.

toggleService poskytuje příslib, že všechny zainteresované strany mohou „čekat“, než budou pokračovat v používání dat/funkcí, které služba poskytuje, aby bylo zajištěno, že služba singleton bude hotová a dostupná. Služba toggleService volá externí rozhraní API, aby získala data týkající se přepínačů funkcí, takže na to musíme počkat.

toggleService pak také poskytuje pomocnou funkci pro vyhledávání ve výsledných toggle-datech. Vyčistili jsme atribut/vazbu „names“ pomocí funkce parseStringOrArray definované v ovladači, což znamená, že vždy budeme mít pole řetězců, i když poskytnete pouze jeden přepínač, čárkami oddělený seznam přepínačů nebo skutečné pole řetězců představujících přepíná. Redigovaný kód pouze umožňoval přidanou logickou funkcionalitu ke vstupům, které pro tento blogový příspěvek nebyly relevantní.

Souhrn

Shrnuje tedy požadované přepínače funkcí a nyní, když se podíváme zpět na šablonu, zobrazí pouze obsah v přeložené části, POKUD splníme požadavky. To znamená, že nebudeme zobrazovat podřízené komponenty, pokud nebyly zapnuty. Nyní jsme snížili množství opakování kódu v každé z komponent opětovným použitím této přepínací komponenty. A díky tomu je html kód mnohem čitelnější.

Odkaz:https://docs.angularjs.org/guide/component