Апликације за једну страницу захтевају од фронт-енд програмера да постану бољи софтверски инжењери. ЦСС и ХТМЛ више нису највећа брига, заправо више не постоји само једна брига. Фронт-енд програмер треба да се бави КСХР-има, логиком апликације (модели, прикази, контролери), перформансама, анимацијама, стиловима, структуром, СЕО-ом и интеграцијом са спољним услугама. Резултат који произилази из свих ових комбинација је корисничко искуство (УКС) којем увек треба дати приоритет.
АнгуларЈС је врло моћан оквир. То је треће спремиште са звездицом на ГитХуб-у. Није тешко започети употребу, али циљеви којима је намењен постизању разумевања потражње. Програмери АнгуларЈС више не могу да игноришу потрошњу меморије, јер се она више неће ресетовати на навигацији. Ово је претходница веб развој . Прихватимо то!
Постоји неколико подешавања оптимизације препоручених за производњу. Један од њих је онемогућавање информација о отклањању грешака.
DebugInfoEnabled
је поставка која је подразумевано тачна и омогућава приступ опсегу кроз ДОМ чворове. Ако то желите испробати путем ЈаваСцрипт конзоле, одаберите елемент ДОМ и приступите његовом опсегу помоћу:
angular.element(document.body).scope()
Може бити корисно чак и када се не користи јКуери са својим ЦСС-ом, али не би требало да се користи ван конзоле. Разлог је тај када $compileProvider.debugInfoEnabled
је постављено на фалсе, позивајући .scope()
на ДОМ чвору ће се вратити undefined
.
То је једна од ретких препоручених опција за производњу.
Имајте на уму да опсегу и даље можете приступити преко конзоле, чак и када је у производњи. Позови angular.reloadWithDebugInfo()
са конзоле и апликација ће управо то учинити.
Вероватно сте то прочитали да јесте немајући тачку у вашем нг-моделу , радили сте погрешно. Када је реч о наследству, та изјава је често тачна. Опсези имају прототипски модел наслеђивања, типичан за ЈаваСцрипт, а угнежђени опсези су уобичајени за АнгуларЈС. Многе директиве креирају подређене опсеге као што су ngRepeat
, ngIf
и ngController
. При решавању модела, претраживање започиње на тренутном опсегу и пролази кроз сваки родитељски опсег, све до $rootScope
.
Али, приликом постављања нове вредности, шта ће се догодити зависи од тога какав модел (променљиву) желимо да променимо. Ако је модел примитиван, подређени опсег ће само створити нови модел. Али ако је промена својства објекта модела, претрага надређених опсега пронаћи ће референцирани објекат и променити његово стварно својство. Нови модел не би био постављен на тренутни опсег, па се не би десило маскирање:
function MainController($scope) { $scope.foo = 1; $scope.bar = {innerProperty: 2}; } angular.module('myApp', []) .controller('MainController', MainController);
OUTER SCOPE:
{{ foo }}
{{ bar.innerProperty }}
INNER SCOPE
{{ foo }}
{{ bar.innerProperty }}
Set primitive Mutate object
Кликом на дугме са ознаком „Сет примитиве“ поставићете фоо у унутрашњем опсегу на 2, али неће променити фоо у спољном опсегу.
Кликом на дугме са ознаком „Промени објекат“ промениће се својство траке из родитељског опсега. Будући да на унутрашњем опсегу нема променљиве, неће се догодити сенчење, а видљива вредност траке биће 3 у оба опсега.
Други начин да се то уради је искористити чињеницу да се родитељски опсези и основни опсег позивају из сваког опсега. Тхе $parent
и $root
објекти се могу користити за приступ надређеном опсегу и $rootScope
, директно из погледа. Можда је моћан начин, али нисам љубитељ тога због проблема са циљањем одређеног опсега уз ток. Постоји још један начин за подешавање и приступ својствима специфичним за опсег - коришћењем controllerAs
синтакса.
Алтернативни и најефикаснији начин додељивања модела за употребу објекта контролера уместо убризганог опсега $. Уместо убризгавања опсега, можемо дефинисати моделе попут овог:
function MainController($scope) { this.foo = 1; var that = this; var setBar = function () { // that.bar = {someProperty: 2}; this.bar = {someProperty: 2}; }; setBar.call(this); // there are other conventions: // var MC = this; // setBar.call(this); when using 'this' inside setBar() }
OUTER SCOPE:
{{ MC.foo }}
{{ MC.bar.someProperty }}
INNER SCOPE
{{ MC.foo }}
{{ MC.bar.someProperty }}
Change MC.foo Change MC.bar.someProperty
Ово је много мање збуњујуће. Нарочито када постоји много угнежђених опсега, као што то може бити случај са угнежђеним стањима.
Постоји још много тога о синтакси управљача.
Постоји неколико напомена о томе како је објекат контролера изложен. У основи је то објекат постављен на опсег контролера, баш као и нормалан модел.
Ако требате гледати својство објекта контролера, можете гледати функцију, али од вас се то не тражи. Ево примера:
function MainController($scope) { this.title = 'Some title'; $scope.$watch(angular.bind(this, function () { return this.title; }), function (newVal, oldVal) { // handle changes }); }
Једноставније је само урадити:
function MainController($scope) { this.title = 'Some title'; $scope.$watch('MC.title', function (newVal, oldVal) { // handle changes }); }
Што значи да такође низ опсег опсега можете да приступите МЦ-у са подређеног контролера:
function NestedController($scope) { if ($scope.MC && $scope.MC.title === 'Some title') { $scope.MC.title = 'New title'; } }
Међутим, да бисте то могли да урадите, морате да будете у складу са скраћеницом коју користите за цонтроАс. Постоје најмање три начина да се то постави. Већ сте видели прву:
…
Међутим, ако користите ui-router
, одређивање контролера на тај начин је склоно грешкама. За државе, контролори би требали бити наведени у конфигурацији стања:
angular.module('myApp', []) .config(function ($stateProvider) { $stateProvider .state('main', { url: '/', controller: 'MainController as MC', templateUrl: '/path/to/template.html' }) }). controller('MainController', function () { … });
Постоји још један начин напомена:
(…) .state('main', { url: '/', controller: 'MainController', controllerAs: 'MC', templateUrl: '/path/to/template.html' })
То можете учинити у директивама:
function AnotherController() { this.text = 'abc'; } function testForApeeScape() { return { controller: 'AnotherController as AC', template: '{{ AC.text }}
' }; } angular.module('myApp', []) .controller('AnotherController', AnotherController) .directive('testForApeeScape', testForApeeScape);
Важи и други начин бележења, мада мање сажет:
function testForApeeScape() { return { controller: 'AnotherController', controllerAs: 'AC', template: '{{ AC.text }}
' }; }
Де фацто решење рутирања за АнгуларЈС је до сада било ui-router
. Уклоњен из језгра пре неког времена, нгРоуте модул, био је превише основни за софистицираније рутирање.
Постоји нови NgRouter
на путу, али аутори и даље сматрају да је прерано за производњу. Када ово пишем, стабилни Ангулар је 1.3.15 и ui-router
стене.
Главни разлози:
Овде ћу покрити гнежђење стања како бих избегао грешке АнгуларЈС.
Схватите ово као сложен, али стандардан случај употребе. Постоји апликација која има приказ почетне странице и производа. Приказ производа има три одвојена одељка: увод, виџет и садржај. Желимо да виџет настави и да се не учита поново приликом пребацивања између стања. Али садржај треба да се поново учита.
Размотрите следећу структуру странице индекса ХТМЛ производа:
SOME PRODUCT SPECIFIC INTRO
Product title
Context-specific content
То је нешто што бисмо могли добити од ХТМЛ кодера и сада га морамо раздвојити у датотеке и стања. Генерално пристајем на конвенцију да постоји апстрактно ГЛАВНО стање, које по потреби чува глобалне податке. Користите то уместо $ роотСцопе. Тхе Главни држава ће такође задржати статични ХТМЛ који је потребан на свакој страници. Одржавам индек.хтмл чистим.
function config($stateProvider) { $stateProvider // MAIN ABSTRACT STATE, ALWAYS ON .state('main', { abstract: true, url: '/', controller: 'MainController as MC', templateUrl: '/routing-demo/main.html' }) // A SIMPLE HOMEPAGE .state('main.homepage', { url: '', controller: 'HomepageController as HC', templateUrl: '/routing-demo/homepage.html' }) // THE ABOVE IS ALL GOOD, HERE IS TROUBLE // A COMPLEX PRODUCT PAGE .state('main.product', { abstract: true, url: ':id', controller: 'ProductController as PC', templateUrl: '/routing-demo/product.html', }) // PRODUCT DEFAULT SUBSTATE .state('main.product.index', { url: '', views: { 'widget': { controller: 'WidgetController as PWC', templateUrl: '/routing-demo/widget.html' }, 'intro': { controller: 'IntroController as PIC', templateUrl: '/routing-demo/intro.html' }, 'content': { controller: 'ContentController as PCC', templateUrl: '/routing-demo/content.html' } } }) // PRODUCT DETAILS SUBSTATE .state('main.product.details', { url: '/details', views: { 'widget': { controller: 'WidgetController as PWC', templateUrl: '/routing-demo/widget.html' }, 'content': { controller: 'ContentController as PCC', templateUrl: '/routing-demo/content.html' } } }); } angular.module('articleApp', [ 'ui.router' ]) .config(config);
main.product.index
Затим да видимо страницу индекса производа:
main.product.details
Као што видите, страница индекса производа има три именована приказа. Један за увод, један за виџет и један за производ. Упознајемо спецификације! Па сада поставимо рутирање:
ui-router
То би био први приступ. Сада, шта се дешава приликом пребацивања између // A COMPLEX PRODUCT PAGE // WITH NO MORE TROUBLE .state('main.product', { abstract: true, url: ':id', views: { // TARGETING THE UNNAMED VIEW IN MAIN.HTML '@main': { controller: 'ProductController as PC', templateUrl: '/routing-demo/product.html' }, // TARGETING THE WIDGET VIEW IN PRODUCT.HTML // BY DEFINING A CHILD VIEW ALREADY HERE, WE ENSURE IT DOES NOT RELOAD ON CHILD STATE CHANGE ' [email protected] ': { controller: 'WidgetController as PWC', templateUrl: '/routing-demo/widget.html' } } }) // PRODUCT DEFAULT SUBSTATE .state('main.product.index', { url: '', views: { 'intro': { controller: 'IntroController as PIC', templateUrl: '/routing-demo/intro.html' }, 'content': { controller: 'ContentController as PCC', templateUrl: '/routing-demo/content.html' } } }) // PRODUCT DETAILS SUBSTATE .state('main.product.details', { url: '/details', views: { 'content': { controller: 'ContentController as PCC', templateUrl: '/routing-demo/content.html' } } });
и $urlRouterProvider.deferIntercept()
? Садржај и виџет се поново учитавају, али ми желимо само да поново учитамо садржај. Ово је било проблематично, а програмери су заправо креирали рутере који би подржавали управо ту функционалност. Једно од имена за ово било је лепљиви погледи . Срећом, 'use strict'; function yoda() { var privateMethod = function () { // this function is not exposed }; var publicMethod1 = function () { // this function is exposed, but it's internals are not exposed // some logic... }; var publicMethod2 = function (arg) { // THE BELOW CALL CANNOT BE SPIED ON WITH JASMINE publicMethod1('someArgument'); }; // IF THE LITERAL IS RETURNED THIS WAY, IT CAN'T BE REFERRED TO FROM INSIDE return { publicMethod1: function () { return publicMethod1(); }, publicMethod2: function (arg) { return publicMethod2(arg); } }; } angular.module('app', []) .factory('yoda', yoda);
подржава то из кутије са апсолутно именовано циљање приказа .
publicMethod1
Премештањем дефиниције стања у родитељски приказ, који је такође апстрактан, можемо сачувати подређени приказ од поновног учитавања приликом пребацивања УРЛ-ова што обично утиче на браћу и сестре тог детета. Наравно, виџет би могао бити једноставна директива. Али поента је у томе што би то могло бити и друго сложено угнежђено стање.
Постоји још један начин да се то уради употребом function yoda() { var privateMethod = function () { // this function is not exposed }; var publicMethod1 = function () { // this function is exposed, but it's internals are not exposed // some logic... }; var publicMethod2 = function (arg) { // the below call cannot be spied on publicMethod1('someArgument'); // BUT THIS ONE CAN! hostObject.publicMethod1('aBetterArgument'); }; var hostObject = { publicMethod1: function () { return publicMethod1(); }, publicMethod2: function (arg) { return publicMethod2(arg); } }; return hostObject; }
, али мислим да је коришћење конфигурације стања заправо боље. Ако сте заинтересовани за пресретање рута, написао сам мали водич о томе СтацкОверфлов .
Ово грешка је лакшег калибра и више је питање стила него избегавање порука о грешкама АнгуларЈС. Можда сте раније приметили да ретко прослеђујем анонимне функције декларацијама ангулар интерне. Обично прво дефинишем функцију, а затим је проследим.
Ово се тиче више од само функција. Овај приступ сам добио из читања водича за стил, посебно Аирбнб-ових и Тодд Мотто-ових. Верујем да постоји неколико предности и готово никаквих недостатака.
Пре свега, својим функцијама и објектима можете много лакше манипулисати и мутирати ако су додељени променљивој. Друго, код је чишћи и лако се може поделити у датотеке. То значи одрживост. Ако не желите да загађујете глобални простор имена, умотајте сваку датотеку у ИИФЕ. Трећи разлог је тестираност. Размотрите овај пример:
function scoringService($q) { var scoreItems = function (items, weights) { var deferred = $q.defer(); var worker = new Worker('/worker-demo/scoring.worker.js'); var orders = { items: items, weights: weights }; worker.postMessage(orders); worker.onmessage = function (e) { if (e.data && e.data.ready) { deferred.resolve(e.data.items); } }; return deferred.promise; }; var hostObject = { scoreItems: function (items, weights) { return scoreItems(items, weights); } }; return hostObject; } angular.module('app.worker') .factory('scoringService', scoringService);
Дакле, сада бисмо могли да се ругамо 'use strict'; function scoringFunction(items, weights) { var itemsArray = []; for (var i = 0; i b.sum) { return -1; } else if (a.sum
scoringService.scoreItems()
Овде се не ради само о стилу, јер је у ствари код вишекратно употребљив и идиоматичан. Програмер добија изражајнију снагу. Дијељење цијелог кода у самосталне блокове само олакшава.
У неким сценаријима може бити потребно обрадити велики низ сложених објеката пролазећи их кроз скуп филтера, декоратера и на крају алгоритма за сортирање. Један од случајева примене је када апликација треба да ради ван мреже или када су перформансе приказивања података кључне. А пошто је ЈаваСцрипт једнонитни, релативно је лако замрзнути прегледач.
Такође је лако то избећи код веб радника. Чини се да не постоји ниједна популарна библиотека која то обрађује посебно за АнгуларЈС. Можда би било најбоље, јер је примена лака.
Прво подесимо услугу:
.toString()
Сада радник:
function resolve(index, timeout) { return { data: function($q, $timeout) { var deferred = $q.defer(); $timeout(function () { deferred.resolve(console.log('Data resolve called ' + index)); }, timeout); return deferred.promise; } }; } function configResolves($stateProvide) { $stateProvider // MAIN ABSTRACT STATE, ALWAYS ON .state('main', { url: '/', controller: 'MainController as MC', templateUrl: '/routing-demo/main.html', resolve: resolve(1, 1597) }) // A COMPLEX PRODUCT PAGE .state('main.product', { url: ':id', controller: 'ProductController as PC', templateUrl: '/routing-demo/product.html', resolve: resolve(2, 2584) }) // PRODUCT DEFAULT SUBSTATE .state('main.product.index', { url: '', views: { 'intro': { controller: 'IntroController as PIC', templateUrl: '/routing-demo/intro.html' }, 'content': { controller: 'ContentController as PCC', templateUrl: '/routing-demo/content.html' } }, resolve: resolve(3, 987) }); }
Сада убризгајте услугу као и обично и третирајте Data resolve called 3 Data resolve called 1 Data resolve called 2 Main Controller executed Product Controller executed Intro Controller executed
као и сваки други начин услуге који враћа обећање. Тешка обрада ће се извршити на посебном навоју и неће нанети штету УКС-у.
На шта треба пазити:
.run()
на положеном имању и исправно је функционисало.Решења додају додатно време учитавању приказа. Верујем да су нам високе перформансе фронт-енд апликације примарни циљ. Не би требало да представља проблем да прикажете неке делове приказа док апликација чека податке из АПИ-ја.
Размотрите ово подешавање:
this.maxPrice = '100'; this.price = '55'; $scope.$watch('MC.price', function (newVal) { if (newVal || newVal === 0) { for (var i = 0; i <987; i++) { console.log('ALL YOUR BASE ARE BELONG TO US'); } } });
Излаз конзоле биће:
this.maxPrice = '100'; this.price = '55'; this.priceTemporary = '55'; $scope.$watch('MC.price', function (newVal) { if (!isNaN(newVal)) { for (var i = 0; i <987; i++) { console.log('ALL YOUR BASE ARE BELONG TO US'); } } }); var timeoutInstance; $scope.$watch('MC.priceTemporary', function (newVal) { if (!isNaN(newVal)) { if (timeoutInstance) { $timeout.cancel(timeoutInstance); } timeoutInstance = $timeout(function () { $scope.MC.price = newVal; }, 144); } });
Што у основи значи да:
То значи да пре него што корисник види било какав излаз, мора да сачека све зависности. Морамо имати те податке, наравно, у реду. Ако је апсолутно неопходно да га имате пре погледа, ставите га у $digest()
блокирати. У супротном, само позовите услугу из контролера и грациозно се побрините за напола оптерећено стање. Видети да је посао у току - а контролер је већ покренут, па је то заправо напредак - боље него да апликација застаје.
а) Узроковање превише сварљивих петљи, попут причвршћивања клизача на моделе
Ово је општи проблем који може резултирати грешкама АнгуларЈС, али о њему ћу расправљати на примеру клизача. Користио сам ову библиотеку клизача, клизач угаоног опсега, јер ми је била потребна проширена функционалност. Та директива има ову синтаксу у минималној верзији:
ng-click
Узмите у обзир следећи код у контролеру:
input
Дакле, то ради споро. Случајно решење било би постављање временског ограничења на улазу. Али то није увек згодно и понекад заиста не желимо да одложимо стварну промену модела у свим случајевима.
Тако ћемо додати привремени модел који ће морати да промени радни модел по истеку времена:
$timeout
а у контролеру:
$http
б) Не користи $ апплиАсинц
АнгуларЈС нема механизам за позивање $watch
. Извршава се само зато што користимо директиве (нпр. .$applyAsync()
, $digest()
), Услуге (applyAsync
, $http
) и методе (mymodule.config(function ($httpProvider) { $httpProvider.useApplyAsync(true); });
) које процењују наш код и после тога назовите сажетак.
Шта .click()
да ли одлаже решавање израза до следећег $apply()
циклус, који се покреће након 0 временског ограничења, што је заправо ~ 10мс.
Постоје два начина за употребу $scope.$root.$digest()
Сада. Аутоматски начин за $rootScope.$digest()
захтеве, а за остале ручни начин.
Да бисте све хттп захтеве који се враћају приближно у исто време решили у једном сажетку, урадите следеће:
$scope.$digest()
Ручни начин показује како то заправо функционише. Размотрите неку функцију која се у повратном позиву изводи на ванили ЈС преслушавачу догађаја или јКуери I AM DIRECTIVE$scope.$applyAsync()
или некој другој спољној библиотеци. Након извршавања и промене модела, ако га већ нисте умотали у $digest()
потребно је да позовете remove directive
(function MainController($rootScope, $scope) { this.removeDirective = function () { $rootScope.$emit('destroyDirective'); }; } function testForApeeScape($rootScope, $timeout) { return { link: function (scope, element, attributes) { var destroyListener = $rootScope.$on('destroyDirective', function () { scope.$destroy(); }); // adding a timeout for the DOM to get ready $timeout(function () { scope.toBeDetached = element.find('p'); }); scope.$on('$destroy', function () { destroyListener(); element.remove(); }); }, template: '
), или најмање scope.$on('$destroy', function () { // setting this model to null // will solve the problem. scope.toBeDetached = null; destroyListener(); element.remove(); });
. У супротном, нећете видети промене.
Ако то учините више пута у једном току, можда ће почети да ради споро. Размислите о позивању … the isolated scope is not available here, look: {{ isolatedModel }}
на изразима уместо тога. Поставит ће само један циклус сажетка за све њих.
ц) Тешка обрада слика
Ако имате лош учинак, разлог можете истражити помоћу хронологије из Цхроме Девелопер Тоолс. О овој алатки ћу написати више грешком # 17. Ако на графикону временске оријентације доминира зелена боја након снимања, проблеми са перформансама могу се односити на обраду слика. Ово није стриктно повезано са АнгуларЈС, али се може догодити поврх проблема са перформансама АнгуларЈС (који би на графикону углавном били жути). Као фронт-енд инжењери, морамо размишљати о комплетном завршном пројекту.
Одвојите тренутак да процените:
Ако сте на најмање три од горе наведених одговорили са „да“, размислите о његовом ублажавању. Можда можете да приказујете различите величине слика и да уопште не мењате величину. Можда бисте могли да додате хак за обраду ГПУ-а „трансформ: транслатеЗ (0)“. Или користите рекуестАниматионФраме за руковаоце.
Много пута вероватно чујете да се не препоручује употреба јКуери-а са АнгуларЈС-ом и да то треба избегавати. Неопходно је разумјети разлог који стоји иза ових изјава. Постоје барем три разлога, колико видим, али ниједан од њих није стварни блокатор.
Разлог 1: Када извршите јКуери код, морате позвати function MainController($interval) { this.foo = { bar: 1 }; this.baz = 1; var that = this; $interval(function () { that.foo.bar++; }, 144); $interval(function () { that.baz++; }, 144); this.quux = [1,2,3]; }
себе. У многим случајевима постоји Решење АнгуларЈС који је прилагођен за АнгуларЈС и може бити бољи за употребу у програму Ангулар од јКуери-а (нпр. нг-цлицк или систем догађаја).
Разлог 2: Метод размишљања о изградњи апликације. Ако сте додавали ЈаваСцрипт веб локацијама, које се поново учитавају током навигације, нисте морали превише да бринете о потрошњи меморије. Са апликацијама на једној страници морате да бринете. Ако не почистите, корисници који проведу више од неколико минута у вашој апликацији могу да имају растуће проблеме са учинком.
Разлог 3: Чишћење заправо није најлакше учинити и анализирати. Из скрипте (у прегледачу) не можете позвати сакупљач смећа. Можда ћете завршити са одвојеним ДОМ стаблима. Направио сам пример (јКуери се учитава у индек.хтмл):
function testDirective() { var postLink = function (scope, element, attrs) { scope.$watch(attrs.watchAttribute, function (newVal) { if (newVal) { // take a look in the console // we can't use the attribute directly console.log(attrs.watchAttribute); // the newVal is evaluated, and it can be used scope.modifiedFooBar = newVal.bar * 10; } }, true); attrs.$observe('observeAttribute', function (newVal) { scope.observed = newVal; }); }; return { link: postLink, templateUrl: '/attributes-demo/test-directive.html' }; }
Ово је једноставна директива која даје неки текст. Испод њега се налази дугме које ће само ручно уништити директиву.
Дакле, када се директива уклони, остаје референца на ДОМ стабло у сцопе.тоБеДетацхед. Ако приступите картици „профили“, а затим „направите брзи снимак“ у цхроме дев алаткама, у излазу ћете видети:
Можете живети са неколицином, али лоше је ако имате тону. Поготово ако га из неког разлога, као у примеру, похраните на опсег. Читав ДОМ ће се процењивати на сваком сажетку. Проблематично одвојено ДОМ стабло је оно са 4 чвора. Па како се то може решити?
attrs.watchAttribute
Уклоњено је одвојено ДОМ стабло са 4 уноса!
У овом примеру, директива користи исти опсег и на опсегу чува елемент ДОМ. Било ми је лакше да то тако покажем. Не постаје увек толико лоше, јер бисте га могли сачувати у променљивој. Међутим, и даље би заузимала меморију ако би неко затварање које се односи на ту променљиву или било које друго из истог опсега функције живело даље.
Кад год вам је потребна директива за коју знате да ће се користити на једном месту или за коју не очекујете да ће бити у сукобу са било којим окружењем у којој се користи, нема потребе да користите изоловани опсег. У последње време постоји тренд стварања компонената за вишекратну употребу, али да ли сте знали да основне кутне директиве уопште не користе изоловани опсег?
Два су главна разлога: не можете применити две изоловане директиве опсега на елемент и можда ћете наићи на проблеме са гнежђењем / наслеђивањем / обрадом догађаја. Нарочито у вези са трансклузијом - ефекти можда неће бити оно што очекујете.
Дакле, ово би пропало:
scope.$watch()
Па чак и ако користите само једну директиву, приметићете да ни изоловани модели опсега ни догађаји емитовани у исолатедСцопеДирецтиве неће бити доступни компанији АнотхерЦонтроллер. Кад је то тужно, можете да се савијете и употребите магију за укључивање да бисте то учинили - али за већину случајева употребе нема потребе да се изолујете.
MC.foo
Дакле, два питања сада:
Постоје два начина, у оба преносите вредности атрибутима. Размотрите овај МаинЦонтроллер:
$watch()
То контролише овај поглед:
MC.foo
Приметите да „атрибут сата“ није интерполиран. Све то функционише, захваљујући ЈС магији. Ево дефиниције директиве:
$parse
Приметите да $eval
се преноси у $destroy
без наводника! То значи да је оно што је заправо прослеђено $ ватцх-у био низ function cleanMeUp($interval, $rootScope, $timeout) { var postLink = function (scope, element, attrs) { var rootModelListener = $rootScope.$watch('someModel', function () { // do something }); var myInterval = $interval(function () { // do something in intervals }, 2584); var myTimeout = $timeout(function () { // defer some action here }, 1597); scope.domElement = element; $timeout(function () { // calling $destroy manually for testing purposes scope.$destroy(); }, 987); // here is where the cleanup happens scope.$on('$destroy', function () { // disable the listener rootModelListener(); // cancel the interval and timeout $interval.cancel(myInterval); $timeout.cancel(myTimeout); // nullify the DOM-bound model scope.domElement = null; }); element.on('$destroy', function () { // this is a jQuery event // clean up all vanilla JavaScript / jQuery artifacts here // respectful jQuery plugins have $destroy handlers, // that is the reason why this event is emitted... // follow the standards. }); };
! Међутим, то функционише, јер је било који низ прешао у $destroy
добија оцену према опсегу и $digest()
је доступан на обиму. То је такође најчешћи начин на који се атрибути гледају у основним директивама АнгуларЈС.
Погледајте код на гитхуб-у за предложак и погледајте {{ model }}
и
за још више страхота.
АнгуларЈС обавља неке послове у ваше име, али не све. Следеће треба ручно очистити:
vastArray
догађајАко то не урадите ручно, наићи ћете на неочекивано понашање и цурење меморије. Још горе - они неће бити одмах видљиви, али ће временом пузати. Марфијев закон.
Невероватно, АнгуларЈС пружа практичне начине да се носите са свим тим:
item.velocity
Обратите пажњу на јКуери {{ someModel }}
догађај. Зове се попут АнгуларЈС, али се њиме рукује одвојено. Гледаоци опсега $ неће реаговати на јКуери догађај.
Ово би сада требало бити сасвим једноставно. Овде треба схватити једну ствар: ng-if
. За свако везивање ng-repeat
, АнгуларЈС креира посматрач. У свакој фази дигестије, свако такво везивање се процењује и упоређује са претходном вредношћу. То се зове прљава провера и то $ дигест ради. Ако се вредност променила од последње провере, активира се повратни позив посматрача. Ако тај повратни позив проматрача модификује модел (променљива $ сцопе), покреће се нови циклус $ дигест (до највише 10) када се изузме изузетак.
Прегледачи немају проблема ни са хиљадама веза, осим ако изрази нису сложени. Уобичајени одговор за „колико посматрача може имати“ је 2000.
Па, како можемо ограничити број посматрача? Негледавањем модела опсега када не очекујемо да се промене. Од АнгуларЈС 1.3 даље је прилично лако, јер су једнократне везе сада у основи.
$watch()
После $Watchers
и $watch()
процене се једном, никада се више неће променити. И даље можете применити филтере на низ, они ће сасвим добро функционисати. Само што сам низ неће бити оцењен. У многим случајевима то је победа.
Ова грешка АнгуларЈС већ је делимично покривена грешкама 9.б и 13. Ово је темељније објашњење. АнгуларЈС ажурира ДОМ као резултат функција повратног позива гледаоцима. Свако везивање, то је директива $rootScope.$digest()
поставља посматраче, али посматрачи су постављени и за многе друге директиве попут $scope
и $watch()
. Само погледајте изворни код, врло је читљив. Посматрачи се могу подесити и ручно, а то сте вероватно и сами учинили најмање неколико пута.
$digest()
они су везани за опсеге. .$apply
могу узети низове који се вреднују према опсегу који .$applyAsync
био везан за. Такође могу проценити функције. И они такође узимају повратне позиве. Дакле, када .$evalAsync
, сви регистровани модели (односно $digest()
променљиве) се процењују и упоређују са њиховим претходним вредностима. Ако се вредности не подударају, повратни позив на $digest()
извршава се.
Важно је схватити да, иако је вредност модела промењена, повратни позив се не активира до следеће фазе сажетка. С разлогом се назива „фаза“ - може се састојати од неколико циклуса варења. Ако само посматрач промени модел опсега, извршава се други циклус сажетка.
Али describe('some module', function () { it('should call the name-it service…', function () { // leave this empty for now }); ... });
није анкетирано за . Позива се из основних директива, услуга, метода итд. Ако промените модел из прилагођене функције која не позива 'use strict'; var gulp = require('gulp'); var args = require('yargs').argv; var browserSync = require('browser-sync'); var karma = require('gulp-karma'); var protractor = require('gulp-protractor').protractor; var webdriverUpdate = require('gulp-protractor').webdriver_update; function test() { // Be sure to return the stream // NOTE: Using the fake './foobar' so as to run the files // listed in karma.conf.js INSTEAD of what was passed to // gulp.src ! return gulp.src('./foobar') .pipe(karma({ configFile: 'test/karma.conf.js', action: 'run' })) .on('error', function(err) { // Make sure failed tests cause gulp to exit non-zero // console.log(err); this.emit('end'); //instead of erroring the stream, end it }); } function tdd() { return gulp.src('./foobar') .pipe(karma({ configFile: 'test/karma.conf.js', action: 'start' })) .on('error', function(err) { // Make sure failed tests cause gulp to exit non-zero // console.log(err); // this.emit('end'); // not ending the stream here }); } function runProtractor () { var argument = args.suite || 'all'; // NOTE: Using the fake './foobar' so as to run the files // listed in protractor.conf.js, instead of what was passed to // gulp.src return gulp.src('./foobar') .pipe(protractor({ configFile: 'test/protractor.conf.js', args: ['--suite', argument] })) .on('error', function (err) { // Make sure failed tests cause gulp to exit non-zero throw err; }) .on('end', function () { // Close browser sync server browserSync.exit(); }); } gulp.task('tdd', tdd); gulp.task('test', test); gulp.task('test-e2e', ['webdriver-update'], runProtractor); gulp.task('webdriver-update', webdriverUpdate);
, console.log()
, debugInfo
или било шта друго што евентуално позива $(document.body).scope().$root
, везови неће бити ажурирани.
Иначе, изворни код за $(
18 најчешћих грешака које програмери АнгуларЈС праве
Апликације за једну страницу захтевају од фронт-енд програмера да постану бољи софтверски инжењери. ЦСС и ХТМЛ више нису највећа брига, заправо више не постоји само једна брига. Фронт-енд програмер треба да се бави КСХР-има, логиком апликације (модели, прикази, контролери), перформансама, анимацијама, стиловима, структуром, СЕО-ом и интеграцијом са спољним услугама. Резултат који произилази из свих ових комбинација је корисничко искуство (УКС) којем увек треба дати приоритет.
АнгуларЈС је врло моћан оквир. То је треће спремиште са звездицом на ГитХуб-у. Није тешко започети употребу, али циљеви којима је намењен постизању разумевања потражње. Програмери АнгуларЈС више не могу да игноришу потрошњу меморије, јер се она више неће ресетовати на навигацији. Ово је претходница веб развој . Прихватимо то!
Постоји неколико подешавања оптимизације препоручених за производњу. Један од њих је онемогућавање информација о отклањању грешака.
DebugInfoEnabled
је поставка која је подразумевано тачна и омогућава приступ опсегу кроз ДОМ чворове. Ако то желите испробати путем ЈаваСцрипт конзоле, одаберите елемент ДОМ и приступите његовом опсегу помоћу:
angular.element(document.body).scope()
Може бити корисно чак и када се не користи јКуери са својим ЦСС-ом, али не би требало да се користи ван конзоле. Разлог је тај када $compileProvider.debugInfoEnabled
је постављено на фалсе, позивајући .scope()
на ДОМ чвору ће се вратити undefined
.
То је једна од ретких препоручених опција за производњу.
Имајте на уму да опсегу и даље можете приступити преко конзоле, чак и када је у производњи. Позови angular.reloadWithDebugInfo()
са конзоле и апликација ће управо то учинити.
Вероватно сте то прочитали да јесте немајући тачку у вашем нг-моделу , радили сте погрешно. Када је реч о наследству, та изјава је често тачна. Опсези имају прототипски модел наслеђивања, типичан за ЈаваСцрипт, а угнежђени опсези су уобичајени за АнгуларЈС. Многе директиве креирају подређене опсеге као што су ngRepeat
, ngIf
и ngController
. При решавању модела, претраживање започиње на тренутном опсегу и пролази кроз сваки родитељски опсег, све до $rootScope
.
Али, приликом постављања нове вредности, шта ће се догодити зависи од тога какав модел (променљиву) желимо да променимо. Ако је модел примитиван, подређени опсег ће само створити нови модел. Али ако је промена својства објекта модела, претрага надређених опсега пронаћи ће референцирани објекат и променити његово стварно својство. Нови модел не би био постављен на тренутни опсег, па се не би десило маскирање:
function MainController($scope) { $scope.foo = 1; $scope.bar = {innerProperty: 2}; } angular.module('myApp', []) .controller('MainController', MainController);
OUTER SCOPE:
{{ foo }}
{{ bar.innerProperty }}
INNER SCOPE
{{ foo }}
{{ bar.innerProperty }}
Set primitive Mutate object
Кликом на дугме са ознаком „Сет примитиве“ поставићете фоо у унутрашњем опсегу на 2, али неће променити фоо у спољном опсегу.
Кликом на дугме са ознаком „Промени објекат“ промениће се својство траке из родитељског опсега. Будући да на унутрашњем опсегу нема променљиве, неће се догодити сенчење, а видљива вредност траке биће 3 у оба опсега.
Други начин да се то уради је искористити чињеницу да се родитељски опсези и основни опсег позивају из сваког опсега. Тхе $parent
и $root
објекти се могу користити за приступ надређеном опсегу и $rootScope
, директно из погледа. Можда је моћан начин, али нисам љубитељ тога због проблема са циљањем одређеног опсега уз ток. Постоји још један начин за подешавање и приступ својствима специфичним за опсег - коришћењем controllerAs
синтакса.
Алтернативни и најефикаснији начин додељивања модела за употребу објекта контролера уместо убризганог опсега $. Уместо убризгавања опсега, можемо дефинисати моделе попут овог:
function MainController($scope) { this.foo = 1; var that = this; var setBar = function () { // that.bar = {someProperty: 2}; this.bar = {someProperty: 2}; }; setBar.call(this); // there are other conventions: // var MC = this; // setBar.call(this); when using 'this' inside setBar() }
OUTER SCOPE:
{{ MC.foo }}
{{ MC.bar.someProperty }}
INNER SCOPE
{{ MC.foo }}
{{ MC.bar.someProperty }}
Change MC.foo Change MC.bar.someProperty
Ово је много мање збуњујуће. Нарочито када постоји много угнежђених опсега, као што то може бити случај са угнежђеним стањима.
Постоји још много тога о синтакси управљача.
Постоји неколико напомена о томе како је објекат контролера изложен. У основи је то објекат постављен на опсег контролера, баш као и нормалан модел.
Ако требате гледати својство објекта контролера, можете гледати функцију, али од вас се то не тражи. Ево примера:
function MainController($scope) { this.title = 'Some title'; $scope.$watch(angular.bind(this, function () { return this.title; }), function (newVal, oldVal) { // handle changes }); }
Једноставније је само урадити:
function MainController($scope) { this.title = 'Some title'; $scope.$watch('MC.title', function (newVal, oldVal) { // handle changes }); }
Што значи да такође низ опсег опсега можете да приступите МЦ-у са подређеног контролера:
function NestedController($scope) { if ($scope.MC && $scope.MC.title === 'Some title') { $scope.MC.title = 'New title'; } }
Међутим, да бисте то могли да урадите, морате да будете у складу са скраћеницом коју користите за цонтроАс. Постоје најмање три начина да се то постави. Већ сте видели прву:
…
Међутим, ако користите ui-router
, одређивање контролера на тај начин је склоно грешкама. За државе, контролори би требали бити наведени у конфигурацији стања:
angular.module('myApp', []) .config(function ($stateProvider) { $stateProvider .state('main', { url: '/', controller: 'MainController as MC', templateUrl: '/path/to/template.html' }) }). controller('MainController', function () { … });
Постоји још један начин напомена:
(…) .state('main', { url: '/', controller: 'MainController', controllerAs: 'MC', templateUrl: '/path/to/template.html' })
То можете учинити у директивама:
function AnotherController() { this.text = 'abc'; } function testForApeeScape() { return { controller: 'AnotherController as AC', template: '{{ AC.text }}
' }; } angular.module('myApp', []) .controller('AnotherController', AnotherController) .directive('testForApeeScape', testForApeeScape);
Важи и други начин бележења, мада мање сажет:
function testForApeeScape() { return { controller: 'AnotherController', controllerAs: 'AC', template: '{{ AC.text }}
' }; }
Де фацто решење рутирања за АнгуларЈС је до сада било ui-router
. Уклоњен из језгра пре неког времена, нгРоуте модул, био је превише основни за софистицираније рутирање.
Постоји нови NgRouter
на путу, али аутори и даље сматрају да је прерано за производњу. Када ово пишем, стабилни Ангулар је 1.3.15 и ui-router
стене.
Главни разлози:
Овде ћу покрити гнежђење стања како бих избегао грешке АнгуларЈС.
Схватите ово као сложен, али стандардан случај употребе. Постоји апликација која има приказ почетне странице и производа. Приказ производа има три одвојена одељка: увод, виџет и садржај. Желимо да виџет настави и да се не учита поново приликом пребацивања између стања. Али садржај треба да се поново учита.
Размотрите следећу структуру странице индекса ХТМЛ производа:
SOME PRODUCT SPECIFIC INTRO
Product title
Context-specific content
То је нешто што бисмо могли добити од ХТМЛ кодера и сада га морамо раздвојити у датотеке и стања. Генерално пристајем на конвенцију да постоји апстрактно ГЛАВНО стање, које по потреби чува глобалне податке. Користите то уместо $ роотСцопе. Тхе Главни држава ће такође задржати статични ХТМЛ који је потребан на свакој страници. Одржавам индек.хтмл чистим.
function config($stateProvider) { $stateProvider // MAIN ABSTRACT STATE, ALWAYS ON .state('main', { abstract: true, url: '/', controller: 'MainController as MC', templateUrl: '/routing-demo/main.html' }) // A SIMPLE HOMEPAGE .state('main.homepage', { url: '', controller: 'HomepageController as HC', templateUrl: '/routing-demo/homepage.html' }) // THE ABOVE IS ALL GOOD, HERE IS TROUBLE // A COMPLEX PRODUCT PAGE .state('main.product', { abstract: true, url: ':id', controller: 'ProductController as PC', templateUrl: '/routing-demo/product.html', }) // PRODUCT DEFAULT SUBSTATE .state('main.product.index', { url: '', views: { 'widget': { controller: 'WidgetController as PWC', templateUrl: '/routing-demo/widget.html' }, 'intro': { controller: 'IntroController as PIC', templateUrl: '/routing-demo/intro.html' }, 'content': { controller: 'ContentController as PCC', templateUrl: '/routing-demo/content.html' } } }) // PRODUCT DETAILS SUBSTATE .state('main.product.details', { url: '/details', views: { 'widget': { controller: 'WidgetController as PWC', templateUrl: '/routing-demo/widget.html' }, 'content': { controller: 'ContentController as PCC', templateUrl: '/routing-demo/content.html' } } }); } angular.module('articleApp', [ 'ui.router' ]) .config(config);
main.product.index
Затим да видимо страницу индекса производа:
main.product.details
Као што видите, страница индекса производа има три именована приказа. Један за увод, један за виџет и један за производ. Упознајемо спецификације! Па сада поставимо рутирање:
ui-router
То би био први приступ. Сада, шта се дешава приликом пребацивања између // A COMPLEX PRODUCT PAGE // WITH NO MORE TROUBLE .state('main.product', { abstract: true, url: ':id', views: { // TARGETING THE UNNAMED VIEW IN MAIN.HTML '@main': { controller: 'ProductController as PC', templateUrl: '/routing-demo/product.html' }, // TARGETING THE WIDGET VIEW IN PRODUCT.HTML // BY DEFINING A CHILD VIEW ALREADY HERE, WE ENSURE IT DOES NOT RELOAD ON CHILD STATE CHANGE ' [email protected] ': { controller: 'WidgetController as PWC', templateUrl: '/routing-demo/widget.html' } } }) // PRODUCT DEFAULT SUBSTATE .state('main.product.index', { url: '', views: { 'intro': { controller: 'IntroController as PIC', templateUrl: '/routing-demo/intro.html' }, 'content': { controller: 'ContentController as PCC', templateUrl: '/routing-demo/content.html' } } }) // PRODUCT DETAILS SUBSTATE .state('main.product.details', { url: '/details', views: { 'content': { controller: 'ContentController as PCC', templateUrl: '/routing-demo/content.html' } } });
и $urlRouterProvider.deferIntercept()
? Садржај и виџет се поново учитавају, али ми желимо само да поново учитамо садржај. Ово је било проблематично, а програмери су заправо креирали рутере који би подржавали управо ту функционалност. Једно од имена за ово било је лепљиви погледи . Срећом, 'use strict'; function yoda() { var privateMethod = function () { // this function is not exposed }; var publicMethod1 = function () { // this function is exposed, but it's internals are not exposed // some logic... }; var publicMethod2 = function (arg) { // THE BELOW CALL CANNOT BE SPIED ON WITH JASMINE publicMethod1('someArgument'); }; // IF THE LITERAL IS RETURNED THIS WAY, IT CAN'T BE REFERRED TO FROM INSIDE return { publicMethod1: function () { return publicMethod1(); }, publicMethod2: function (arg) { return publicMethod2(arg); } }; } angular.module('app', []) .factory('yoda', yoda);
подржава то из кутије са апсолутно именовано циљање приказа .
publicMethod1
Премештањем дефиниције стања у родитељски приказ, који је такође апстрактан, можемо сачувати подређени приказ од поновног учитавања приликом пребацивања УРЛ-ова што обично утиче на браћу и сестре тог детета. Наравно, виџет би могао бити једноставна директива. Али поента је у томе што би то могло бити и друго сложено угнежђено стање.
Постоји још један начин да се то уради употребом function yoda() { var privateMethod = function () { // this function is not exposed }; var publicMethod1 = function () { // this function is exposed, but it's internals are not exposed // some logic... }; var publicMethod2 = function (arg) { // the below call cannot be spied on publicMethod1('someArgument'); // BUT THIS ONE CAN! hostObject.publicMethod1('aBetterArgument'); }; var hostObject = { publicMethod1: function () { return publicMethod1(); }, publicMethod2: function (arg) { return publicMethod2(arg); } }; return hostObject; }
, али мислим да је коришћење конфигурације стања заправо боље. Ако сте заинтересовани за пресретање рута, написао сам мали водич о томе СтацкОверфлов .
Ово грешка је лакшег калибра и више је питање стила него избегавање порука о грешкама АнгуларЈС. Можда сте раније приметили да ретко прослеђујем анонимне функције декларацијама ангулар интерне. Обично прво дефинишем функцију, а затим је проследим.
Ово се тиче више од само функција. Овај приступ сам добио из читања водича за стил, посебно Аирбнб-ових и Тодд Мотто-ових. Верујем да постоји неколико предности и готово никаквих недостатака.
Пре свега, својим функцијама и објектима можете много лакше манипулисати и мутирати ако су додељени променљивој. Друго, код је чишћи и лако се може поделити у датотеке. То значи одрживост. Ако не желите да загађујете глобални простор имена, умотајте сваку датотеку у ИИФЕ. Трећи разлог је тестираност. Размотрите овај пример:
function scoringService($q) { var scoreItems = function (items, weights) { var deferred = $q.defer(); var worker = new Worker('/worker-demo/scoring.worker.js'); var orders = { items: items, weights: weights }; worker.postMessage(orders); worker.onmessage = function (e) { if (e.data && e.data.ready) { deferred.resolve(e.data.items); } }; return deferred.promise; }; var hostObject = { scoreItems: function (items, weights) { return scoreItems(items, weights); } }; return hostObject; } angular.module('app.worker') .factory('scoringService', scoringService);
Дакле, сада бисмо могли да се ругамо 'use strict'; function scoringFunction(items, weights) { var itemsArray = []; for (var i = 0; i b.sum) { return -1; } else if (a.sum
scoringService.scoreItems()
Овде се не ради само о стилу, јер је у ствари код вишекратно употребљив и идиоматичан. Програмер добија изражајнију снагу. Дијељење цијелог кода у самосталне блокове само олакшава.
У неким сценаријима може бити потребно обрадити велики низ сложених објеката пролазећи их кроз скуп филтера, декоратера и на крају алгоритма за сортирање. Један од случајева примене је када апликација треба да ради ван мреже или када су перформансе приказивања података кључне. А пошто је ЈаваСцрипт једнонитни, релативно је лако замрзнути прегледач.
Такође је лако то избећи код веб радника. Чини се да не постоји ниједна популарна библиотека која то обрађује посебно за АнгуларЈС. Можда би било најбоље, јер је примена лака.
Прво подесимо услугу:
.toString()
Сада радник:
function resolve(index, timeout) { return { data: function($q, $timeout) { var deferred = $q.defer(); $timeout(function () { deferred.resolve(console.log('Data resolve called ' + index)); }, timeout); return deferred.promise; } }; } function configResolves($stateProvide) { $stateProvider // MAIN ABSTRACT STATE, ALWAYS ON .state('main', { url: '/', controller: 'MainController as MC', templateUrl: '/routing-demo/main.html', resolve: resolve(1, 1597) }) // A COMPLEX PRODUCT PAGE .state('main.product', { url: ':id', controller: 'ProductController as PC', templateUrl: '/routing-demo/product.html', resolve: resolve(2, 2584) }) // PRODUCT DEFAULT SUBSTATE .state('main.product.index', { url: '', views: { 'intro': { controller: 'IntroController as PIC', templateUrl: '/routing-demo/intro.html' }, 'content': { controller: 'ContentController as PCC', templateUrl: '/routing-demo/content.html' } }, resolve: resolve(3, 987) }); }
Сада убризгајте услугу као и обично и третирајте Data resolve called 3 Data resolve called 1 Data resolve called 2 Main Controller executed Product Controller executed Intro Controller executed
као и сваки други начин услуге који враћа обећање. Тешка обрада ће се извршити на посебном навоју и неће нанети штету УКС-у.
На шта треба пазити:
.run()
на положеном имању и исправно је функционисало.Решења додају додатно време учитавању приказа. Верујем да су нам високе перформансе фронт-енд апликације примарни циљ. Не би требало да представља проблем да прикажете неке делове приказа док апликација чека податке из АПИ-ја.
Размотрите ово подешавање:
this.maxPrice = '100'; this.price = '55'; $scope.$watch('MC.price', function (newVal) { if (newVal || newVal === 0) { for (var i = 0; i <987; i++) { console.log('ALL YOUR BASE ARE BELONG TO US'); } } });
Излаз конзоле биће:
this.maxPrice = '100'; this.price = '55'; this.priceTemporary = '55'; $scope.$watch('MC.price', function (newVal) { if (!isNaN(newVal)) { for (var i = 0; i <987; i++) { console.log('ALL YOUR BASE ARE BELONG TO US'); } } }); var timeoutInstance; $scope.$watch('MC.priceTemporary', function (newVal) { if (!isNaN(newVal)) { if (timeoutInstance) { $timeout.cancel(timeoutInstance); } timeoutInstance = $timeout(function () { $scope.MC.price = newVal; }, 144); } });
Што у основи значи да:
То значи да пре него што корисник види било какав излаз, мора да сачека све зависности. Морамо имати те податке, наравно, у реду. Ако је апсолутно неопходно да га имате пре погледа, ставите га у $digest()
блокирати. У супротном, само позовите услугу из контролера и грациозно се побрините за напола оптерећено стање. Видети да је посао у току - а контролер је већ покренут, па је то заправо напредак - боље него да апликација застаје.
а) Узроковање превише сварљивих петљи, попут причвршћивања клизача на моделе
Ово је општи проблем који може резултирати грешкама АнгуларЈС, али о њему ћу расправљати на примеру клизача. Користио сам ову библиотеку клизача, клизач угаоног опсега, јер ми је била потребна проширена функционалност. Та директива има ову синтаксу у минималној верзији:
ng-click
Узмите у обзир следећи код у контролеру:
input
Дакле, то ради споро. Случајно решење било би постављање временског ограничења на улазу. Али то није увек згодно и понекад заиста не желимо да одложимо стварну промену модела у свим случајевима.
Тако ћемо додати привремени модел који ће морати да промени радни модел по истеку времена:
$timeout
а у контролеру:
$http
б) Не користи $ апплиАсинц
АнгуларЈС нема механизам за позивање $watch
. Извршава се само зато што користимо директиве (нпр. .$applyAsync()
, $digest()
), Услуге (applyAsync
, $http
) и методе (mymodule.config(function ($httpProvider) { $httpProvider.useApplyAsync(true); });
) које процењују наш код и после тога назовите сажетак.
Шта .click()
да ли одлаже решавање израза до следећег $apply()
циклус, који се покреће након 0 временског ограничења, што је заправо ~ 10мс.
Постоје два начина за употребу $scope.$root.$digest()
Сада. Аутоматски начин за $rootScope.$digest()
захтеве, а за остале ручни начин.
Да бисте све хттп захтеве који се враћају приближно у исто време решили у једном сажетку, урадите следеће:
$scope.$digest()
Ручни начин показује како то заправо функционише. Размотрите неку функцију која се у повратном позиву изводи на ванили ЈС преслушавачу догађаја или јКуери I AM DIRECTIVE$scope.$applyAsync()
или некој другој спољној библиотеци. Након извршавања и промене модела, ако га већ нисте умотали у $digest()
потребно је да позовете remove directive
(function MainController($rootScope, $scope) { this.removeDirective = function () { $rootScope.$emit('destroyDirective'); }; } function testForApeeScape($rootScope, $timeout) { return { link: function (scope, element, attributes) { var destroyListener = $rootScope.$on('destroyDirective', function () { scope.$destroy(); }); // adding a timeout for the DOM to get ready $timeout(function () { scope.toBeDetached = element.find('p'); }); scope.$on('$destroy', function () { destroyListener(); element.remove(); }); }, template: '
), или најмање scope.$on('$destroy', function () { // setting this model to null // will solve the problem. scope.toBeDetached = null; destroyListener(); element.remove(); });
. У супротном, нећете видети промене.
Ако то учините више пута у једном току, можда ће почети да ради споро. Размислите о позивању … the isolated scope is not available here, look: {{ isolatedModel }}
на изразима уместо тога. Поставит ће само један циклус сажетка за све њих.
ц) Тешка обрада слика
Ако имате лош учинак, разлог можете истражити помоћу хронологије из Цхроме Девелопер Тоолс. О овој алатки ћу написати више грешком # 17. Ако на графикону временске оријентације доминира зелена боја након снимања, проблеми са перформансама могу се односити на обраду слика. Ово није стриктно повезано са АнгуларЈС, али се може догодити поврх проблема са перформансама АнгуларЈС (који би на графикону углавном били жути). Као фронт-енд инжењери, морамо размишљати о комплетном завршном пројекту.
Одвојите тренутак да процените:
Ако сте на најмање три од горе наведених одговорили са „да“, размислите о његовом ублажавању. Можда можете да приказујете различите величине слика и да уопште не мењате величину. Можда бисте могли да додате хак за обраду ГПУ-а „трансформ: транслатеЗ (0)“. Или користите рекуестАниматионФраме за руковаоце.
Много пута вероватно чујете да се не препоручује употреба јКуери-а са АнгуларЈС-ом и да то треба избегавати. Неопходно је разумјети разлог који стоји иза ових изјава. Постоје барем три разлога, колико видим, али ниједан од њих није стварни блокатор.
Разлог 1: Када извршите јКуери код, морате позвати function MainController($interval) { this.foo = { bar: 1 }; this.baz = 1; var that = this; $interval(function () { that.foo.bar++; }, 144); $interval(function () { that.baz++; }, 144); this.quux = [1,2,3]; }
себе. У многим случајевима постоји Решење АнгуларЈС који је прилагођен за АнгуларЈС и може бити бољи за употребу у програму Ангулар од јКуери-а (нпр. нг-цлицк или систем догађаја).
Разлог 2: Метод размишљања о изградњи апликације. Ако сте додавали ЈаваСцрипт веб локацијама, које се поново учитавају током навигације, нисте морали превише да бринете о потрошњи меморије. Са апликацијама на једној страници морате да бринете. Ако не почистите, корисници који проведу више од неколико минута у вашој апликацији могу да имају растуће проблеме са учинком.
Разлог 3: Чишћење заправо није најлакше учинити и анализирати. Из скрипте (у прегледачу) не можете позвати сакупљач смећа. Можда ћете завршити са одвојеним ДОМ стаблима. Направио сам пример (јКуери се учитава у индек.хтмл):
function testDirective() { var postLink = function (scope, element, attrs) { scope.$watch(attrs.watchAttribute, function (newVal) { if (newVal) { // take a look in the console // we can't use the attribute directly console.log(attrs.watchAttribute); // the newVal is evaluated, and it can be used scope.modifiedFooBar = newVal.bar * 10; } }, true); attrs.$observe('observeAttribute', function (newVal) { scope.observed = newVal; }); }; return { link: postLink, templateUrl: '/attributes-demo/test-directive.html' }; }
Ово је једноставна директива која даје неки текст. Испод њега се налази дугме које ће само ручно уништити директиву.
Дакле, када се директива уклони, остаје референца на ДОМ стабло у сцопе.тоБеДетацхед. Ако приступите картици „профили“, а затим „направите брзи снимак“ у цхроме дев алаткама, у излазу ћете видети:
Можете живети са неколицином, али лоше је ако имате тону. Поготово ако га из неког разлога, као у примеру, похраните на опсег. Читав ДОМ ће се процењивати на сваком сажетку. Проблематично одвојено ДОМ стабло је оно са 4 чвора. Па како се то може решити?
attrs.watchAttribute
Уклоњено је одвојено ДОМ стабло са 4 уноса!
У овом примеру, директива користи исти опсег и на опсегу чува елемент ДОМ. Било ми је лакше да то тако покажем. Не постаје увек толико лоше, јер бисте га могли сачувати у променљивој. Међутим, и даље би заузимала меморију ако би неко затварање које се односи на ту променљиву или било које друго из истог опсега функције живело даље.
Кад год вам је потребна директива за коју знате да ће се користити на једном месту или за коју не очекујете да ће бити у сукобу са било којим окружењем у којој се користи, нема потребе да користите изоловани опсег. У последње време постоји тренд стварања компонената за вишекратну употребу, али да ли сте знали да основне кутне директиве уопште не користе изоловани опсег?
Два су главна разлога: не можете применити две изоловане директиве опсега на елемент и можда ћете наићи на проблеме са гнежђењем / наслеђивањем / обрадом догађаја. Нарочито у вези са трансклузијом - ефекти можда неће бити оно што очекујете.
Дакле, ово би пропало:
scope.$watch()
Па чак и ако користите само једну директиву, приметићете да ни изоловани модели опсега ни догађаји емитовани у исолатедСцопеДирецтиве неће бити доступни компанији АнотхерЦонтроллер. Кад је то тужно, можете да се савијете и употребите магију за укључивање да бисте то учинили - али за већину случајева употребе нема потребе да се изолујете.
MC.foo
Дакле, два питања сада:
Постоје два начина, у оба преносите вредности атрибутима. Размотрите овај МаинЦонтроллер:
$watch()
То контролише овај поглед:
MC.foo
Приметите да „атрибут сата“ није интерполиран. Све то функционише, захваљујући ЈС магији. Ево дефиниције директиве:
$parse
Приметите да $eval
се преноси у $destroy
без наводника! То значи да је оно што је заправо прослеђено $ ватцх-у био низ function cleanMeUp($interval, $rootScope, $timeout) { var postLink = function (scope, element, attrs) { var rootModelListener = $rootScope.$watch('someModel', function () { // do something }); var myInterval = $interval(function () { // do something in intervals }, 2584); var myTimeout = $timeout(function () { // defer some action here }, 1597); scope.domElement = element; $timeout(function () { // calling $destroy manually for testing purposes scope.$destroy(); }, 987); // here is where the cleanup happens scope.$on('$destroy', function () { // disable the listener rootModelListener(); // cancel the interval and timeout $interval.cancel(myInterval); $timeout.cancel(myTimeout); // nullify the DOM-bound model scope.domElement = null; }); element.on('$destroy', function () { // this is a jQuery event // clean up all vanilla JavaScript / jQuery artifacts here // respectful jQuery plugins have $destroy handlers, // that is the reason why this event is emitted... // follow the standards. }); };
! Међутим, то функционише, јер је било који низ прешао у $destroy
добија оцену према опсегу и $digest()
је доступан на обиму. То је такође најчешћи начин на који се атрибути гледају у основним директивама АнгуларЈС.
Погледајте код на гитхуб-у за предложак и погледајте {{ model }}
и
за још више страхота.
АнгуларЈС обавља неке послове у ваше име, али не све. Следеће треба ручно очистити:
vastArray
догађајАко то не урадите ручно, наићи ћете на неочекивано понашање и цурење меморије. Још горе - они неће бити одмах видљиви, али ће временом пузати. Марфијев закон.
Невероватно, АнгуларЈС пружа практичне начине да се носите са свим тим:
item.velocity
Обратите пажњу на јКуери {{ someModel }}
догађај. Зове се попут АнгуларЈС, али се њиме рукује одвојено. Гледаоци опсега $ неће реаговати на јКуери догађај.
Ово би сада требало бити сасвим једноставно. Овде треба схватити једну ствар: ng-if
. За свако везивање ng-repeat
, АнгуларЈС креира посматрач. У свакој фази дигестије, свако такво везивање се процењује и упоређује са претходном вредношћу. То се зове прљава провера и то $ дигест ради. Ако се вредност променила од последње провере, активира се повратни позив посматрача. Ако тај повратни позив проматрача модификује модел (променљива $ сцопе), покреће се нови циклус $ дигест (до највише 10) када се изузме изузетак.
Прегледачи немају проблема ни са хиљадама веза, осим ако изрази нису сложени. Уобичајени одговор за „колико посматрача може имати“ је 2000.
Па, како можемо ограничити број посматрача? Негледавањем модела опсега када не очекујемо да се промене. Од АнгуларЈС 1.3 даље је прилично лако, јер су једнократне везе сада у основи.
$watch()
После $Watchers
и $watch()
процене се једном, никада се више неће променити. И даље можете применити филтере на низ, они ће сасвим добро функционисати. Само што сам низ неће бити оцењен. У многим случајевима то је победа.
Ова грешка АнгуларЈС већ је делимично покривена грешкама 9.б и 13. Ово је темељније објашњење. АнгуларЈС ажурира ДОМ као резултат функција повратног позива гледаоцима. Свако везивање, то је директива $rootScope.$digest()
поставља посматраче, али посматрачи су постављени и за многе друге директиве попут $scope
и $watch()
. Само погледајте изворни код, врло је читљив. Посматрачи се могу подесити и ручно, а то сте вероватно и сами учинили најмање неколико пута.
$digest()
они су везани за опсеге. .$apply
могу узети низове који се вреднују према опсегу који .$applyAsync
био везан за. Такође могу проценити функције. И они такође узимају повратне позиве. Дакле, када .$evalAsync
, сви регистровани модели (односно $digest()
променљиве) се процењују и упоређују са њиховим претходним вредностима. Ако се вредности не подударају, повратни позив на $digest()
извршава се.
Важно је схватити да, иако је вредност модела промењена, повратни позив се не активира до следеће фазе сажетка. С разлогом се назива „фаза“ - може се састојати од неколико циклуса варења. Ако само посматрач промени модел опсега, извршава се други циклус сажетка.
Али describe('some module', function () { it('should call the name-it service…', function () { // leave this empty for now }); ... });
није анкетирано за . Позива се из основних директива, услуга, метода итд. Ако промените модел из прилагођене функције која не позива 'use strict'; var gulp = require('gulp'); var args = require('yargs').argv; var browserSync = require('browser-sync'); var karma = require('gulp-karma'); var protractor = require('gulp-protractor').protractor; var webdriverUpdate = require('gulp-protractor').webdriver_update; function test() { // Be sure to return the stream // NOTE: Using the fake './foobar' so as to run the files // listed in karma.conf.js INSTEAD of what was passed to // gulp.src ! return gulp.src('./foobar') .pipe(karma({ configFile: 'test/karma.conf.js', action: 'run' })) .on('error', function(err) { // Make sure failed tests cause gulp to exit non-zero // console.log(err); this.emit('end'); //instead of erroring the stream, end it }); } function tdd() { return gulp.src('./foobar') .pipe(karma({ configFile: 'test/karma.conf.js', action: 'start' })) .on('error', function(err) { // Make sure failed tests cause gulp to exit non-zero // console.log(err); // this.emit('end'); // not ending the stream here }); } function runProtractor () { var argument = args.suite || 'all'; // NOTE: Using the fake './foobar' so as to run the files // listed in protractor.conf.js, instead of what was passed to // gulp.src return gulp.src('./foobar') .pipe(protractor({ configFile: 'test/protractor.conf.js', args: ['--suite', argument] })) .on('error', function (err) { // Make sure failed tests cause gulp to exit non-zero throw err; }) .on('end', function () { // Close browser sync server browserSync.exit(); }); } gulp.task('tdd', tdd); gulp.task('test', test); gulp.task('test-e2e', ['webdriver-update'], runProtractor); gulp.task('webdriver-update', webdriverUpdate);
, console.log()
, debugInfo
или било шта друго што евентуално позива $(document.body).scope().$root
, везови неће бити ажурирани.
Иначе, изворни код за $($0).scope()
је заправо прилично сложен. Ипак је вредно прочитати, јер урнебесна упозорења то надокнађују.
Ако пратите трендове у фронт енд развоју и помало сте лењи - попут мене - онда вероватно покушавате да не радите све ручно. Праћење свих ваших зависности, обрада скупова датотека на различите начине, поновно учитавање прегледача након сваког чувања датотеке - развијање је пуно више од самог кодирања.
Дакле, можда користите бовер и можда нпм у зависности од тога како служите својој апликацији. Постоји шанса да можда користите гунђање, гутљај или бранч. Или басх, што је такође супер. У ствари, можда сте започели свој најновији пројекат са неким Иеоман генератором!
То доводи до питања: да ли разумете читав процес онога што ваша инфраструктура заиста ради? Да ли вам треба оно што имате, посебно ако сте само сате покушавали да поправите функционалност ливерелоад-а вашег веб сервера?
Одвојите секунду да процените шта вам треба. Сви ти алати су овде само да вам помогну, нема друге награде за њихово коришћење. Искуснији програмери са којима разговарам теже да поједноставе ствари.
Тестови неће учинити ваш код без порука о грешкама АнгуларЈС. Оно што ће они учинити је да осигурају да ваш тим не наилази стално на проблеме регресије.
Овде пишем посебно о јединственим тестовима, не зато што сматрам да су они важнији од е2е тестова, већ зато што се извршавају много брже. Морам признати да је процес који ћу описати врло угодан.
Тест Дривен Девелопмент као примена за нпр. гулп-карма руннер, у основи покреће све ваше јединичне тестове на сваком спремању датотеке. Мој омиљени начин писања тестова је да прво напишем празна уверења:
angular.reloadWithDebugInfo()
После тога напишем или рефакторишем стварни код, а затим се вратим тестовима и испуним гаранције стварним кодом теста.
Покретање ТДД задатка у терминалу убрзава процес за око 100%. Јединствени тестови се извршавају у року од неколико секунди, чак и ако их имате пуно. Само сачувајте тест датотеку и тркач ће је подићи, проценити ваше тестове и одмах пружити повратне информације.
Са е2е тестовима, процес је много спорији. Мој савет - поделите е2е тестове у тест пакете и само покрените један по један. Кутомер их подржава, а испод је код који користим за своје тест задатке (волим гутљај).
var injector = $(document.body).injector(); var someService = injector.get('someService');
А - хром тачке прелома
Цхроме дев алати вам омогућавају да усмерите на одређено место у било којој од датотека учитаних у прегледач, зауставите извршавање кода у том тренутку и омогућите вам интеракцију са свим променљивим доступним са те тачке. То је пуно! Та функционалност уопште не захтева додавање било ког кода, све се дешава у развојним алаткама.
Не само да добијате приступ свим променљивим, већ видите и скуп позива, трагове низа штампе и још много тога. Можете га чак конфигурисати за рад са минификованим датотекама. Прочитајте о томе овде .
Постоје и други начини на које можете добити сличан приступ током извођења, нпр. додавањем Ng-init
позива. Али тачке прекида су софистицираније.
АнгуларЈС вам такође омогућава приступ опсегу кроз ДОМ елементе (све док је омогућен ng-if
) и убризгавање доступних услуга путем конзоле. Узмите у обзир следеће у конзоли:
ng-repeat
или усмерите на елемент у инспектору, а затим:
var ngInitDirective = ngDirective({ priority: 450, compile: function() { return { pre: function(scope, element, attrs) { scope.$eval(attrs.ngInit); } }; } });
Чак и ако дебугИнфо није омогућен, можете:
var ngShowDirective = ['$animate', function($animate) { return { restrict: 'A', multiElement: true, link: function(scope, element, attr) { scope.$watch(attr.ngShow, function ngShowWatchAction(value) { // we're adding a temporary, animation-specific class for ng-hide since this way // we can control when the element is actually displayed on screen without having // to have a global/greedy CSS selector that breaks when other animations are run. // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, { tempClasses: NG_HIDE_IN_PROGRESS_CLASS }); }); } }; }];
И нека буде доступан након поновног учитавања:
Покушајте да убризгате и комуницирате са услугом са конзоле:
$watch
Б - хромирана хронологија
Још један сјајан алат који долази са развојним алаткама је временска линија. То ће вам омогућити да снимате и анализирате перформансе апликације уживо док је користите. Излаз приказује, између осталог, употребу меморије, брзину кадрова и сецирање различитих процеса који заузимају ЦПУ: учитавање, скриптирање, приказивање и сликање.
Ако приметите да се перформансе ваше апликације погоршавају, узрок томе ћете највероватније моћи да пронађете на картици временске траке. Само забележите своје радње које су довеле до проблема са перформансама и погледајте шта ће се догодити. Превише посматрача? Видећете како жуте траке заузимају пуно простора. Цурење меморије? На графикону можете видети колико је меморије потрошено током времена.
Детаљан опис: хттпс://девелопер.цхроме.цом/девтоолс/доцс/тимелине
Ц - даљински преглед апликација на иОС-у и Андроид-у
Ако развијате хибридну апликацију или прилагодљиву веб апликацију, можете да приступите конзоли уређаја, ДОМ стаблу и свим другим алаткама доступним путем Цхроме или Сафари развојних алата. То укључује ВебВиев и УИВебВиев.
Прво покрените свој веб сервер на хосту 0.0.0.0 тако да му је доступан из ваше локалне мреже. Омогућите веб инспектора у подешавањима. Затим повежите уређај са радном површином и приступите локалној развојној страници, користећи ип вашег уређаја уместо уобичајеног „лоцалхост“. То је све што је потребно, ваш уређај би вам сада требао бити доступан из прегледача радне површине.
Ево су детаљна упутства за Андроид. А за иОС, незваничне водиче можете лако пронаћи путем гоогле-а.
Недавно сам имао неко кул искуство са бровсерСинц . Ради на сличан начин за ливерелоад, али такође синхронизује све прегледаче који прегледавају исту страницу путем бровсерСинц. То укључује корисничку интеракцију попут помицања, клика на дугмад итд. Гледао сам излазни запис дневника апликације иОС док сам управљао страницом на иПаду са радне површине. Лепо је функционисало!
// when in doubt, comment it out! :)
, по звуку, треба да буде слично
|_+_|и
|_+_|, зар не? Да ли сте се икад запитали зашто у документима постоји коментар да га не треба користити? ИМХО то је било изненађујуће! Очекивао бих да директива покрене модел. То је такође оно што чини, али ... примењује се на другачији начин, то јест, не посматра вредност атрибута. Не треба да прегледате изворни код АнгуларЈС - дозволите ми да вам га донесем:
|_+_|
Мање него што бисте очекивали? Прилично читљиво, осим незгодне синтаксе директиве, зар не? Шести ред је о чему се ради.
Упоредите са нг-схов:
|_+_|
Опет шести ред. Постоји
|_+_|ето, то је оно што ову директиву чини динамичном. У изворном коду АнгуларЈС, велики део целокупног кода чине коментари који описују код који је углавном био читљив од почетка. Верујем да је то одличан начин за учење о АнгуларЈС.
Овај водич који покрива најчешће грешке АнгуларЈС скоро је двоструко дужи од осталих водича. Испало је тако природно. Потражња за висококвалитетним ЈаваСцрипт инжењерима је веома велика. АнгуларЈС је тако вруће одмах , и већ неколико година држи стабилну позицију међу најпопуларнијим развојним алатима. Са АнгуларЈС 2.0 на путу, он ће вероватно доминирати годинама које долазе.
Оно што је сјајно у погледу фронт-енд развоја је то што је то врло корисно. Наш рад је видљив тренутно, а људи директно комуницирају са производима које испоручујемо. Време проведено у учењу ЈаваСцрипт , и верујем да бисмо се требали усредсредити на језик ЈаваСцрипт, врло је добра инвестиција. То је језик Интернета. Конкуренција је супер јака! За нас постоји један фокус - корисничко искуство. Да бисмо били успешни, морамо све да покријемо.
Изворни код коришћен у овим примерима можете преузети са ГитХуб . Слободно га преузмите и направите по свом.
Желео сам да доделим кредите четворици програмера издаваштва који су ме највише инспирисали:
Такође сам желео да захвалим свим сјајним људима на ФрееНоде #ангуларјс и #јавасцрипт каналима на многим одличним разговорима и континуираној подршци.
И на крају, увек запамтите:
|_+_|
Ако пратите трендове у фронт енд развоју и помало сте лењи - попут мене - онда вероватно покушавате да не радите све ручно. Праћење свих ваших зависности, обрада скупова датотека на различите начине, поновно учитавање прегледача након сваког чувања датотеке - развијање је пуно више од самог кодирања.
Дакле, можда користите бовер и можда нпм у зависности од тога како служите својој апликацији. Постоји шанса да можда користите гунђање, гутљај или бранч. Или басх, што је такође супер. У ствари, можда сте започели свој најновији пројекат са неким Иеоман генератором!
То доводи до питања: да ли разумете читав процес онога што ваша инфраструктура заиста ради? Да ли вам треба оно што имате, посебно ако сте само сате покушавали да поправите функционалност ливерелоад-а вашег веб сервера?
Одвојите секунду да процените шта вам треба. Сви ти алати су овде само да вам помогну, нема друге награде за њихово коришћење. Искуснији програмери са којима разговарам теже да поједноставе ствари.
Тестови неће учинити ваш код без порука о грешкама АнгуларЈС. Оно што ће они учинити је да осигурају да ваш тим не наилази стално на проблеме регресије.
Овде пишем посебно о јединственим тестовима, не зато што сматрам да су они важнији од е2е тестова, већ зато што се извршавају много брже. Морам признати да је процес који ћу описати врло угодан.
Тест Дривен Девелопмент као примена за нпр. гулп-карма руннер, у основи покреће све ваше јединичне тестове на сваком спремању датотеке. Мој омиљени начин писања тестова је да прво напишем празна уверења:
angular.reloadWithDebugInfo()
После тога напишем или рефакторишем стварни код, а затим се вратим тестовима и испуним гаранције стварним кодом теста.
Покретање ТДД задатка у терминалу убрзава процес за око 100%. Јединствени тестови се извршавају у року од неколико секунди, чак и ако их имате пуно. Само сачувајте тест датотеку и тркач ће је подићи, проценити ваше тестове и одмах пружити повратне информације.
Са е2е тестовима, процес је много спорији. Мој савет - поделите е2е тестове у тест пакете и само покрените један по један. Кутомер их подржава, а испод је код који користим за своје тест задатке (волим гутљај).
var injector = $(document.body).injector(); var someService = injector.get('someService');
А - хром тачке прелома
Цхроме дев алати вам омогућавају да усмерите на одређено место у било којој од датотека учитаних у прегледач, зауставите извршавање кода у том тренутку и омогућите вам интеракцију са свим променљивим доступним са те тачке. То је пуно! Та функционалност уопште не захтева додавање било ког кода, све се дешава у развојним алаткама.
Не само да добијате приступ свим променљивим, већ видите и скуп позива, трагове низа штампе и још много тога. Можете га чак конфигурисати за рад са минификованим датотекама. Прочитајте о томе овде .
Постоје и други начини на које можете добити сличан приступ током извођења, нпр. додавањем Ng-init
позива. Али тачке прекида су софистицираније.
АнгуларЈС вам такође омогућава приступ опсегу кроз ДОМ елементе (све док је омогућен ng-if
) и убризгавање доступних услуга путем конзоле. Узмите у обзир следеће у конзоли:
ng-repeat
или усмерите на елемент у инспектору, а затим:
var ngInitDirective = ngDirective({ priority: 450, compile: function() { return { pre: function(scope, element, attrs) { scope.$eval(attrs.ngInit); } }; } });
Чак и ако дебугИнфо није омогућен, можете:
var ngShowDirective = ['$animate', function($animate) { return { restrict: 'A', multiElement: true, link: function(scope, element, attr) { scope.$watch(attr.ngShow, function ngShowWatchAction(value) { // we're adding a temporary, animation-specific class for ng-hide since this way // we can control when the element is actually displayed on screen without having // to have a global/greedy CSS selector that breaks when other animations are run. // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, { tempClasses: NG_HIDE_IN_PROGRESS_CLASS }); }); } }; }];
И нека буде доступан након поновног учитавања:
Покушајте да убризгате и комуницирате са услугом са конзоле:
$watch
Б - хромирана хронологија
Још један сјајан алат који долази са развојним алаткама је временска линија. То ће вам омогућити да снимате и анализирате перформансе апликације уживо док је користите. Излаз приказује, између осталог, употребу меморије, брзину кадрова и сецирање различитих процеса који заузимају ЦПУ: учитавање, скриптирање, приказивање и сликање.
Ако приметите да се перформансе ваше апликације погоршавају, узрок томе ћете највероватније моћи да пронађете на картици временске траке. Само забележите своје радње које су довеле до проблема са перформансама и погледајте шта ће се догодити. Превише посматрача? Видећете како жуте траке заузимају пуно простора. Цурење меморије? На графикону можете видети колико је меморије потрошено током времена.
Детаљан опис: хттпс://девелопер.цхроме.цом/девтоолс/доцс/тимелине
Ц - даљински преглед апликација на иОС-у и Андроид-у
Ако развијате хибридну апликацију или прилагодљиву веб апликацију, можете да приступите конзоли уређаја, ДОМ стаблу и свим другим алаткама доступним путем Цхроме или Сафари развојних алата. То укључује ВебВиев и УИВебВиев.
Прво покрените свој веб сервер на хосту 0.0.0.0 тако да му је доступан из ваше локалне мреже. Омогућите веб инспектора у подешавањима. Затим повежите уређај са радном површином и приступите локалној развојној страници, користећи ип вашег уређаја уместо уобичајеног „лоцалхост“. То је све што је потребно, ваш уређај би вам сада требао бити доступан из прегледача радне површине.
Ево су детаљна упутства за Андроид. А за иОС, незваничне водиче можете лако пронаћи путем гоогле-а.
Недавно сам имао неко кул искуство са бровсерСинц . Ради на сличан начин за ливерелоад, али такође синхронизује све прегледаче који прегледавају исту страницу путем бровсерСинц. То укључује корисничку интеракцију попут помицања, клика на дугмад итд. Гледао сам излазни запис дневника апликације иОС док сам управљао страницом на иПаду са радне површине. Лепо је функционисало!
// when in doubt, comment it out! :)
, по звуку, треба да буде слично
|_+_|и
|_+_|, зар не? Да ли сте се икад запитали зашто у документима постоји коментар да га не треба користити? ИМХО то је било изненађујуће! Очекивао бих да директива покрене модел. То је такође оно што чини, али ... примењује се на другачији начин, то јест, не посматра вредност атрибута. Не треба да прегледате изворни код АнгуларЈС - дозволите ми да вам га донесем:
|_+_|
Мање него што бисте очекивали? Прилично читљиво, осим незгодне синтаксе директиве, зар не? Шести ред је о чему се ради.
Упоредите са нг-схов:
|_+_|
Опет шести ред. Постоји
|_+_|ето, то је оно што ову директиву чини динамичном. У изворном коду АнгуларЈС, велики део целокупног кода чине коментари који описују код који је углавном био читљив од почетка. Верујем да је то одличан начин за учење о АнгуларЈС.
Овај водич који покрива најчешће грешке АнгуларЈС скоро је двоструко дужи од осталих водича. Испало је тако природно. Потражња за висококвалитетним ЈаваСцрипт инжењерима је веома велика. АнгуларЈС је тако вруће одмах , и већ неколико година држи стабилну позицију међу најпопуларнијим развојним алатима. Са АнгуларЈС 2.0 на путу, он ће вероватно доминирати годинама које долазе.
Оно што је сјајно у погледу фронт-енд развоја је то што је то врло корисно. Наш рад је видљив тренутно, а људи директно комуницирају са производима које испоручујемо. Време проведено у учењу ЈаваСцрипт , и верујем да бисмо се требали усредсредити на језик ЈаваСцрипт, врло је добра инвестиција. То је језик Интернета. Конкуренција је супер јака! За нас постоји један фокус - корисничко искуство. Да бисмо били успешни, морамо све да покријемо.
Изворни код коришћен у овим примерима можете преузети са ГитХуб . Слободно га преузмите и направите по свом.
Желео сам да доделим кредите четворици програмера издаваштва који су ме највише инспирисали:
Такође сам желео да захвалим свим сјајним људима на ФрееНоде #ангуларјс и #јавасцрипт каналима на многим одличним разговорима и континуираној подршци.
И на крају, увек запамтите:
|_+_|