8 |
9 | ---
10 |
11 | > Want an example structure as reference? Check out my [component based architecture 1.5 app](https://github.com/toddmotto/angular-1-5-components-app).
12 |
13 | ---
14 |
15 | *A sensible styleguide for teams by [@toddmotto](//twitter.com/toddmotto)*
16 |
17 | This architecture and styleguide has been rewritten from the ground up for ES2015, the changes in AngularJS 1.5+ for future-upgrading your application to Angular. This guide includes new best practices for one-way dataflow, event delegation, component architecture and component routing.
18 |
19 | You can find the old styleguide [here](https://github.com/toddmotto/angular-styleguide/tree/angular-old-es5), and the reasoning behind the new one [here](https://toddmotto.com/rewriting-angular-styleguide-angular-2).
20 |
21 | > Join the Ultimate AngularJS learning experience to fully master beginner and advanced AngularJS features to build real-world apps that are fast, and scale.
22 |
23 |
24 |
25 | ## Table of Contents
26 |
27 | 1. [Modular architecture](#modular-architecture)
28 | 1. [Theory](#module-theory)
29 | 1. [Root module](#root-module)
30 | 1. [Component module](#component-module)
31 | 1. [Common module](#common-module)
32 | 1. [Low-level modules](#low-level-modules)
33 | 1. [File naming conventions](#file-naming-conventions)
34 | 1. [Scalable file structure](#scalable-file-structure)
35 | 1. [Components](#components)
36 | 1. [Theory](#component-theory)
37 | 1. [Supported properties](#supported-properties)
38 | 1. [Controllers](#controllers)
39 | 1. [One-way dataflow and Events](#one-way-dataflow-and-events)
40 | 1. [Stateful Components](#stateful-components)
41 | 1. [Stateless Components](#stateless-components)
42 | 1. [Routed Components](#routed-components)
43 | 1. [Directives](#directives)
44 | 1. [Theory](#directive-theory)
45 | 1. [Recommended properties](#recommended-properties)
46 | 1. [Constants or Classes](#constants-or-classes)
47 | 1. [Services](#services)
48 | 1. [Theory](#service-theory)
49 | 1. [Classes for Service](#classes-for-service)
50 | 1. [Styles](#styles)
51 | 1. [ES2015 and Tooling](#es2015-and-tooling)
52 | 1. [State management](#state-management)
53 | 1. [Resources](#resources)
54 | 1. [Documentation](#documentation)
55 | 1. [Contributing](#contributing)
56 |
57 | # Modular architecture
58 |
59 | Each module in an Angular app is a module component. A module component is the root definition for that module that encapsulates the logic, templates, routing and child components.
60 |
61 | ### Module theory
62 |
63 | The design in the modules maps directly to our folder structure, which keeps things maintainable and predictable. We should ideally have three high-level modules: root, component and common. The root module defines the base module that bootstraps our app, and the corresponding template. We then import our component and common modules into the root module to include our dependencies. The component and common modules then require lower-level component modules, which contain our components, controllers, services, directives, filters and tests for each reusable feature.
64 |
65 | **[Back to top](#table-of-contents)**
66 |
67 | ### Root module
68 |
69 | A root module begins with a root component that defines the base element for the entire application, with a routing outlet defined, example shown using `ui-view` from `ui-router`.
70 |
71 | ```js
72 | // app.component.js
73 | export const AppComponent = {
74 | template: `
75 |
76 | Hello world
77 |
78 |
79 |
80 |
81 |
84 | `
85 | };
86 | ```
87 |
88 | A root module is then created, with `AppComponent` imported and registered with `.component('app', AppComponent)`. Further imports for submodules (component and common modules) are made to include all components relevant for the application. You'll notice styles are also being imported here, we'll come onto this in later chapters in this guide.
89 |
90 | ```js
91 | // app.module.js
92 | import angular from 'angular';
93 | import uiRouter from 'angular-ui-router';
94 | import { AppComponent } from './app.component';
95 | import { ComponentsModule } from './components/components.module';
96 | import { CommonModule } from './common/common.module';
97 | import './app.scss';
98 |
99 | export const AppModule = angular
100 | .module('app', [
101 | ComponentsModule,
102 | CommonModule,
103 | uiRouter
104 | ])
105 | .component('app', AppComponent)
106 | .name;
107 | ```
108 |
109 | **[Back to top](#table-of-contents)**
110 |
111 | ### Component module
112 |
113 | A Component module is the container reference for all reusable components. See above how we import `ComponentsModule` and inject them into the Root module, this gives us a single place to import all components for the app. These modules we require are decoupled from all other modules and thus can be moved into any other application with ease.
114 |
115 | ```js
116 | // components/components.module.js
117 | import angular from 'angular';
118 | import { CalendarModule } from './calendar/calendar.module';
119 | import { EventsModule } from './events/events.module';
120 |
121 | export const ComponentsModule = angular
122 | .module('app.components', [
123 | CalendarModule,
124 | EventsModule
125 | ])
126 | .name;
127 | ```
128 |
129 | **[Back to top](#table-of-contents)**
130 |
131 | ### Common module
132 |
133 | The Common module is the container reference for all application specific components, that we don't want to use in another application. This can be things like layout, navigation and footers. See above how we import `CommonModule` and inject them into the Root module, this gives us a single place to import all common components for the app.
134 |
135 | ```js
136 | // common/common.module.js
137 | import angular from 'angular';
138 | import { NavModule } from './nav/nav.module';
139 | import { FooterModule } from './footer/footer.module';
140 |
141 | export const CommonModule = angular
142 | .module('app.common', [
143 | NavModule,
144 | FooterModule
145 | ])
146 | .name;
147 | ```
148 |
149 | **[Back to top](#table-of-contents)**
150 |
151 | ### Low-level modules
152 |
153 | Low-level modules are individual component modules that contain the logic for each feature block. These will each define a module, to be imported to a higher-level module, such as a component or common module, an example below. Always remember to add the `.name` suffix to each `export` when creating a _new_ module, not when referencing one. You'll noticed routing definitions also exist here, we'll come onto this in later chapters in this guide.
154 |
155 | ```js
156 | // calendar/calendar.module.js
157 | import angular from 'angular';
158 | import uiRouter from 'angular-ui-router';
159 | import { CalendarComponent } from './calendar.component';
160 | import './calendar.scss';
161 |
162 | export const CalendarModule = angular
163 | .module('calendar', [
164 | uiRouter
165 | ])
166 | .component('calendar', CalendarComponent)
167 | .config(($stateProvider, $urlRouterProvider) => {
168 | 'ngInject';
169 | $stateProvider
170 | .state('calendar', {
171 | url: '/calendar',
172 | component: 'calendar'
173 | });
174 | $urlRouterProvider.otherwise('/');
175 | })
176 | .name;
177 | ```
178 |
179 | **[Back to top](#table-of-contents)**
180 |
181 | ### File naming conventions
182 |
183 | Keep it simple and lowercase, use the component name, e.g. `calendar.*.js*`, `calendar-grid.*.js` - with the name of the type of file in the middle. Use `*.module.js` for the module definition file, as it keeps it verbose and consistent with Angular.
184 |
185 | ```
186 | calendar.module.js
187 | calendar.component.js
188 | calendar.service.js
189 | calendar.directive.js
190 | calendar.filter.js
191 | calendar.spec.js
192 | calendar.html
193 | calendar.scss
194 | ```
195 |
196 | **[Back to top](#table-of-contents)**
197 |
198 | ### Scalable file structure
199 |
200 | File structure is extremely important, this describes a scalable and predictable structure. An example file structure to illustrate a modular component architecture.
201 |
202 | ```
203 | ├── app/
204 | │ ├── components/
205 | │ │ ├── calendar/
206 | │ │ │ ├── calendar.module.js
207 | │ │ │ ├── calendar.component.js
208 | │ │ │ ├── calendar.service.js
209 | │ │ │ ├── calendar.spec.js
210 | │ │ │ ├── calendar.html
211 | │ │ │ ├── calendar.scss
212 | │ │ │ └── calendar-grid/
213 | │ │ │ ├── calendar-grid.module.js
214 | │ │ │ ├── calendar-grid.component.js
215 | │ │ │ ├── calendar-grid.directive.js
216 | │ │ │ ├── calendar-grid.filter.js
217 | │ │ │ ├── calendar-grid.spec.js
218 | │ │ │ ├── calendar-grid.html
219 | │ │ │ └── calendar-grid.scss
220 | │ │ ├── events/
221 | │ │ │ ├── events.module.js
222 | │ │ │ ├── events.component.js
223 | │ │ │ ├── events.directive.js
224 | │ │ │ ├── events.service.js
225 | │ │ │ ├── events.spec.js
226 | │ │ │ ├── events.html
227 | │ │ │ ├── events.scss
228 | │ │ │ └── events-signup/
229 | │ │ │ ├── events-signup.module.js
230 | │ │ │ ├── events-signup.component.js
231 | │ │ │ ├── events-signup.service.js
232 | │ │ │ ├── events-signup.spec.js
233 | │ │ │ ├── events-signup.html
234 | │ │ │ └── events-signup.scss
235 | │ │ └── components.module.js
236 | │ ├── common/
237 | │ │ ├── nav/
238 | │ │ │ ├── nav.module.js
239 | │ │ │ ├── nav.component.js
240 | │ │ │ ├── nav.service.js
241 | │ │ │ ├── nav.spec.js
242 | │ │ │ ├── nav.html
243 | │ │ │ └── nav.scss
244 | │ │ ├── footer/
245 | │ │ │ ├── footer.module.js
246 | │ │ │ ├── footer.component.js
247 | │ │ │ ├── footer.service.js
248 | │ │ │ ├── footer.spec.js
249 | │ │ │ ├── footer.html
250 | │ │ │ └── footer.scss
251 | │ │ └── common.module.js
252 | │ ├── app.module.js
253 | │ ├── app.component.js
254 | │ └── app.scss
255 | └── index.html
256 | ```
257 |
258 | The high level folder structure simply contains `index.html` and `app/`, a directory in which all our root, component, common and low-level modules live along with the markup and styles for each component.
259 |
260 | **[Back to top](#table-of-contents)**
261 |
262 | # Components
263 |
264 | ### Component theory
265 |
266 | Components are essentially templates with a controller. They are _not_ Directives, nor should you replace Directives with Components, unless you are upgrading "template Directives" with controllers, which are best suited as a component. Components also contain bindings that define inputs and outputs for data and events, lifecycle hooks and the ability to use one-way data flow and event Objects to get data back up to a parent component. These are the new defacto standard in AngularJS 1.5 and above. Everything template and controller driven that we create will likely be a component, which may be a stateful, stateless or routed component. You can think of a "component" as a complete piece of code, not just the `.component()` definition Object. Let's explore some best practices and advisories for components, then dive into how you should be structuring them via stateful, stateless and routed component concepts.
267 |
268 | **[Back to top](#table-of-contents)**
269 |
270 | ### Supported properties
271 |
272 | These are the supported properties for `.component()` that you can/should use:
273 |
274 | | Property | Support |
275 | |---|---|
276 | | bindings | Yes, use `'@'`, `'<'`, `'&'` only |
277 | | controller | Yes |
278 | | controllerAs | Yes, default is `$ctrl` |
279 | | require | Yes (new Object syntax) |
280 | | template | Yes |
281 | | templateUrl | Yes |
282 | | transclude | Yes |
283 |
284 | **[Back to top](#table-of-contents)**
285 |
286 | ### Controllers
287 |
288 | Controllers should only be used alongside components, never anywhere else. If you feel you need a controller, what you really need is likely a stateless component to manage that particular piece of behaviour.
289 |
290 | Here are some advisories for using `Class` for controllers:
291 |
292 | * Drop the name "Controller", i.e. use `controller: class TodoComponent {...}` to aid future Angular migration
293 | * Always use the `constructor` for dependency injection purposes
294 | * Use [ng-annotate](https://github.com/olov/ng-annotate)'s `'ngInject';` syntax for `$inject` annotations
295 | * If you need to access the lexical scope, use arrow functions
296 | * Alternatively to arrow functions, `let ctrl = this;` is also acceptable and may make more sense depending on the use case
297 | * Bind all public functions directly to the `Class`
298 | * Make use of the appropriate lifecycle hooks, `$onInit`, `$onChanges`, `$postLink` and `$onDestroy`
299 | * Note: `$onChanges` is called before `$onInit`, see [resources](#resources) section for articles detailing this in more depth
300 | * Use `require` alongside `$onInit` to reference any inherited logic
301 | * Do not override the default `$ctrl` alias for the `controllerAs` syntax, therefore do not use `controllerAs` anywhere
302 |
303 | **[Back to top](#table-of-contents)**
304 |
305 | ### One-way dataflow and Events
306 |
307 | One-way dataflow was introduced in AngularJS 1.5, and redefines component communication.
308 |
309 | Here are some advisories for using one-way dataflow:
310 |
311 | * In components that receive data, always use one-way databinding syntax `'<'`
312 | * _Do not_ use `'='` two-way databinding syntax anymore, anywhere
313 | * Components that have `bindings` should use `$onChanges` to clone the one-way binding data to break Objects passing by reference and updating the parent data
314 | * Use `$event` as a function argument in the parent method (see stateful example below `$ctrl.addTodo($event)`)
315 | * Pass an `$event: {}` Object back up from a stateless component (see stateless example below `this.onAddTodo`).
316 | * Bonus: Use an `EventEmitter` wrapper with `.value()` to mirror Angular, avoids manual `$event` Object creation
317 | * Why? This mirrors Angular and keeps consistency inside every component. It also makes state predictable.
318 |
319 | **[Back to top](#table-of-contents)**
320 |
321 | ### Stateful components
322 |
323 | Let's define what we'd call a "stateful component".
324 |
325 | * Fetches state, essentially communicating to a backend API through a service
326 | * Does not directly mutate state
327 | * Renders child components that mutate state
328 | * Also referred to as smart/container components
329 |
330 | An example of a stateful component, complete with its low-level module definition (this is only for demonstration, so some code has been omitted for brevity):
331 |
332 | ```js
333 | /* ----- todo/todo.component.js ----- */
334 | import templateUrl from './todo.html';
335 |
336 | export const TodoComponent = {
337 | templateUrl,
338 | controller: class TodoComponent {
339 | constructor(TodoService) {
340 | 'ngInject';
341 | this.todoService = TodoService;
342 | }
343 | $onInit() {
344 | this.newTodo = {
345 | title: '',
346 | selected: false
347 | };
348 | this.todos = [];
349 | this.todoService.getTodos().then(response => this.todos = response);
350 | }
351 | addTodo({ todo }) {
352 | if (!todo) return;
353 | this.todos.unshift(todo);
354 | this.newTodo = {
355 | title: '',
356 | selected: false
357 | };
358 | }
359 | }
360 | };
361 |
362 | /* ----- todo/todo.html ----- */
363 |
364 |
367 |
369 |
370 |
371 | /* ----- todo/todo.module.js ----- */
372 | import angular from 'angular';
373 | import { TodoComponent } from './todo.component';
374 | import './todo.scss';
375 |
376 | export const TodoModule = angular
377 | .module('todo', [])
378 | .component('todo', TodoComponent)
379 | .name;
380 | ```
381 |
382 | This example shows a stateful component, that fetches state inside the controller, through a service, and then passes it down into stateless child components. Notice how there are no Directives being used such as `ng-repeat` and friends inside the template. Instead, data and functions are delegated into `` and `` stateless components.
383 |
384 | **[Back to top](#table-of-contents)**
385 |
386 | ### Stateless components
387 |
388 | Let's define what we'd call a "stateless component".
389 |
390 | * Has defined inputs and outputs using `bindings: {}`
391 | * Data enters the component through attribute bindings (inputs)
392 | * Data leaves the component through events (outputs)
393 | * Mutates state, passes data back up on-demand (such as a click or submit event)
394 | * Doesn't care where data comes from - it's stateless
395 | * Are highly reusable components
396 | * Also referred to as dumb/presentational components
397 |
398 | An example of a stateless component (let's use `` as an example), complete with its low-level module definition (this is only for demonstration, so some code has been omitted for brevity):
399 |
400 | ```js
401 | /* ----- todo/todo-form/todo-form.component.js ----- */
402 | import templateUrl from './todo-form.html';
403 |
404 | export const TodoFormComponent = {
405 | bindings: {
406 | todo: '<',
407 | onAddTodo: '&'
408 | },
409 | templateUrl,
410 | controller: class TodoFormComponent {
411 | constructor(EventEmitter) {
412 | 'ngInject';
413 | this.EventEmitter = EventEmitter;
414 | }
415 | $onChanges(changes) {
416 | if (changes.todo) {
417 | this.todo = Object.assign({}, this.todo);
418 | }
419 | }
420 | onSubmit() {
421 | if (!this.todo.title) return;
422 | // with EventEmitter wrapper
423 | this.onAddTodo(
424 | this.EventEmitter({
425 | todo: this.todo
426 | })
427 | );
428 | // without EventEmitter wrapper
429 | this.onAddTodo({
430 | $event: {
431 | todo: this.todo
432 | }
433 | });
434 | }
435 | }
436 | };
437 |
438 | /* ----- todo/todo-form/todo-form.html ----- */
439 |
443 |
444 | /* ----- todo/todo-form/todo-form.module.js ----- */
445 | import angular from 'angular';
446 | import { TodoFormComponent } from './todo-form.component';
447 | import './todo-form.scss';
448 |
449 | export const TodoFormModule = angular
450 | .module('todo.form', [])
451 | .component('todoForm', TodoFormComponent)
452 | .value('EventEmitter', payload => ({ $event: payload }))
453 | .name;
454 | ```
455 |
456 | Note how the `` component fetches no state, it simply receives it, mutates an Object via the controller logic associated with it, and passes it back to the parent component through the property bindings. In this example, the `$onChanges` lifecycle hook makes a clone of the initial `this.todo` binding Object and reassigns it, which means the parent data is not affected until we submit the form, alongside one-way data flow new binding syntax `'<'`.
457 |
458 | **[Back to top](#table-of-contents)**
459 |
460 | ### Routed components
461 |
462 | Let's define what we'd call a "routed component".
463 |
464 | * It's essentially a stateful component, with routing definitions
465 | * No more `router.js` files
466 | * We use Routed components to define their own routing logic
467 | * Data "input" for the component is done via the route resolve (optional, still available in the controller with service calls)
468 |
469 | For this example, we're going to take the existing `` component, refactor it to use a route definition and `bindings` on the component which receives data (the secret here with `ui-router` is the `resolve` properties we create, in this case `todoData` directly map across to `bindings` for us). We treat it as a routed component because it's essentially a "view":
470 |
471 | ```js
472 | /* ----- todo/todo.component.js ----- */
473 | import templateUrl from './todo.html';
474 |
475 | export const TodoComponent = {
476 | bindings: {
477 | todoData: '<'
478 | },
479 | templateUrl,
480 | controller: class TodoComponent {
481 | constructor() {
482 | 'ngInject'; // Not actually needed but best practice to keep here incase dependencies needed in the future
483 | }
484 | $onInit() {
485 | this.newTodo = {
486 | title: '',
487 | selected: false
488 | };
489 | }
490 | $onChanges(changes) {
491 | if (changes.todoData) {
492 | this.todos = Object.assign({}, this.todoData);
493 | }
494 | }
495 | addTodo({ todo }) {
496 | if (!todo) return;
497 | this.todos.unshift(todo);
498 | this.newTodo = {
499 | title: '',
500 | selected: false
501 | };
502 | }
503 | }
504 | };
505 |
506 | /* ----- todo/todo.html ----- */
507 |
508 |
511 |
513 |
514 |
515 | /* ----- todo/todo.service.js ----- */
516 | export class TodoService {
517 | constructor($http) {
518 | 'ngInject';
519 | this.$http = $http;
520 | }
521 | getTodos() {
522 | return this.$http.get('/api/todos').then(response => response.data);
523 | }
524 | }
525 |
526 | /* ----- todo/todo.module.js ----- */
527 | import angular from 'angular';
528 | import uiRouter from 'angular-ui-router';
529 | import { TodoComponent } from './todo.component';
530 | import { TodoService } from './todo.service';
531 | import './todo.scss';
532 |
533 | export const TodoModule = angular
534 | .module('todo', [
535 | uiRouter
536 | ])
537 | .component('todo', TodoComponent)
538 | .service('TodoService', TodoService)
539 | .config(($stateProvider, $urlRouterProvider) => {
540 | 'ngInject';
541 | $stateProvider
542 | .state('todos', {
543 | url: '/todos',
544 | component: 'todo',
545 | resolve: {
546 | todoData: TodoService => TodoService.getTodos()
547 | }
548 | });
549 | $urlRouterProvider.otherwise('/');
550 | })
551 | .name;
552 | ```
553 |
554 | **[Back to top](#table-of-contents)**
555 |
556 | # Directives
557 |
558 | ### Directive theory
559 |
560 | Directives gives us `template`, `scope` bindings, `bindToController`, `link` and many other things. The usage of these should be carefully considered now that `.component()` exists. Directives should not declare templates and controllers anymore, or receive data through bindings. Directives should be used solely for decorating the DOM. By this, it means extending existing HTML - created with `.component()`. In a simple sense, if you need custom DOM events/APIs and logic, use a Directive and bind it to a template inside a component. If you need a sensible amount of DOM manipulation, there is also the `$postLink` lifecycle hook to consider, however this is not a place to migrate all your DOM manipulation to, use a Directive if you can for non-Angular things.
561 |
562 | Here are some advisories for using Directives:
563 |
564 | * Never use templates, scope, bindToController or controllers
565 | * Always `restrict: 'A'` with Directives
566 | * Use compile and link where necessary
567 | * Remember to destroy and unbind event handlers inside `$scope.$on('$destroy', fn);`
568 |
569 | **[Back to top](#table-of-contents)**
570 |
571 | ### Recommended properties
572 |
573 | Due to the fact directives support most of what `.component()` does (template directives were the original component), I'm recommending limiting your directive Object definitions to only these properties, to avoid using directives incorrectly:
574 |
575 | | Property | Use it? | Why |
576 | |---|---|---|
577 | | bindToController | No | Use `bindings` in components |
578 | | compile | Yes | For pre-compile DOM manipulation/events |
579 | | controller | No | Use a component |
580 | | controllerAs | No | Use a component |
581 | | link functions | Yes | For pre/post DOM manipulation/events |
582 | | multiElement | Yes | [See docs](https://docs.angularjs.org/api/ng/service/$compile#-multielement-) |
583 | | priority | Yes | [See docs](https://docs.angularjs.org/api/ng/service/$compile#-priority-) |
584 | | require | No | Use a component |
585 | | restrict | Yes | Defines directive usage, always use `'A'` |
586 | | scope | No | Use a component |
587 | | template | No | Use a component |
588 | | templateNamespace | Yes (if you must) | [See docs](https://docs.angularjs.org/api/ng/service/$compile#-templatenamespace-) |
589 | | templateUrl | No | Use a component |
590 | | transclude | No | Use a component |
591 |
592 | **[Back to top](#table-of-contents)**
593 |
594 | ### Constants or Classes
595 |
596 | There are a few ways to approach using ES2015 and directives, either with an arrow function and easier assignment, or using an ES2015 `Class`. Choose what's best for you or your team, keep in mind Angular uses `Class`.
597 |
598 | Here's an example using a constant with an Arrow function an expression wrapper `() => ({})` returning an Object literal (note the usage differences inside `.directive()`):
599 |
600 | ```js
601 | /* ----- todo/todo-autofocus.directive.js ----- */
602 | import angular from 'angular';
603 |
604 | export const TodoAutoFocus = ($timeout) => {
605 | 'ngInject';
606 | return {
607 | restrict: 'A',
608 | link($scope, $element, $attrs) {
609 | $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => {
610 | if (!newValue) {
611 | return;
612 | }
613 | $timeout(() => $element[0].focus());
614 | });
615 | }
616 | }
617 | };
618 |
619 | /* ----- todo/todo.module.js ----- */
620 | import angular from 'angular';
621 | import { TodoComponent } from './todo.component';
622 | import { TodoAutofocus } from './todo-autofocus.directive';
623 | import './todo.scss';
624 |
625 | export const TodoModule = angular
626 | .module('todo', [])
627 | .component('todo', TodoComponent)
628 | .directive('todoAutofocus', TodoAutoFocus)
629 | .name;
630 | ```
631 |
632 | Or using ES2015 `Class` (note manually calling `new TodoAutoFocus` when registering the directive) to create the Object:
633 |
634 | ```js
635 | /* ----- todo/todo-autofocus.directive.js ----- */
636 | import angular from 'angular';
637 |
638 | export class TodoAutoFocus {
639 | constructor($timeout) {
640 | 'ngInject';
641 | this.restrict = 'A';
642 | this.$timeout = $timeout;
643 | }
644 | link($scope, $element, $attrs) {
645 | $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => {
646 | if (!newValue) {
647 | return;
648 | }
649 | this.$timeout(() => $element[0].focus());
650 | });
651 | }
652 | }
653 |
654 | /* ----- todo/todo.module.js ----- */
655 | import angular from 'angular';
656 | import { TodoComponent } from './todo.component';
657 | import { TodoAutofocus } from './todo-autofocus.directive';
658 | import './todo.scss';
659 |
660 | export const TodoModule = angular
661 | .module('todo', [])
662 | .component('todo', TodoComponent)
663 | .directive('todoAutofocus', ($timeout) => new TodoAutoFocus($timeout))
664 | .name;
665 | ```
666 |
667 | **[Back to top](#table-of-contents)**
668 |
669 | # Services
670 |
671 | ### Service theory
672 |
673 | Services are essentially containers for business logic that our components shouldn't request directly. Services contain other built-in or external services such as `$http`, that we can then inject into component controllers elsewhere in our app. We have two ways of doing services, using `.service()` or `.factory()`. With ES2015 `Class`, we should only use `.service()`, complete with dependency injection annotation using `$inject`.
674 |
675 | **[Back to top](#table-of-contents)**
676 |
677 | ### Classes for Service
678 |
679 | Here's an example implementation for our `` app using ES2015 `Class`:
680 |
681 | ```js
682 | /* ----- todo/todo.service.js ----- */
683 | export class TodoService {
684 | constructor($http) {
685 | 'ngInject';
686 | this.$http = $http;
687 | }
688 | getTodos() {
689 | return this.$http.get('/api/todos').then(response => response.data);
690 | }
691 | }
692 |
693 | /* ----- todo/todo.module.js ----- */
694 | import angular from 'angular';
695 | import { TodoComponent } from './todo.component';
696 | import { TodoService } from './todo.service';
697 | import './todo.scss';
698 |
699 | export const TodoModule = angular
700 | .module('todo', [])
701 | .component('todo', TodoComponent)
702 | .service('TodoService', TodoService)
703 | .name;
704 | ```
705 |
706 | **[Back to top](#table-of-contents)**
707 |
708 | # Styles
709 |
710 | Using [Webpack](https://webpack.github.io/) we can now use `import` statements on our `.scss` files in our `*.module.js` to let Webpack know to include that file in our stylesheet. Doing this lets us keep our components isolated for both functionality and style; it also aligns more closely to how stylesheets are declared for use in Angular. Doing this won't isolate our styles to just that component like it does with Angular; the styles will still be usable application wide but it is more manageable and makes our applications structure easier to reason about.
711 |
712 | If you have some variables or globally used styles like form input elements then these files should still be placed into the root `scss` folder. e.g. `scss/_forms.scss`. These global styles can then be `@imported` into your root module (`app.module.js`) stylesheet like you would normally do.
713 |
714 | **[Back to top](#table-of-contents)**
715 |
716 | # ES2015 and Tooling
717 |
718 | ##### ES2015
719 |
720 | * Use [Babel](https://babeljs.io/) to compile your ES2015+ code and any polyfills
721 | * Consider using [TypeScript](http://www.typescriptlang.org/) to make way for any Angular upgrades
722 |
723 | ##### Tooling
724 | * Use `ui-router` [latest alpha](https://github.com/angular-ui/ui-router) (see the Readme) if you want to support component-routing
725 | * Otherwise you're stuck with `template: ''` and no `bindings`/resolve mapping
726 | * Consider preloading templates into `$templateCache` with `angular-templates` or `ngtemplate-loader`
727 | * [Gulp version](https://www.npmjs.com/package/gulp-angular-templatecache)
728 | * [Grunt version](https://www.npmjs.com/package/grunt-angular-templates)
729 | * [Webpack version](https://github.com/WearyMonkey/ngtemplate-loader)
730 | * Consider using [Webpack](https://webpack.github.io/) for compiling your ES2015 code and styles
731 | * Use [ngAnnotate](https://github.com/olov/ng-annotate) to automatically annotate `$inject` properties
732 | * How to use [ngAnnotate with ES6](https://www.timroes.de/2015/07/29/using-ecmascript-6-es6-with-angularjs-1-x/#ng-annotate)
733 |
734 | **[Back to top](#table-of-contents)**
735 |
736 | # State management
737 |
738 | Consider using Redux with AngularJS 1.5 for data management.
739 |
740 | * [Angular Redux](https://github.com/angular-redux/ng-redux)
741 |
742 | **[Back to top](#table-of-contents)**
743 |
744 | # Resources
745 |
746 | * [Stateful and stateless components, the missing manual](https://toddmotto.com/stateful-stateless-components)
747 | * [Understanding the .component() method](https://toddmotto.com/exploring-the-angular-1-5-component-method/)
748 | * [Using "require" with $onInit](https://toddmotto.com/on-init-require-object-syntax-angular-component/)
749 | * [Understanding all the lifecycle hooks, $onInit, $onChanges, $postLink, $onDestroy](https://toddmotto.com/angular-1-5-lifecycle-hooks)
750 | * [Using "resolve" in routes](https://toddmotto.com/resolve-promises-in-angular-routes/)
751 | * [Redux and Angular state management](http://blog.rangle.io/managing-state-redux-angular/)
752 | * [Sample Application from Community](https://github.com/chihab/angular-styleguide-sample)
753 |
754 | **[Back to top](#table-of-contents)**
755 |
756 | # Documentation
757 | For anything else, including API reference, check the [AngularJS documentation](//docs.angularjs.org/api).
758 |
759 | # Contributing
760 |
761 | Open an issue first to discuss potential changes/additions. Please don't open issues for questions.
762 |
763 | ## License
764 |
765 | #### (The MIT License)
766 |
767 | Copyright (c) 2016-2018 Todd Motto
768 |
769 | Permission is hereby granted, free of charge, to any person obtaining
770 | a copy of this software and associated documentation files (the
771 | 'Software'), to deal in the Software without restriction, including
772 | without limitation the rights to use, copy, modify, merge, publish,
773 | distribute, sublicense, and/or sell copies of the Software, and to
774 | permit persons to whom the Software is furnished to do so, subject to
775 | the following conditions:
776 |
777 | The above copyright notice and this permission notice shall be
778 | included in all copies or substantial portions of the Software.
779 |
780 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
781 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
782 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
783 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
784 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
785 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
786 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
787 |
--------------------------------------------------------------------------------
/i18n/es.md:
--------------------------------------------------------------------------------
1 | # Guía de estilo AngularJS (ES2015)
2 |
3 | ### Arquitectura, estructura de archivos, componentes, one-way dataflow y buenas prácticas.
4 |
5 | *Una guía de estilos sensata par equipos, por [@toddmotto](//twitter.com/toddmotto)*
6 |
7 | Esta arquitectura y guía de estilo ha sido reescrita desde cero para ES2015, los cambios en AngularJS 1.5+ para la futura actualización de tu aplicación a Angular. Esta guía incluye nuevas buenas prácticas para flujos de datos en una dirección, delegación de eventos, arquitectura de componentes y enrutamiento de componentes.
8 |
9 | Puedes encontrar la vieja guía de estilo [aquí](https://github.com/toddmotto/angular-styleguide/tree/angular-old-es5), y el razonamiento detrás de la nueva [aquí](https://toddmotto.com/rewriting-angular-styleguide-angular-2).
10 |
11 | > Unete a la experiencia Ultimate de AngularJS y domina completamente características básicas y avanzadas de AngularJS para desarrollar aplicaciones del mundo real que son rápidas y escalables.
12 |
13 |
14 |
15 | ## Tabla de contenidos
16 |
17 | 1. [Arquitectura Modular](#arquitectura-modular)
18 | 1. [Teoría](#teoria-de-modulos)
19 | 1. [Root module](#root-module)
20 | 1. [Component module](#component-module)
21 | 1. [Common module](#common-module)
22 | 1. [Low-level modules](#low-level-modules)
23 | 1. [Conveciones de nombres de archivos](#convenciones-de-nombres-de-archivos)
24 | 1. [Estructura de archivos escalable](#estructura-de-archivos-escalable)
25 | 1. [Componentes](#componentes)
26 | 1. [Teoría](#teoría-del-component)
27 | 1. [Propiedades admitidas](#propiedades-admitidas)
28 | 1. [Controllers](#controllers)
29 | 1. [One-way dataflow y eventos](#one-way-dataflow-y-eventos)
30 | 1. [Stateful Components](#stateful-components)
31 | 1. [Stateless Components](#stateless-components)
32 | 1. [Routed Components](#routed-components)
33 | 1. [Directivas](#directivas)
34 | 1. [Teoría](#teoría-de-directiva)
35 | 1. [Propiedades recomendadas](#propiedades-recomendadas)
36 | 1. [Constantes o Clases](#constantes-o-clases)
37 | 1. [Servicios](#servicios)
38 | 1. [Teoría](#teoría-de-servicios)
39 | 1. [Clases para services](#clases-para-services)
40 | 1. [ES2015 y Herramientas](#es2015-y-herramientas)
41 | 1. [State managment](#state-managment)
42 | 1. [Recursos](#recursos)
43 | 1. [Documentación](#documentacion)
44 | 1. [Contribuyendo](#contribuyendo)
45 |
46 | # Arquitectura modular
47 |
48 | Cada modulo en una aplicación angular es un module component. Un module component es la definición principal de dicho modulo que encapsula la lógica, templates, enrutamiento y componentes hijo.
49 |
50 | ### Teoría
51 |
52 | El diseño de modulos se correlaciona directamente con nuestra estructura de directorios, lo que mantiene las cosas mantenibles y predecibles. Deberíamos tener idealmente tres modulos de alto nivel: `root`, `component` y `common`. El módulo `root` define la base que inicia nuestra aplicación, y la plantilla correspondiente. Después importamos nuestro componente y módulos comunes dentro del módulo `root` para incluir nuestras dependencias. El componente y `common modules` entonces requieren módulos de más bajo nivel, los cuales contienen nuestros componentes, controladores, servicios, directivas, filtros y pruebas para cada característica reutilizable.
53 |
54 | **[Volver arriba](#table-de-contenidos)**
55 |
56 | ### Root module
57 |
58 | Un `Root module`comienza con un `root component` que define el elemento base para toda la aplicación, con una salida de enrutamiento definida, el ejemplo se muestra el uso de `ui-view` desde `ui-router`.
59 |
60 | ```js
61 | // app.component.js
62 | const AppComponent = {
63 | template: `
64 |
65 | Hello world
66 |
67 |
68 |
69 |
70 |
73 | `
74 | };
75 |
76 | export default AppComponent;
77 | ```
78 |
79 | Un `Root module` es entonces creado, con el `AppComponent` importado y registrado con `.component('app', AppComponent)`. Nuevas importaciones para submodulos( componentes y common modules) son hechas para incluir todos los componentes relevantes para la aplicación.
80 |
81 | ```js
82 | // app.module.js
83 | import angular from 'angular';
84 | import uiRouter from 'angular-ui-router';
85 | import AppComponent from './app.component';
86 | import ComponentsModule from './components/components.module';
87 | import CommonModule from './common/common.module';
88 |
89 | const AppModule = angular
90 | .module('app', [
91 | ComponentsModule,
92 | CommonModule,
93 | uiRouter
94 | ])
95 | .component('app', AppComponent)
96 | .name;
97 |
98 | export default AppModule;
99 | ```
100 |
101 | **[Volver arriba](#table-de-contenidos)**
102 |
103 | ### Component module
104 |
105 | Un `Component module` es el contenedor referencia para todos los componentes reusables. Ve más arriba como importamos `Componentes` y los inyectamos dentro del `Root module`, esto nos da un lugar único para importar todos los componentes para la aplicación. Estos módulos que necesitas estan desconectados de los demás modulos y por lo tanto se pueden mover dentro de cualqueir otra aplicación con facilidad.
106 |
107 | ```js
108 | import angular from 'angular';
109 | import CalendarModule from './calendar/calendar.module';
110 | import EventsModule from './events/events.module';
111 |
112 | const ComponentsModule = angular
113 | .module('app.components', [
114 | CalendarModule,
115 | EventsModule
116 | ])
117 | .name;
118 |
119 | export default ComponentsModule;
120 | ```
121 |
122 | **[Volver arriba](#table-de-contenidos)**
123 |
124 | ### Common module
125 |
126 | El `Common module` es el contenedor referencia para todos los componentes específicos de la aplicación, que no queremos utilizar en ninguna otra aplicación. Estos pueden ser cosas como el layout, navegación y pie de página. Ve más arriba como importamos `CommonModule` y los inyectamos dentro del `Root module`, esto nos da un lugar único para importar todos los componentes comunes para la aplicación.
127 |
128 | ```js
129 | import angular from 'angular';
130 | import NavModule from './nav/nav.module';
131 | import FooterModule from './footer/footer.module';
132 |
133 | const CommonModule = angular
134 | .module('app.common', [
135 | NavModule,
136 | FooterModule
137 | ])
138 | .name;
139 |
140 | export default CommonModule;
141 | ```
142 |
143 | **[Volver arriba](#table-de-contenidos)**
144 |
145 | ### Low-level modules
146 |
147 | `Low-level modules` son componentes individuales que contienen la lógica para cada bloque de función. Cada una de ellas definirá un modulo, para ser importado a un modulo de un nivel más alto, como un componente o `common module`, hay un ejemplo abajo. Siempre recuerda agregar el sufijo `.name` a cada `export` cuando se crea un moduloe _nuevo_, y no cuando se haga referencia a uno. Notarás que existen definiciones de enrutamiento aquí, los cuales se verán en un captítulo posterior de está guía.
148 |
149 | ```js
150 | import angular from 'angular';
151 | import uiRouter from 'angular-ui-router';
152 | import CalendarComponent from './calendar.component';
153 |
154 | const CalendarModule = angular
155 | .module('calendar', [
156 | uiRouter
157 | ])
158 | .component('calendar', CalendarComponent)
159 | .config(($stateProvider, $urlRouterProvider) => {
160 | $stateProvider
161 | .state('calendar', {
162 | url: '/calendar',
163 | component: 'calendar'
164 | });
165 | $urlRouterProvider.otherwise('/');
166 | })
167 | .name;
168 |
169 | export default CalendarModule;
170 | ```
171 |
172 | **[Volver arriba](#table-de-contenidos)**
173 |
174 | ### Convenciones de nombres de archivos
175 |
176 | Mantenlo simple y en minúsculas, utiliza el nombre del componente, ejemplo `calendar.*.js*`, `calendar-grid.*.js` - con el nombre del tpo de archivo en el medio:
177 |
178 | ```
179 | calendar.module.js
180 | calendar.controller.js
181 | calendar.component.js
182 | calendar.service.js
183 | calendar.directive.js
184 | calendar.filter.js
185 | calendar.spec.js
186 | ```
187 |
188 | **[Volver arriba](#table-de-contenidos)**
189 |
190 | ### Estructura de archivos escalable
191 |
192 | La estructura de archivos es extremadamente importante, esto describe una estructura escalable y predecible. Un ejemplo de estructura de archivo para ilustrar una arquitectura de componentes modulares.
193 |
194 | ```
195 | ├── app/
196 | │ ├── components/
197 | │ │ ├── calendar/
198 | │ │ │ ├── calendar.module.js
199 | │ │ │ ├── calendar.controller.js
200 | │ │ │ ├── calendar.component.js
201 | │ │ │ ├── calendar.service.js
202 | │ │ │ ├── calendar.spec.js
203 | │ │ │ └── calendar-grid/
204 | │ │ │ ├── calendar-grid.module.js
205 | │ │ │ ├── calendar-grid.controller.js
206 | │ │ │ ├── calendar-grid.component.js
207 | │ │ │ ├── calendar-grid.directive.js
208 | │ │ │ ├── calendar-grid.filter.js
209 | │ │ │ └── calendar-grid.spec.js
210 | │ │ ├── events/
211 | │ │ │ ├── events.module.js
212 | │ │ │ ├── events.controller.js
213 | │ │ │ ├── events.component.js
214 | │ │ │ ├── events.directive.js
215 | │ │ │ ├── events.service.js
216 | │ │ │ ├── events.spec.js
217 | │ │ │ └── events-signup/
218 | │ │ │ ├── events-signup.module.js
219 | │ │ │ ├── events-signup.controller.js
220 | │ │ │ ├── events-signup.component.js
221 | │ │ │ ├── events-signup.service.js
222 | │ │ │ └── events-signup.spec.js
223 | │ │ └── components.module.js
224 | │ ├── common/
225 | │ │ ├── nav/
226 | │ │ │ ├── nav.module.js
227 | │ │ │ ├── nav.controller.js
228 | │ │ │ ├── nav.component.js
229 | │ │ │ ├── nav.service.js
230 | │ │ │ └── nav.spec.js
231 | │ │ ├── footer/
232 | │ │ │ ├── footer.module.js
233 | │ │ │ ├── footer.controller.js
234 | │ │ │ ├── footer.component.js
235 | │ │ │ ├── footer.service.js
236 | │ │ │ └── footer.spec.js
237 | │ │ └── common.module.js
238 | │ ├── app.module.js
239 | │ └── app.component.js
240 | └── index.html
241 | ```
242 |
243 | El nivel más alto de la estructura del directorio contiene simplemente `index.html` y `app/`, un directorio donde todos nuestro root, component, common y low-level modules viven.
244 |
245 | **[Volver arriba](#table-de-contenidos)**
246 |
247 | # Componentes
248 |
249 | ### Teoría
250 |
251 | Los componentes son esencialmente plantillas con un controlador. _No_ son directivas, ni debe sustituir las directivas con Componentes, a menos que estes actuliazando "template Directives" con controladores, que son los más adecuados para un componente. Los componentes tambien contienen bindings que definen los inputs y outputs de datos y eventos, lifecycle hooks y la habilidad de utilizar flujos de datos unidireccionales y eventos de objetos para obtener copias de seguridad de un componente padre. Estos son el nuevo estándar de facto en AngularJS 1.5 y superior. Cualquier template y controlador que creemos será un componente, que puede ser `stateful`, `stateless` o `routed component`. Se puede pensar en un "component" como una pieza completa de código, no solo la definción de objeto del `.component()`. Vamos a explorar algunas de las mejores prácticas y advertencias de los componentes y exploraremos la forma en que deben estructurarse a través de conceptos de componentes stateful, stateless y routed.
252 |
253 | **[Volver arriba](#table-de-contenidos)**
254 |
255 | ### Propiedades admitidas
256 |
257 | Estas son las propiedades adminitidas para `.component()` que tu puedes/deberías utilizar:
258 |
259 | | Property | Support |
260 | |---|---|
261 | | bindings | Sí, usa solo `'@'`, `'<'`, `'&'` |
262 | | controller | Sí |
263 | | controllerAs | Sí, el valor por defecto es `$ctrl` |
264 | | require | Sí (nueva sintaxis de objeto) |
265 | | template | Sí |
266 | | templateUrl | Sí |
267 | | transclude | Sí |
268 |
269 | **[Volver arriba](#table-de-contenidos)**
270 |
271 | ### Controllers
272 |
273 | Los controladores solo deben ser utilizados junto con los componentes, nunca en otro lugar. Si sientes que necesitas un controlador, lo que realmente necesitas es problablemente un `stateless component` para manejar esa pieza particular de comportamiento.
274 |
275 | Estas son algunas advertencias para usar `Class` en un controlador:
276 |
277 | * Siempre use el `constructor` para propósitos de inyección de dependencias.
278 | * No exportes la `clase`directamente, exporta su nombre para permitir anotaciones `$inject`
279 | * Si necesitas acceder al `lexical scope`, utilizar arrow functions
280 | * Una alternativa a las arrow functions, `let ctrl = this;` es también aceptable y puede tener más sentido según el caso de uso
281 | * Liga todas las funciones publicas directamente a la `Clase`
282 | * Haz uso del apropiado `lifecycle hook`, `$onInit`, `$onChanges`, `$postLink` y `$onDestroy`
283 | * Nota: `$onChanges` es llamado antes de `$onInit`, ve la sección de [recursos](#resources) para encontrar artículos que detallan esto en más profundidad.
284 | * Utiliza `require` junto con `$onInit` para referencia una lógica heredada
285 | * No sobreescribas el alias default `$ctrl` para la sintaxis `controllerAs`, por lo tanto no uses `controllerAs` en cualquier sitio.
286 |
287 | **[Volver arriba](#table-de-contenidos)**
288 |
289 | ### One-way dataflow y Eventos
290 |
291 | One-way dataflow fue introducido en AngularJS 1.5, y redefine la comunicación de componentes.
292 |
293 | Estas son algunas advertencias para usar one-way dataflow:
294 |
295 | * En componentes que reciben datos, siempre utiliza la sintaxis one-way databindings `'<'`
296 | * _No utilices_ `'='` la sintaxis two-way databiding nunca más, en nigún sitio
297 | * Los componentes que tengan `bindings` deben itilizar `$onChanges` para clonar el one-way binding data para romper los objetos que para por referencia y actualizar la información de los padres.
298 | * Utiliza `$event` como un argumento de la función en el método padre (mirá el ejemplo stateful abajo `$ctrl.addTodo($event)`)
299 | * Pasa un objeto de respaldo `$event: {}` de un stateless component (mirá el ejemplo stateless abajo `this.onAddTodo`)
300 | * Bonus: Utiliza una wrapper `EventEmitter` con `.value()` para reflejar Angular,evita la creación manual de objetos `$event`.
301 | * ¿Por qué? Este refleja Angular y mantiene la consistencia dentro de cada componente. Si no que también hace un estado predecible.
302 |
303 | **[Volver arriba](#table-de-contenidos)**
304 |
305 | ### Stateful components
306 |
307 | Vamos a definir a lo que llamaríamos un "stateful component".
308 |
309 | * Obtiene el estado, esencialmente comunicando aun API de backend a través de un servicio
310 | * No muta directamente de estado
311 | * Renderea componentes hijo que mutan de estado
312 | * También se denominan como un componente inteligente/contenedor
313 |
314 | Un ejemplo de stateful component, completa con su definición de modulo low-level (esto es sólo para demostración, así que algún código ha sido omitido por razones de brevedad):
315 |
316 | ```js
317 | /* ----- todo/todo.component.js ----- */
318 | import controller from './todo.controller';
319 |
320 | const TodoComponent = {
321 | controller,
322 | template: `
323 |
324 |
327 |
329 |
330 | `
331 | };
332 |
333 | export default TodoComponent;
334 |
335 | /* ----- todo/todo.controller.js ----- */
336 | class TodoController {
337 | constructor(TodoService) {
338 | this.todoService = TodoService;
339 | }
340 | $onInit() {
341 | this.newTodo = {
342 | title: '',
343 | selected: false
344 | };
345 | this.todos = [];
346 | this.todoService.getTodos().then(response => this.todos = response);
347 | }
348 | addTodo({ todo }) {
349 | if (!todo) return;
350 | this.todos.unshift(todo);
351 | this.newTodo = {
352 | title: '',
353 | selected: false
354 | };
355 | }
356 | }
357 |
358 | TodoController.$inject = ['TodoService'];
359 |
360 | export default TodoController;
361 |
362 | /* ----- todo/todo.module.js ----- */
363 | import angular from 'angular';
364 | import TodoComponent from './todo.component';
365 |
366 | const TodoModule = angular
367 | .module('todo', [])
368 | .component('todo', TodoComponent)
369 | .name;
370 |
371 | export default TodoModule;
372 | ```
373 | Este ejemplo muestra un stateful component, que obtiene el estado dentro del controlador, a través de un servicios, y a continuación pasa hacia los componentes hijo de tipo stateless. Nota como no hay directivas siendo utilizadas como `ng-repeat` y relacionadas dentro del template. En cambio, los datos y funcionar son delegadas dentro de los stateless components `` y ``.
374 |
375 | **[Volver arriba](#table-de-contenidos)**
376 |
377 | ### Stateless components
378 |
379 | Vamos a definir a lo que llamaríamos un "stateless component".
380 |
381 | * Tiene inputs y outputs definidas utilizando `bindings: {}`
382 | * Los datos ingresan al componente a través de atributos bindings (inputs)
383 | * Los datos abandonan el componente a través de eventos (outputs)
384 | * Mutación de estados, pasa a través del respaldo bajo demanda (como un click o envíando un evento)
385 | * No le importa de donde vienen los datos, son stateless
386 | * Son componentes altamente reusables
387 | * También se denominan como un componente dumb/presentacional
388 |
389 | Un ejemplo de stateless component (usemos `` como un ejemplo), completa con su definición de modulo low-level (esto es sólo para demostración, así que algún código ha sido omitido por razones de brevedad):
390 |
391 | ```js
392 | /* ----- todo/todo-form/todo-form.component.js ----- */
393 | import controller from './todo-form.controller';
394 |
395 | const TodoFormComponent = {
396 | bindings: {
397 | todo: '<',
398 | onAddTodo: '&'
399 | },
400 | controller,
401 | template: `
402 |
406 | `
407 | };
408 |
409 | export default TodoFormComponent;
410 |
411 | /* ----- todo/todo-form/todo-form.controller.js ----- */
412 | class TodoFormController {
413 | constructor(EventEmitter) {
414 | this.EventEmitter = EventEmitter;
415 | }
416 | $onChanges(changes) {
417 | if (changes.todo) {
418 | this.todo = Object.assign({}, this.todo);
419 | }
420 | }
421 | onSubmit() {
422 | if (!this.todo.title) return;
423 | // with EventEmitter wrapper
424 | this.onAddTodo(
425 | this.EventEmitter({
426 | newTodo: this.todo
427 | })
428 | );
429 | // without EventEmitter wrapper
430 | this.onAddTodo({
431 | $event: {
432 | newTodo: this.todo
433 | }
434 | });
435 | }
436 | }
437 |
438 | TodoFormController.$inject = ['EventEmitter'];
439 |
440 | export default TodoFormController;
441 |
442 | /* ----- todo/todo-form/todo-form.module.js ----- */
443 | import angular from 'angular';
444 | import TodoFormComponent from './todo-form.component';
445 |
446 | const TodoFormModule = angular
447 | .module('todo.form', [])
448 | .component('todoForm', TodoFormComponent)
449 | .value('EventEmitter', payload => ({ $event: payload}))
450 | .name;
451 |
452 | export default TodoFormModule;
453 | ```
454 |
455 | Nota como el componente `` no obtiene ningún estado, simplemente lo recibe, muta un objeto a través de la logica del controlador asociada a él, y lo envía de regreso al componente padre a través de la propiedad bindings. En este ejemplo, el lifecycle hook `$onChanges` crear un clon del objeto binding inicial `this.todo` y lo reasigna, lo que significa que la información padre no es afectada hasta que se envía el formulario, junto a la nueva sintaxis one-way data flow `'<'`.
456 |
457 | **[Volver arriba](#table-de-contenidos)**
458 |
459 | ### Routed components
460 |
461 | Vamos a definir a lo que llamaríamos un "routed component".
462 |
463 | * Es esencialmente un componente stateful, con definición de rutas
464 | * No más archivos `router.js`
465 | * Utilizamos componentes routed para definir su propia lógica de ruteo
466 | * Los datos de "ingreso" para el componente son realizados a través del route resolve(opcional, todavía disponible en el controlador a través de llamadas del service)
467 |
468 | Para este ejemplo, vamos a tomar el componente existente ``, refactorizarlo para utilizar la definición route y `bindings` en el componente que recibe los datos (el secreto aquí con `ui-router` es la propiedad `resolve` que creamos, en este caso `todoData` mapea directamente a través de los `bindings` por nosotros). Lo tratamos como un routed component por que es esencialmente una "vista":
469 |
470 | ```js
471 | /* ----- todo/todo.component.js ----- */
472 | import controller from './todo.controller';
473 |
474 | const TodoComponent = {
475 | bindings: {
476 | todoData: '<'
477 | },
478 | controller,
479 | template: `
480 |
481 |
484 |
486 |
487 | `
488 | };
489 |
490 | export default TodoComponent;
491 |
492 | /* ----- todo/todo.controller.js ----- */
493 | class TodoController {
494 | constructor() {}
495 | $onInit() {
496 | this.newTodo = {
497 | title: '',
498 | selected: false
499 | };
500 | }
501 | $onChanges(changes) {
502 | if (changes.todoData) {
503 | this.todos = Object.assign({}, this.todoData);
504 | }
505 | }
506 | addTodo({ todo }) {
507 | if (!todo) return;
508 | this.todos.unshift(todo);
509 | this.newTodo = {
510 | title: '',
511 | selected: false
512 | };
513 | }
514 | }
515 |
516 | export default TodoController;
517 |
518 | /* ----- todo/todo.module.js ----- */
519 | import angular from 'angular';
520 | import TodoComponent from './todo.component';
521 |
522 | const TodoModule = angular
523 | .module('todo', [])
524 | .component('todo', TodoComponent)
525 | .service('TodoService', TodoService)
526 | .config(($stateProvider, $urlRouterProvider) => {
527 | $stateProvider
528 | .state('todos', {
529 | url: '/todos',
530 | component: 'todo',
531 | resolve: {
532 | todoData: PeopleService => PeopleService.getAllPeople()
533 | }
534 | });
535 | $urlRouterProvider.otherwise('/');
536 | })
537 | .name;
538 |
539 | export default TodoModule;
540 | ```
541 |
542 | **[Volver arriba](#table-de-contenidos)**
543 |
544 | # Directivas
545 |
546 | ### Teoría
547 |
548 | Las directivas nos da `template`, `scope` bindings, `bindToController`, `link` y muchas otras cosas. El uso de estas debe ser cuidadosamente considerando ahora la existencia de `.component()`. Las directivas no deben declarar templates y controladores nunca más, o recibir información a través de bindings. Las directivas deben ser utilizadas solamente para decoración del DOM. Por esto, si necesitas eventos/APIS personalizadas y lógica, usa una directiva y ligalo al template dentro de un componente. Si necesitas una cantidad importante de manipulación del DOM, también está el lifecycle hoook `$postLink` para ser considerado, de cualquier manera este no es el lugar para migrar toda tu manipulación del DOM, usa una directiva si es posible para cosas no-Angular.
549 |
550 | Estas son algunas de las advertencias para el uso de Directivas:
551 |
552 | * Nunca utilizar templates, scope, bindToController o controladores
553 | * Siempre usar `restrict: 'A'` en directivas
554 | * Utiliza compile y link cuando sea necesario
555 | * Recuerda destruir y desvincular event handlers dentro `$scope.$on('$destroy', fn);`
556 |
557 | **[Volver arriba](#table-de-contenidos)**
558 |
559 | ### Propiedades recomendades
560 |
561 | Debido al hecho de que las directivas soportan más que lo hace `.component()` (template directives fueron el componente original), yo recomiendo limitar la definiciones de objeto de la directiva a solo estas propiedades, para evitar utilizar directivas incorrectamente:
562 |
563 |
564 | | Propiedad | Usarlo? | Razón |
565 | |---|---|---|
566 | | bindToController | No | Utiliza `bindings` en componentes |
567 | | compile | Sí | Para pre-compilar manipulación/eventos DOM|
568 | | controller | No | Utiliza un componente |
569 | | controllerAs | No | Utiliza un componente |
570 | | link functions | Sí | Para pre/post manipulación/eventos DOM |
571 | | multiElement | Sí | [Ver documentación](https://docs.angularjs.org/api/ng/service/$compile#-multielement-) |
572 | | priority | Sí | [Ver documentación](https://docs.angularjs.org/api/ng/service/$compile#-priority-) |
573 | | require | No | Utiliza un componente |
574 | | restrict | Sí | Define el uso de la directive, utiliza siempre `'A'` |
575 | | scope | No | Utiliza un componente |
576 | | template | No | Utiliza un componente |
577 | | templateNamespace | Sí (si es necesario) | [Ver documentación](https://docs.angularjs.org/api/ng/service/$compile#-templatenamespace-) |
578 | | templateUrl | No | Utiliza un componente |
579 | | transclude | No | Utiliza un componente |
580 |
581 | **[Volver arriba](#table-de-contenidos)**
582 |
583 | ### Constantes o Clases
584 |
585 | Hay algunas maneras de abordar el uso de ES2015 y directivas, ya sea con una `arrow function` y la asignación más sencilla, o utilizando una `Clase` de ES2015. Selecciona lo mejor que sea para ti y tu equipo, manten en mente que Angular utiliza clases.
586 |
587 | Aquí hay un ejemplo utilizando una constante con una `Arrow function` y `expression wrapper`, `() => ({})` regresa un Objeto, (toma en cuenta las diferencias de uso en el interior de `.directive()` ):
588 |
589 | ```js
590 | /* ----- todo/todo-autofocus.directive.js ----- */
591 | import angular from 'angular';
592 |
593 | const TodoAutoFocus = ($timeout) => ({
594 | restrict: 'A',
595 | link($scope, $element, $attrs) {
596 | $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => {
597 | if (!newValue) {
598 | return;
599 | }
600 | $timeout(() => $element[0].focus());
601 | });
602 | }
603 | });
604 |
605 | TodoAutoFocus.$inject = ['$timeout'];
606 |
607 | export default TodoAutoFocus;
608 |
609 | /* ----- todo/todo.module.js ----- */
610 | import angular from 'angular';
611 | import TodoComponent from './todo.component';
612 | import TodoAutofocus from './todo-autofocus.directive';
613 |
614 | const TodoModule = angular
615 | .module('todo', [])
616 | .component('todo', TodoComponent)
617 | .directive('todoAutofocus', TodoAutoFocus)
618 | .name;
619 |
620 | export default TodoModule;
621 | ```
622 |
623 | O utilizando una clases ES2015 (toma en cuenta la llamada manual de `new TodoAutoFocus` cuando se registra la directiva) para crear el objeto:
624 |
625 | ```js
626 | /* ----- todo/todo-autofocus.directive.js ----- */
627 | import angular from 'angular';
628 |
629 | class TodoAutoFocus {
630 | constructor($timeout) {
631 | this.restrict = 'A';
632 | this.$timeout = $timeout;
633 | }
634 | link($scope, $element, $attrs) {
635 | $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => {
636 | if (!newValue) {
637 | return;
638 | }
639 | this.$timeout(() => $element[0].focus());
640 | });
641 | }
642 | }
643 |
644 | TodoAutoFocus.$inject = ['$timeout'];
645 |
646 | export default TodoAutoFocus;
647 |
648 | /* ----- todo/todo.module.js ----- */
649 | import angular from 'angular';
650 | import TodoComponent from './todo.component';
651 | import TodoAutofocus from './todo-autofocus.directive';
652 |
653 | const TodoModule = angular
654 | .module('todo', [])
655 | .component('todo', TodoComponent)
656 | .directive('todoAutofocus', ($timeout) => new TodoAutoFocus($timeout))
657 | .name;
658 |
659 | export default TodoModule;
660 | ```
661 |
662 | **[Volver arriba](#table-de-contenidos)**
663 |
664 | # Servicios
665 |
666 | ### Teoría
667 |
668 | Los servicios son esencialmente contenedores para la lógica de negocio que nuestros componentes no deben solicitar directamente. Los servicios contienen otros servicios incorporados o externos como lo es `$http`, que podemos inyectar dentro de los controladores de nuestro componente en otra parte de nuestra aplicación. Tenemos dos maneras de hacer servicios, utilizando `.service()` o `.factory()`. Con las `Clases` ES2015, solo debemos utilizar `.services()`, completa la anotación de inyección de dependiencias con `$inject`.
669 |
670 | **[Volver arriba](#table-de-contenidos)**
671 |
672 | ### Clases para Services
673 |
674 | Aquí está un ejemplo de implementación para nuestro aplicación de `todo` utilizando una `Clase` ES2015:
675 |
676 | ```js
677 | /* ----- todo/todo.service.js ----- */
678 | class TodoService {
679 | constructor($http) {
680 | this.$http = $http;
681 | }
682 | getTodos() {
683 | return this.$http.get('/api/todos').then(response => response.data);
684 | }
685 | }
686 |
687 | TodoService.$inject = ['$http'];
688 |
689 | export default TodoService;
690 |
691 | /* ----- todo/todo.module.js ----- */
692 | import angular from 'angular';
693 | import TodoComponent from './todo.component';
694 | import TodoService from './todo.service';
695 |
696 | const TodoModule = angular
697 | .module('todo', [])
698 | .component('todo', TodoComponent)
699 | .service('TodoService', TodoService)
700 | .name;
701 |
702 | export default TodoModule;
703 | ```
704 |
705 | **[Volver arriba](#table-de-contenidos)**
706 |
707 | # ES2015 y Herramientas
708 |
709 | ##### ES2015
710 |
711 | * Utiliza [Babel](https://babeljs.io/) para compilar tu código ES2015+ code y cualquier polyfills
712 | * Considera utilizar [TypeScript](http://www.typescriptlang.org/) para dar paso a cualquier actualización de Angular
713 |
714 | ##### Herramientas
715 | * Utiliza `ui-router` [latest alpha](https://github.com/angular-ui/ui-router) (ve el Readme) si tu quiere soporte de component-routing
716 | * De otra manera estarás atado a `template: ''` y no `bindings`
717 | * Considera utilizar [Webpack](https://webpack.github.io/) para compilar tu código ES2015
718 | * Utiliza [ngAnnotate](https://github.com/olov/ng-annotate) para anotar automáticamente propiedades en el `$inject`
719 |
720 | **[Volver arriba](#table-de-contenidos)**
721 |
722 | # State management
723 |
724 | Considera el uso de Redux con AngularJS 1.5 para la gestión de datos.
725 |
726 | * [Angular Redux](https://github.com/angular-redux/ng-redux)
727 |
728 | **[Volver arriba](#table-de-contenidos)**
729 |
730 | # Recursos
731 |
732 | * [Comprendiendo el método .component()](https://toddmotto.com/exploring-the-angular-1-5-component-method/)
733 | * [Utilizando "require" con $onInit](https://toddmotto.com/on-init-require-object-syntax-angular-component/)
734 | * [Comprendiendo todo el lifecycle hooks, $onInit, $onChanges, $postLink, $onDestroy](https://toddmotto.com/angular-1-5-lifecycle-hooks)
735 | * [Utilizando "resolve" en routes](https://toddmotto.com/resolve-promises-in-angular-routes/)
736 | * [Redux y Angular state management](http://blog.rangle.io/managing-state-redux-angular/)
737 | * [Sample Application from Community](https://github.com/chihab/angular-styleguide-sample)
738 |
739 | **[Volver arriba](#table-de-contenidos)**
740 |
741 | # Documentación
742 | Para algo más, incluyendo referencia al API, revisa la [documentación de AngularJS](//docs.angularjs.org/api).
743 |
744 | # Contribuyendo
745 |
746 | Abre un `issue` primero para discutir posibles cambios/adiciones. Por favor no abras `issues` para preguntas.
747 |
748 |
749 | ## License
750 |
751 | #### (The MIT License)
752 |
753 | Copyright (c) 2016 Todd Motto
754 |
755 | Permission is hereby granted, free of charge, to any person obtaining
756 | a copy of this software and associated documentation files (the
757 | 'Software'), to deal in the Software without restriction, including
758 | without limitation the rights to use, copy, modify, merge, publish,
759 | distribute, sublicense, and/or sell copies of the Software, and to
760 | permit persons to whom the Software is furnished to do so, subject to
761 | the following conditions:
762 |
763 | The above copyright notice and this permission notice shall be
764 | included in all copies or substantial portions of the Software.
765 |
766 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
767 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
768 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
769 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
770 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
771 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
772 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
773 |
--------------------------------------------------------------------------------
/i18n/id.md:
--------------------------------------------------------------------------------
1 | # Panduan Gaya AngularJS (ES2015)
2 |
3 | ### Arsitektur, struktur berkas, komponen, aliran data satu-arah dan contoh-contoh praktis
4 |
5 | *Panduan gaya yang mudah dipahami untuk tim oleh [@toddmotto](//twitter.com/toddmotto)*
6 |
7 | Arsitektur dan panduan gaya ini telah ditulis kembali secara lengkap untuk ES2015, perubahan-perubahan yang ada di AngularJS 1.5+ untuk Anda yang ingin melakukan upgrade aplikasi ke Angular nantinya. Panduan ini menyertakan contoh-contoh praktis aliran data satu-arah, delegasi event, arsitektur komponen dan ruting komponen.
8 |
9 | Anda dapat temukan panduan gaya yang lama [disini](https://github.com/toddmotto/angular-styleguide/tree/angular-old-es5), dan latar belakang yang baru [disini](https://toddmotto.com/rewriting-angular-styleguide-angular-2).
10 |
11 | > Bergabung bersama pengalaman belajar Ultimate AngularJS untuk menguasai sepenuhnya fitur-fitur pemula dan lanjutan Angular untuk membangun aplikasi nyata yang cepat, dan berskala.
12 |
13 |
14 |
15 | ## Daftar Isi
16 |
17 | 1. [Arsitektur Modular](#arsitektur-modular)
18 | 1. [Teori](#teori-modul)
19 | 1. [Modul Pangkal](#modul-pangkal)
20 | 1. [Modul Komponen](#modul-komponen)
21 | 1. [Modul Umum](#modul-umum)
22 | 1. [Modul Tingkat-Rendah](#modul-tingkat-rendah)
23 | 1. [Struktur Berkas Berskala](#struktur-berkas-berskala)
24 | 1. [Konvensi Penamaan Berkas](#konvensi-penamaan-berkas)
25 | 1. [Komponen](#komponen)
26 | 1. [Teori](#teori-komponen)
27 | 1. [Properti yang Didukung](#properti-yang-didukung)
28 | 1. [Pengendali](#pengendali)
29 | 1. [Aliran Data Satu-arah dan Event](#aliran-data-satu-arah-dan-event)
30 | 1. [Komponen Berkondisi](#komponen-berkondisi)
31 | 1. [Komponen Tanpa Kondisi](#komponen-tanpa-kondisi)
32 | 1. [Komponen yang memiliki Rute](#komponen-yang-memiliki-rute)
33 | 1. [Direktif](#direktif)
34 | 1. [Teori](#teori-direktif)
35 | 1. [Properti yang Disarankan](#properti-yang-disarankan)
36 | 1. [Konstanta atau Kelas](#konstanta-atau-kelas)
37 | 1. [Servis](#servis)
38 | 1. [Teori](#teori-servis)
39 | 1. [Kelas untuk Servis](#kelas-untuk-servis)
40 | 1. [ES2015 dan Perangkat](#es2015-dan-perangkat)
41 | 1. [Pengelolaan kondisi](#pengelolaan-kondisi)
42 | 1. [Sumber](#sumber)
43 | 1. [Dokumentasi](#dokumentasi)
44 | 1. [Berkontribusi](#berkontribusi)
45 |
46 | # Arsitektur modular
47 |
48 | Setiap modul yang ada di dalam aplikasi Angular adalah sebuah komponen modul. Sebuah komponen modul adalah definisi pangkal untuk modul itu sendiri yang akan merangkum logika, templat, rute, dan komponen anakan.
49 |
50 | ### Teori modul
51 |
52 | Rancangan modul dipetakan secara langsung ke dalam struktur folder kita, untuk menjaga agar semuanya dapat tetap terpelihara dan dapat diprediksi. Idealnya kita harus memiliki tiga modul tingkat-tertinggi: pangkal, komponen, dan umum. Modul pangkal menentukan modul dasar yang mem-bootstrap aplikasi kita, serta templat yang berhubungan dengannya. Kemudian kita mengimpor modul-modul komponen dan umum ke dalam modul pangkal untuk menyertakan ketergantungan (dependensi) kita. Modul-modul komponen dan umum kemudian memerlukan modul-modul komponen tingkat-yang-lebih-rendah, yang mengandung komponen-komponen kita, seperti pengendali, servis, direktif, filter, dan pengujian, untuk masing-masing fitur yang dapat dipakai-ulang.
53 |
54 | **[Kembali ke atas](#daftar-isi)**
55 |
56 | ### Modul pangkal
57 |
58 | Sebuah modul Pangkal dimulai dengan komponen pangkal yang menentukan unsur dasar dari keseluruhan aplikasi, bersama sebuah rute keluaran yang sudah ditentukan, contohnya dengan menggunakan `ui-view` dari `ui-router`.
59 |
60 | ```js
61 | // app.component.js
62 | const AppComponent = {
63 | template: `
64 |
65 | Hello world
66 |
67 |
68 |
69 |
70 |
73 | `
74 | };
75 |
76 | export default AppComponent;
77 | ```
78 |
79 | Sebuah modul Pangkal kemudian dibuat, dengan `AppComponent` yang diimpor dan didaftarkan dengan `.component('app', AppComponent)`. Pengimporan submodul berikutnya (modul-modul komponen dan umum) dibuat untuk menyertakan semua komponen yang berhubungan dengan aplikasinya.
80 |
81 | ```js
82 | // app.module.js
83 | import angular from 'angular';
84 | import uiRouter from 'angular-ui-router';
85 | import AppComponent from './app.component';
86 | import ComponentsModule from './components/components.module';
87 | import CommonModule from './common/common.module';
88 |
89 | const AppModule = angular
90 | .module('app', [
91 | ComponentsModule,
92 | CommonModule,
93 | uiRouter
94 | ])
95 | .component('app', AppComponent)
96 | .name;
97 |
98 | export default AppModule;
99 | ```
100 |
101 | **[Kembali ke atas](#daftar-isi)**
102 |
103 | ### Modul komponen
104 |
105 | Sebuah modul Komponen adalah suatu referensi kontainer untuk semua komponen yang dapat dipakai-ulang. Lihat diatas bagaimana kita mengimpor `ComponentsModule` dan menginjeksinya ke dalam modul Pangkal, ini memberikan kita satu tempat tunggal untuk mengimpor semua komponen aplikasi. Modul-modul yang kita butuhkan ini dipisahkan dari semua modul lainnya sehingga kita dapat memindahkannya ke aplikasi lain dengan mudah.
106 |
107 | ```js
108 | import angular from 'angular';
109 | import CalendarModule from './calendar/calendar.module';
110 | import EventsModule from './events/events.module';
111 |
112 | const ComponentsModule = angular
113 | .module('app.components', [
114 | CalendarModule,
115 | EventsModule
116 | ])
117 | .name;
118 |
119 | export default ComponentsModule;
120 | ```
121 |
122 | **[Kembali ke atas](#daftar-isi)**
123 |
124 | ### Modul umum
125 |
126 | Modul Umum adalah refensi kontainer untuk semua komponen-komponen aplikasi yang spesifik, yang tidak ingin kita gunakan di aplikasi yang lain. Seperti tata letak, navigasi, dan kaki halaman. Lihat diatas bagaimana kita mengimpor `CommonModule` dan menginjeksinya ke dalam modul Pangkal, ini memberikan kita satu tempat tunggal untuk mengimpor semua komponen umum untuk aplikasi tersebut.
127 |
128 | ```js
129 | import angular from 'angular';
130 | import NavModule from './nav/nav.module';
131 | import FooterModule from './footer/footer.module';
132 |
133 | const CommonModule = angular
134 | .module('app.common', [
135 | NavModule,
136 | FooterModule
137 | ])
138 | .name;
139 |
140 | export default CommonModule;
141 | ```
142 |
143 | **[Kembali ke atas](#daftar-isi)**
144 |
145 | ### Modul tingkat-rendah
146 |
147 | Modul-modul Tingkat-Rendah adalah modul-modul komponen individual yang mengandung logika untuk setiap blok fitur. Masing-masingnya akan menetapkan sebuah modul, yang akan diimpor ke sebuah modul tingkat-yang-lebih-tinggi, seperti sebuah modul komponen atau umum, lihat contoh dibawah ini. Ingatlah untuk selalu menambahkan akhiran `.name` untuk setiap `export` ketika membuat sebuah modul _baru_, bukan pada saat mereferensikannya. Anda akan lihat penetapan ruting hadir disini, kita akan gali lebih dalam di bab berikut panduan ini.
148 |
149 | ```js
150 | import angular from 'angular';
151 | import uiRouter from 'angular-ui-router';
152 | import CalendarComponent from './calendar.component';
153 |
154 | const CalendarModule = angular
155 | .module('calendar', [
156 | uiRouter
157 | ])
158 | .component('calendar', CalendarComponent)
159 | .config(($stateProvider, $urlRouterProvider) => {
160 | $stateProvider
161 | .state('calendar', {
162 | url: '/calendar',
163 | component: 'calendar'
164 | });
165 | $urlRouterProvider.otherwise('/');
166 | })
167 | .name;
168 |
169 | export default CalendarModule;
170 | ```
171 |
172 | **[Kembali ke atas](#daftar-isi)**
173 |
174 | # Konvensi penamaan berkas
175 |
176 | Buatlah sesederhana mungkin dan berhuruf kecil, gunakan nama komponen, misalnya `calendar.*.js*`, `calendar-grid.*.js` - dengan nama jenis berkasnya di tengah. Gunakan `*.module.js` untuk berkas penetapan modulnya, sehingga Anda dapat mengimpor modul tersebut berdasarkan nama direktori.
177 |
178 | ```
179 | calendar.module.js
180 | calendar.controller.js
181 | calendar.component.js
182 | calendar.service.js
183 | calendar.directive.js
184 | calendar.filter.js
185 | calendar.spec.js
186 | ```
187 |
188 | **[Kembali ke atas](#daftar-isi)**
189 |
190 | ### Struktur berkas berskala
191 |
192 | Struktur berkas adalah sangat penting, ini menjelaskan sebuah sebuah struktur yang berskala dan dapat diprediksi. Sebuah contoh struktur berkas untuk menggambarkan sebuah arsitektur komponen yang modular.
193 |
194 | ```
195 | ├── app/
196 | │ ├── components/
197 | │ │ ├── calendar/
198 | │ │ │ ├── calendar.module.js
199 | │ │ │ ├── calendar.controller.js
200 | │ │ │ ├── calendar.component.js
201 | │ │ │ ├── calendar.service.js
202 | │ │ │ ├── calendar.spec.js
203 | │ │ │ └── calendar-grid/
204 | │ │ │ ├── calendar-grid.module.js
205 | │ │ │ ├── calendar-grid.controller.js
206 | │ │ │ ├── calendar-grid.component.js
207 | │ │ │ ├── calendar-grid.directive.js
208 | │ │ │ ├── calendar-grid.filter.js
209 | │ │ │ └── calendar-grid.spec.js
210 | │ │ ├── events/
211 | │ │ │ ├── events.module.js
212 | │ │ │ ├── events.controller.js
213 | │ │ │ ├── events.component.js
214 | │ │ │ ├── events.directive.js
215 | │ │ │ ├── events.service.js
216 | │ │ │ ├── events.spec.js
217 | │ │ │ └── events-signup/
218 | │ │ │ ├── events-signup.module.js
219 | │ │ │ ├── events-signup.controller.js
220 | │ │ │ ├── events-signup.component.js
221 | │ │ │ ├── events-signup.service.js
222 | │ │ │ └── events-signup.spec.js
223 | │ │ └── components.module.js
224 | │ ├── common/
225 | │ │ ├── nav/
226 | │ │ │ ├── nav.module.js
227 | │ │ │ ├── nav.controller.js
228 | │ │ │ ├── nav.component.js
229 | │ │ │ ├── nav.service.js
230 | │ │ │ └── nav.spec.js
231 | │ │ ├── footer/
232 | │ │ │ ├── footer.module.js
233 | │ │ │ ├── footer.controller.js
234 | │ │ │ ├── footer.component.js
235 | │ │ │ ├── footer.service.js
236 | │ │ │ └── footer.spec.js
237 | │ │ └── common.module.js
238 | │ ├── app.module.js
239 | │ └── app.component.js
240 | └── index.html
241 | ```
242 |
243 | Tingkat yang paling tinggi dari struktur folder hanya mengandung `index.html` dan `app/`, sebuah direktori dimana semua modul pangkal, komponen, umum dan tingkat-rendah kita berada.
244 |
245 | **[Kembali ke atas](#daftar-isi)**
246 |
247 | # Komponen
248 |
249 | ### Teori komponen
250 |
251 | Komponen pada dasarnya adalah templat dengan sebuah pengendali. Mereka _bukan_ Direktif, dan Anda juga seharusnya tidak menggantikan Direktif dengan Komponen, kecuali bila Anda mengupgrade "Direktif templat" dengan pengendali, yang sangat cocok sebagai sebuah komponen. Komponen juga mengandung ikatan yang menentukan input dan output untuk data dan event, kaitan siklus, dan kemampuan untuk menggunakan aliran data satu-arah dan Obyek event untuk mencadangkan data ke sebuah komponen induk. Ini adalah standar defacto yang baru di AngularJS 1.5 dan versi berikutnya. Semua yang didorong oleh templat dan pengendali yang kita buat akan mirip seperti sebuah komponen, yang mungkin saja berupa komponen berkondisi, tanpa-kondisi, atau memiliki rute. Anda bisa bayangkan "komponen" itu seperti sebuah potongan kode yang lengkap, bukan hanya sekedar Obyek penetapan `.component()` saja. Mari kita telusuri beberapa contoh praktis dan saran-saran tentang komponen, untuk selanjutnya menyelam lebih dalam tentang bagaimana seharusnya Anda menyusunnya melalui konsep-konsep komponen berkondisi, tanpa-kondisi dan memiliki rute.
252 |
253 | **[Kembali ke atas](#daftar-isi)**
254 |
255 | ### Properti yang didukung
256 |
257 | Berikut adalah properti yang didukung untuk `.component()` yang dapat/harus Anda gunakan:
258 |
259 | | Properti | Dukungan |
260 | |---|---|
261 | | bindings | Ya, gunakan `'@'`, `'<'`, `'&'` saja |
262 | | controller | Ya |
263 | | controllerAs | Ya, standarnya adalah `$ctrl` |
264 | | require | Ya (sintak Obyek baru) |
265 | | template | Ya |
266 | | templateUrl | Ya |
267 | | transclude | Ya |
268 |
269 | **[Kembali ke atas](#daftar-isi)**
270 |
271 | ### Pengendali
272 |
273 | Pengendali harus digunakan bersama komponen, jangan pernah di tempat lain. Bila rasanya Anda memerlukan sebuah pengendali, maka apa yang sebenarnya Anda perlukan untuk mengelola perilaku tersebut adalah sebuah komponen tanpa-kondisi.
274 |
275 | Berikut adalah beberapa saran tentang pemakaian `Class` untuk pengendali:
276 |
277 | * Selalu gunakan `constructor` untuk tujuan-tujuan injeksi dependensi
278 | * Tidak mengekspor `Class` secara langsung, ekspor namanya untuk menerangkan `$inject`
279 | * Bila Anda perlu mengakses lingkup bahasa, gunakan fungsi panah
280 | * Alternatif lain fungsi panah, `let ctrl = this;` juga dapat diterima dan mungkin lebih masuk akal tergantung kasusnya
281 | * Balut semua fungsi publik langsung ke dalam `Class`
282 | * Gunakan kaitan siklus yang wajar, `$onInit`, `$onChanges`, `$postLink` and `$onDestroy`
283 | * Catatan: `$onChanges` dipanggil sebelum `$onInit`, lihat [sumber](#sumber) untuk artikel yang menjelaskan tentang hal ini lebih dalam
284 | * Gunakan `require` bersama `$onInit` untuk mereferensikan logika warisan apapun
285 | * Jangan mengubah alias standar `$ctrl` untuk sintak `controllerAs`, oleh karena itu jangan gunakan `controllerAs` di sembarang tempat
286 |
287 | **[Kembali ke atas](#daftar-isi)**
288 |
289 | ### Aliran data satu-arah dan Event
290 |
291 | Aliran data satu-arah diperkenalkan di AngularJS 1.5, dan mengubah komunikasi komponen.
292 |
293 | Berikut adalah beberapa saran menggunakan aliran data satu-arah:
294 |
295 | * Di dalam komponen yang menerima data, selalu gunakan sintak databinding satu-arah `'<'`
296 | * _Jangan_ gunakan `'='` sintak databinding dua-arah lagi, dimanapun
297 | * Komponen yang memiliki `bindings` harus menggunakan `$onChanges` untuk mereplika databinding satu-arah guna memenggal Obyek yang diteruskan oleh referensi dan memperbarui data induk
298 | * Gunakan `$event` sebagai argumen fungsi dalam metoda induknya (lihat contoh berkondisi `$ctrl.addTodo($event)` yang ada dibawah)
299 | * Lempar sebuah `$event: {}` cadangan Obyek dari sebuah komponen tanpa-kondisi (lihat contoh tanpa-kondisi `this.onAddTodo` dibawah).
300 | * Bonus: Gunakan sebuah pembungkus `EventEmitter` dengan `.value()` untuk mencerminkan Angular, hindari pembuatan Obyek `$event` manual
301 | * Mengapa? Ini mencerminkan Angular dan menjaga konsistensi setiap komponen. Juga membuat semua kondisi dapat diprediksi.
302 |
303 | **[Kembali ke atas](#daftar-isi)**
304 |
305 | ### Komponen berkondisi
306 |
307 | Mari kita lihat apa yang kita sebut dengan "komponen berkondisi".
308 |
309 | * Kondisi penarikan, pada dasarnya berkomunikasi ke API backend melalui sebuah servis
310 | * Tidak memutasi kondisi secara langsung
311 | * Membuat komponen anakan yang memutasi kondisi
312 | * Mengacu pada sebuah komponen yang pintar/kontainer
313 |
314 | Contoh dari komponen berkondisi, lengkap dengan penetapan modul tingkat-rendahnya (ini hanya untuk demo, jadi beberapa kode telah dihilangkan untuk menyederhanakannya):
315 |
316 | ```js
317 | /* ----- todo/todo.component.js ----- */
318 | import controller from './todo.controller';
319 |
320 | const TodoComponent = {
321 | controller,
322 | template: `
323 |
324 |
327 |
329 |
330 | `
331 | };
332 |
333 | export default TodoComponent;
334 |
335 | /* ----- todo/todo.controller.js ----- */
336 | class TodoController {
337 | constructor(TodoService) {
338 | this.todoService = TodoService;
339 | }
340 | $onInit() {
341 | this.newTodo = {
342 | title: '',
343 | selected: false
344 | };
345 | this.todos = [];
346 | this.todoService.getTodos().then(response => this.todos = response);
347 | }
348 | addTodo({ todo }) {
349 | if (!todo) return;
350 | this.todos.unshift(todo);
351 | this.newTodo = {
352 | title: '',
353 | selected: false
354 | };
355 | }
356 | }
357 |
358 | TodoController.$inject = ['TodoService'];
359 |
360 | export default TodoController;
361 |
362 | /* ----- todo/todo.module.js ----- */
363 | import angular from 'angular';
364 | import TodoComponent from './todo.component';
365 |
366 | const TodoModule = angular
367 | .module('todo', [])
368 | .component('todo', TodoComponent)
369 | .name;
370 |
371 | export default TodoModule;
372 | ```
373 |
374 | Contoh ini menunjukkan sebuah komponen berkondisi, yang menarik suatu kondisi di pengendali, melalui sebuah servis, kemudian melemparnya ke komponen anakan tanpa-kondisi. Perhatikan bagaimana disana tidak ada Direktif yang digunakan seperti misalnya `ng-repeat` dan sebagainya di dalam templat. Melainkan, data dan fungsi yang didelegasikan ke dalam `` dan `` komponen-komponen tanpa-kondisi.
375 |
376 | **[Kembali ke atas](#daftar-isi)**
377 |
378 | ### Komponen tanpa-kondisi
379 |
380 | Mari kita lihat apa yang kita sebut dengan "komponen tanpa-kondisi".
381 |
382 | * Memiliki input dan output yang ditetapkan menggunakan `bindings: {}`
383 | * Data masuk ke komponen melalui atribut bindings (inputs)
384 | * Data keluar dari komponen melalui event (outputs)
385 | * Memutasikan kondisi, melemparkan cadangan data atas permintaan (seperti sebuah event klik atau kirim)
386 | * Tidak peduli dari mana datangnya data, semua tanpa-kondisi
387 | * Merupakan komponen-komponen yang sangat bisa dipakai-ulang
388 | * Mengacu pada komponen dumb/bersifat presentasi
389 |
390 | Contoh dari komponen tanpa-kondisi (mari kita gunakan `` sebagai contohnya), lengkap dengan penetapan modul tingkat-rendahnya (ini hanya untuk demo, jadi beberapa kode telah dihilangkan untuk menyederhanakannya):
391 |
392 | ```js
393 | /* ----- todo/todo-form/todo-form.component.js ----- */
394 | import controller from './todo-form.controller';
395 |
396 | const TodoFormComponent = {
397 | bindings: {
398 | todo: '<',
399 | onAddTodo: '&'
400 | },
401 | controller,
402 | template: `
403 |
407 | `
408 | };
409 |
410 | export default TodoFormComponent;
411 |
412 | /* ----- todo/todo-form/todo-form.controller.js ----- */
413 | class TodoFormController {
414 | constructor(EventEmitter) {
415 | this.EventEmitter = EventEmitter;
416 | }
417 | $onChanges(changes) {
418 | if (changes.todo) {
419 | this.todo = Object.assign({}, this.todo);
420 | }
421 | }
422 | onSubmit() {
423 | if (!this.todo.title) return;
424 | // with EventEmitter wrapper
425 | this.onAddTodo(
426 | this.EventEmitter({
427 | todo: this.todo
428 | })
429 | );
430 | // without EventEmitter wrapper
431 | this.onAddTodo({
432 | $event: {
433 | todo: this.todo
434 | }
435 | });
436 | }
437 | }
438 |
439 | TodoFormController.$inject = ['EventEmitter'];
440 |
441 | export default TodoFormController;
442 |
443 | /* ----- todo/todo-form/todo-form.module.js ----- */
444 | import angular from 'angular';
445 | import TodoFormComponent from './todo-form.component';
446 |
447 | const TodoFormModule = angular
448 | .module('todo.form', [])
449 | .component('todoForm', TodoFormComponent)
450 | .value('EventEmitter', payload => ({ $event: payload}))
451 | .name;
452 |
453 | export default TodoFormModule;
454 | ```
455 |
456 | Perhatikan bagaimana komponen `` tidak menarik kondisi apapun, ia hanya menerimanya saja, memutasi sebuah Obyek melalui logika pengendali yang berhubungan dengannya, kemudian melemparnya ke komponen induk melalui properti bindings. Dalam contoh ini, kaitan siklus `$onChanges` mereplika `this.todo` Obyek binding awal dan menetapkannya kembali, yang artinya bahwa data induk tidak terpengaruh sama sekali sampai kita mengirim formnya, bersama dengan sintak `'<'` binding yang baru dari aliran data satu-arah.
457 |
458 | **[Kembali ke atas](#daftar-isi)**
459 |
460 | ### Komponen yang memiliki rute
461 |
462 | Mari kita lihat apa yang kita sebut dengan "komponen yang memiliki rute".
463 |
464 | * Pada dasarnya adalah sebuah komponen berkondisi, dengan penetapan ruting
465 | * Tidak ada lagi berkas-berkas `router.js`
466 | * Kita menggunakan komponen yang memiliki Rute untuk menetapkan logika ruting mereka
467 | * Data "input" untuk komponen telah rampung melalui penyelesaian rute (opsional, masih tersedia di pengendali dengan pemanggilan servis)
468 |
469 | Untuk contoh ini, kita akan mengambil komponen `` yang ada, membuatnya kembali agar menggunakan sebuah penetapan rute dan `bindings` pada komponen yang menerima data (rahasianya disini `ui-router` adalah properti `resolve` yang kita buat, dalam hal ini `todoData` dipetakan secara langsung ke keseluruhan `bindings`). Kita memperlakukannya sebagai komponen yang memiliki rute karena pada dasarnya itu adalah sebuah "view":
470 |
471 | ```js
472 | /* ----- todo/todo.component.js ----- */
473 | import controller from './todo.controller';
474 |
475 | const TodoComponent = {
476 | bindings: {
477 | todoData: '<'
478 | },
479 | controller,
480 | template: `
481 |
482 |
485 |
487 |
488 | `
489 | };
490 |
491 | export default TodoComponent;
492 |
493 | /* ----- todo/todo.controller.js ----- */
494 | class TodoController {
495 | constructor() {}
496 | $onInit() {
497 | this.newTodo = {
498 | title: '',
499 | selected: false
500 | };
501 | }
502 | $onChanges(changes) {
503 | if (changes.todoData) {
504 | this.todos = Object.assign({}, this.todoData);
505 | }
506 | }
507 | addTodo({ todo }) {
508 | if (!todo) return;
509 | this.todos.unshift(todo);
510 | this.newTodo = {
511 | title: '',
512 | selected: false
513 | };
514 | }
515 | }
516 |
517 | export default TodoController;
518 |
519 | /* ----- todo/todo.service.js ----- */
520 | class TodoService {
521 | constructor($http) {
522 | this.$http = $http;
523 | }
524 | getTodos() {
525 | return this.$http.get('/api/todos').then(response => response.data);
526 | }
527 | }
528 |
529 | TodoService.$inject = ['$http'];
530 |
531 | export default TodoService;
532 |
533 | /* ----- todo/todo.module.js ----- */
534 | import angular from 'angular';
535 | import uiRouter from 'angular-ui-router';
536 | import TodoComponent from './todo.component';
537 | import TodoService from './todo.service';
538 |
539 | const TodoModule = angular
540 | .module('todo', [
541 | uiRouter
542 | ])
543 | .component('todo', TodoComponent)
544 | .service('TodoService', TodoService)
545 | .config(($stateProvider, $urlRouterProvider) => {
546 | $stateProvider
547 | .state('todos', {
548 | url: '/todos',
549 | component: 'todo',
550 | resolve: {
551 | todoData: TodoService => TodoService.getTodos()
552 | }
553 | });
554 | $urlRouterProvider.otherwise('/');
555 | })
556 | .name;
557 |
558 | export default TodoModule;
559 | ```
560 |
561 | **[Kembali ke atas](#daftar-isi)**
562 |
563 | # Direktif
564 |
565 | ### Teori direktif
566 |
567 | Direktif memberikan kita `template`, bindings `scope`, `bindToController`, `link` dan banyak hal lainnya. Pemakaiannya sekarang harus benar-benar memperhatikan keberadaan `.component()`. Direktif tidak boleh mendeklarasikan templat dan pengendali, atau menerima data melalui bindings. Direktif harus digunakan semata-mata untuk mendekorasi DOM. Dengan begini, maka artinya adalah memperluas keberadaan HTML - yang dibuat dengan `.component()`. Pemikiran sederhananya, bila Anda memerlukan sebuah event DOM/API tertentu dan logis, gunakanlah sebuah Direktif dan ikatkannya ke sebuah templat yang ada di dalam sebuah komponen. Bila Anda memerlukan sejumlah manipulasi DOM yang pintar, ada kaitan siklus `$postLink` sebagai pertimbangannya, tapi ini bukanlah tempatnya untuk memigrasikan semua manipulasi DOM Anda, sebisa mungkin gunakanlah Direktif untuk hal-hal yang bukan-Angular.
568 |
569 | Berikut adalah beberapa saran menggunakan Direktif:
570 |
571 | * Jangan pernah gunakan template, scope, bindToController atau controller
572 | * Selalu `restrict: 'A'` dengan Direktif
573 | * Gunakan compile dan link bila diperlukan
574 | * Jangan lupa hilangkan (destroy) dan unbind pengendali event di dalam `$scope.$on('$destroy', fn);`
575 |
576 | **[Kembali ke atas](#daftar-isi)**
577 |
578 | ### Properti yang disarankan
579 |
580 | Faktanya direktif mendukung kebanyakan apa yang dilakukan oleh `.component()` (direktif templat adalah komponen aslinya), saya menyarankan pembatasan penetapan Obyek direktif Anda hanya ke properti-properti berikut ini, untuk menghindari pemakaian direktif yang tidak benar:
581 |
582 | | Properti | Gunakan? | Mengapa |
583 | |---|---|---|
584 | | bindToController | Tidak | Gunakan `bindings` di dalam komponen |
585 | | compile | Ya | Untuk manipulasi/event pra-kompilasi DOM |
586 | | controller | Tidak | Gunakan sebuah komponen |
587 | | controllerAs | Tidak | Gunakan sebuah komponen |
588 | | link functions | Ya | Untuk manipulasi/event pra/post DOM |
589 | | multiElement | Ya | [Lihat dok](https://docs.angularjs.org/api/ng/service/$compile#-multielement-) |
590 | | priority | Ya | [Lihat dok](https://docs.angularjs.org/api/ng/service/$compile#-priority-) |
591 | | require | Tidak | Gunakan sebuah komponen |
592 | | restrict | Ya | Menetapkan pemakaian direktif, selalu gunakan `'A'` |
593 | | scope | Tidak | Gunakan sebuah komponen |
594 | | template | Tidak | Gunakan sebuah komponen |
595 | | templateNamespace | Ya (kalau Anda harus) | [Lihat dok](https://docs.angularjs.org/api/ng/service/$compile#-templatenamespace-) |
596 | | templateUrl | Tidak | Gunakan sebuah komponen |
597 | | transclude | Tidak | Gunakan sebuah komponen |
598 |
599 | **[Kembali ke atas](#daftar-isi)**
600 |
601 | ### Konstanta atau Kelas
602 |
603 | Ada beberapa pendekatan menggunakan ES2015 dan direktif, baik itu dengan sebuah fungsi panah maupun penetapan yang lebih mudah, atau dengan menggunakan sebuah `Class` ES2015. Pilih mana yang terbaik untuk Anda dan tim, ingatlah Angular menggunakan `Class`.
604 |
605 | Berikut adalah contoh bagaimana menggunakan konstanta dengan fungsi Panah dimana pembungkus eskpresi `() => ({})` mengembalikan sebuah Obyek literal (catat perbedaan pemakaian di dalam `.directive()`):
606 |
607 | ```js
608 | /* ----- todo/todo-autofocus.directive.js ----- */
609 | import angular from 'angular';
610 |
611 | const TodoAutoFocus = ($timeout) => ({
612 | restrict: 'A',
613 | link($scope, $element, $attrs) {
614 | $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => {
615 | if (!newValue) {
616 | return;
617 | }
618 | $timeout(() => $element[0].focus());
619 | });
620 | }
621 | });
622 |
623 | TodoAutoFocus.$inject = ['$timeout'];
624 |
625 | export default TodoAutoFocus;
626 |
627 | /* ----- todo/todo.module.js ----- */
628 | import angular from 'angular';
629 | import TodoComponent from './todo.component';
630 | import TodoAutofocus from './todo-autofocus.directive';
631 |
632 | const TodoModule = angular
633 | .module('todo', [])
634 | .component('todo', TodoComponent)
635 | .directive('todoAutofocus', TodoAutoFocus)
636 | .name;
637 |
638 | export default TodoModule;
639 | ```
640 |
641 | Atau menggunakan `Class` ES2015 (catat pemanggilan `new TodoAutoFocus` secara manual pada saat mendaftarkan direktif) untuk membuat Obyek:
642 |
643 | ```js
644 | /* ----- todo/todo-autofocus.directive.js ----- */
645 | import angular from 'angular';
646 |
647 | class TodoAutoFocus {
648 | constructor($timeout) {
649 | this.restrict = 'A';
650 | this.$timeout = $timeout;
651 | }
652 | link($scope, $element, $attrs) {
653 | $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => {
654 | if (!newValue) {
655 | return;
656 | }
657 | this.$timeout(() => $element[0].focus());
658 | });
659 | }
660 | }
661 |
662 | TodoAutoFocus.$inject = ['$timeout'];
663 |
664 | export default TodoAutoFocus;
665 |
666 | /* ----- todo/todo.module.js ----- */
667 | import angular from 'angular';
668 | import TodoComponent from './todo.component';
669 | import TodoAutofocus from './todo-autofocus.directive';
670 |
671 | const TodoModule = angular
672 | .module('todo', [])
673 | .component('todo', TodoComponent)
674 | .directive('todoAutofocus', ($timeout) => new TodoAutoFocus($timeout))
675 | .name;
676 |
677 | export default TodoModule;
678 | ```
679 |
680 | **[Kembali ke atas](#daftar-isi)**
681 |
682 | # Servis
683 |
684 | ### Teori servis
685 |
686 | Servis pada dasarnya adalah kontainer-kontainer untuk logika bisnis dimana komponen-komponen kita seharusnya tidak dipanggil secara langsung. Servis mengandung servis-servis bawaan lainnya atau eksternal seperti `$http`, yang dapat kita injeksi ke dalam pengendali komponen manapun di aplikasi kita. Kita memiliki dua cara memperlakukan servis, dengan menggunakan `.service()` atau `.factory()`. Dengan `Class` ES2015, kita seharusnya hanya menggunakan `.service()` saja, lengkap dengan keterangan injeksi dependensinya menggunakan `$inject`.
687 |
688 | **[Kembali ke atas](#daftar-isi)**
689 |
690 | ### Kelas untuk Servis
691 |
692 | Berikut adalah beberapa contoh implementasi untuk aplikasi `` kita menggunakan `Class` ES2015:
693 |
694 | ```js
695 | /* ----- todo/todo.service.js ----- */
696 | class TodoService {
697 | constructor($http) {
698 | this.$http = $http;
699 | }
700 | getTodos() {
701 | return this.$http.get('/api/todos').then(response => response.data);
702 | }
703 | }
704 |
705 | TodoService.$inject = ['$http'];
706 |
707 | export default TodoService;
708 |
709 | /* ----- todo/todo.module.js ----- */
710 | import angular from 'angular';
711 | import TodoComponent from './todo.component';
712 | import TodoService from './todo.service';
713 |
714 | const TodoModule = angular
715 | .module('todo', [])
716 | .component('todo', TodoComponent)
717 | .service('TodoService', TodoService)
718 | .name;
719 |
720 | export default TodoModule;
721 | ```
722 |
723 | **[Kembali ke atas](#daftar-isi)**
724 |
725 | # ES2015 dan Perangkat
726 |
727 | ##### ES2015
728 |
729 | * Gunakan [Babel](https://babeljs.io/) untuk mengkompilasi kode ES2015+ Anda dan polyfills apapun
730 | * Pertimbangkan untuk menggunakan [TypeScript](http://www.typescriptlang.org/) untuk memungkinkan upgrade ke Angular
731 |
732 | ##### Perangkat
733 | * Gunakan `ui-router` [versi alfa terakhir](https://github.com/angular-ui/ui-router) (lihat Readme) bila Anda ingin mendukung ruting-komponen
734 | * Bila tidak maka Anda akan terjebak dengan `template: ''` dan tidak ada pemetaan penyelesaian/`bindings`
735 | * Pertimbangkan pra-muat templat ke dalam `$templateCache` dengan `angular-templates`
736 | * [Versi Gulp](https://www.npmjs.com/package/gulp-angular-templatecache)
737 | * [Versi Grunt](https://www.npmjs.com/package/grunt-angular-templates)
738 | * Pertimbangkan untuk menggunakan [Webpack](https://webpack.github.io/) untuk mengkompilasi kode ES2015 Anda
739 | * Gunakan [ngAnnotate](https://github.com/olov/ng-annotate) untuk menerangkan properti `$inject` secara otomatis
740 | * Bagaimana menggunakan [ngAnnotate dengan ES6](https://www.timroes.de/2015/07/29/using-ecmascript-6-es6-with-angularjs-1-x/#ng-annotate)
741 |
742 | **[Kembali ke atas](#daftar-isi)**
743 |
744 | # Pengelolaan kondisi
745 |
746 | Untuk pengelolaan data, pertimbangkan Redux bersama AngularJS 1.5.
747 |
748 | * [Angular Redux](https://github.com/angular-redux/ng-redux)
749 |
750 | **[Kembali ke atas](#daftar-isi)**
751 |
752 | # Sumber
753 |
754 | * [Memahami metoda .component()](https://toddmotto.com/exploring-the-angular-1-5-component-method/)
755 | * [Menggunakan "require" dengan $onInit](https://toddmotto.com/on-init-require-object-syntax-angular-component/)
756 | * [Memahami semua kaitan siklus, $onInit, $onChange, $postLink, $onDestroy](https://toddmotto.com/angular-1-5-lifecycle-hooks)
757 | * [Menggunakan "resolve" di dalam rute](https://toddmotto.com/resolve-promises-in-angular-routes/)
758 | * [Redux dan pengelolaan kondisi Angular](http://blog.rangle.io/managing-state-redux-angular/)
759 | * [Contoh Aplikasi dari Komunitas](https://github.com/chihab/angular-styleguide-sample)
760 |
761 | **[Kembali ke atas](#daftar-isi)**
762 |
763 | # Dokumentasi
764 | Untuk lainnya, termasuk referensi API, periksalah [dokumentasi AngularJS](//docs.angularjs.org/api).
765 |
766 | # Berkontribusi
767 |
768 | Bukalah sebuah isu terlebih dahulu untuk mendiskusikan perubahan/penambahan yang potensial. Mohon tidak membuka isu untuk pertanyaan-pertanyaan.
769 |
770 | ## Lisensi
771 |
772 | #### (The MIT License)
773 |
774 | Copyright (c) 2016 Todd Motto
775 |
776 | Permission is hereby granted, free of charge, to any person obtaining
777 | a copy of this software and associated documentation files (the
778 | 'Software'), to deal in the Software without restriction, including
779 | without limitation the rights to use, copy, modify, merge, publish,
780 | distribute, sublicense, and/or sell copies of the Software, and to
781 | permit persons to whom the Software is furnished to do so, subject to
782 | the following conditions:
783 |
784 | The above copyright notice and this permission notice shall be
785 | included in all copies or substantial portions of the Software.
786 |
787 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
788 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
789 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
790 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
791 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
792 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
793 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
794 |
--------------------------------------------------------------------------------
/i18n/pt-pt.md:
--------------------------------------------------------------------------------
1 | # Guia de AngularJS (ES2015)
2 |
3 | ### Arquitetura, estrutura de ficheiros, componentes, fluxo de data unidireccional e boas práticas
4 |
5 | *Um guia de estilo sensato para equipas [@toddmotto](//twitter.com/toddmotto)*
6 |
7 | Esta arquitetura e guia de estilo foi totalmente reescrita para ES2015, mudanças em AngularJS 1.5+ para futuro upgrade para Angular. Este guia de estilo contem boas práticas para fluxo de data unidirecional, delegação de eventos, arquitetura por componentes e component routing.
8 |
9 | O guia de estilo antigo pode ser encontrado [aqui](https://github.com/toddmotto/angular-styleguide/tree/angular-old-es5), e os motivos para a criação de um novo [aqui](https://toddmotto.com/rewriting-angular-styleguide-angular-2).
10 |
11 | > Junte-se á experiencia de ensino Ultimate AngularJS para dominar funcionalidades básicas e avançadas de Angular e criar aplicações rápidas e escaláveis.
12 |
13 |
14 |
15 | ## Tabela de conteúdos
16 |
17 | 1. [Arquitetura modular](#arquitetura-modular)
18 | 1. [Teoria](#teoria)
19 | 1. [Root module](#root-module)
20 | 1. [Component module](#component-module)
21 | 1. [Common module](#common-module)
22 | 1. [Low-level modules](#low-level-modules)
23 | 1. [Estrutura de ficheiros escalável](#estrutura-de-ficheiros-escalável)
24 | 1. [Convenções para nomes de ficheiros](#convenções-para-nomes-de-ficheiros)
25 | 1. [Componentes](#componentes)
26 | 1. [Teoria](#componentes-teoria)
27 | 1. [Propriedades suportadas](#propriedades-suportadas)
28 | 1. [Controllers](#controllers)
29 | 1. [One-way dataflow e Eventos](#one-way-dataflow-e-eventos)
30 | 1. [Stateful Components](#stateful-components)
31 | 1. [Stateless Components](#stateless-components)
32 | 1. [Routed Components](#routed-components)
33 | 1. [Diretivas](#diretivas)
34 | 1. [Teoria](#diretivas-teoria)
35 | 1. [Propriedades recomendadas](#propriedades-recomendadas)
36 | 1. [Classes e constantes](#classes-e-constantes)
37 | 1. [Serviços](#serviços)
38 | 1. [Teoria](#serviços-teoria)
39 | 1. [Classes para serviços](#classes-para-serviços)
40 | 1. [ES2015 e Tooling](#es2015-e-tooling)
41 | 1. [State management](#state-management)
42 | 1. [Recursos](#recursos)
43 | 1. [Documentação](#documentação)
44 | 1. [Contribuições](#contribuições)
45 |
46 | # Arquitetura modular
47 |
48 | Cada module numa aplicação Angular é um module component. Um module component é a definição raiz para esse module e contém a lógica, templates, routing e child components (componentes filhos).
49 |
50 | ### Teoria
51 |
52 | O esquema dos módulos relaciona-se diretamente com a nossa estrutura de ficheiros, que mantém tudo gerível e escalável. Devemos idealmente ter três high-level modules : root, component e common. O root module define o módulo base que faz o bootstrap (inicialização) da nossa app, e correspondente template. Podemos então importar os nossos component e common modules para o nosso root module para incluir as nossas dependências. Os component e common modules requerem por sua vez lower-level component modules, que contêm os nossos componentes, serviços, diretivas, filtros e testes para cada funcionalidade reutilizável.
53 |
54 | **[Voltar ao topo](#tabela-de-conteúdos)**
55 |
56 | ### Root module
57 |
58 | Um root module começa com um root component que define o elemento base para toda a aplicação, com uma routing outlet definida. o exemplo demonstra utilizando `ui-view` do `ui-router`.
59 |
60 | ```js
61 | // app.component.js
62 | const AppComponent = {
63 | template: `
64 |
65 | Hello world
66 |
67 |
68 |
69 |
70 |
73 | `
74 | };
75 |
76 | export default AppComponent;
77 | ```
78 |
79 | Um root module é então criado, com `AppComponent` importado e registado com `.component('app', AppComponent)`. Imports para outros submodules (component e common modules) são também feitos para incluir todos os componentes relevantes para a aplicação.
80 |
81 | ```js
82 | // app.module.js
83 | import angular from 'angular';
84 | import uiRouter from 'angular-ui-router';
85 | import ComponentsModule from './components/components.module';
86 | import CommonModule from './common/common.module';
87 |
88 | const AppModule = angular
89 | .module('app', [
90 | ComponentsModule,
91 | CommonModule,
92 | uiRouter
93 | ])
94 | .component('app', AppComponent)
95 | .name;
96 |
97 | export default AppModule;
98 | ```
99 |
100 | **[Voltar ao topo](#tabela-de-conteúdos)**
101 |
102 | ### Component module
103 |
104 | Um component module é a aquele que contém referência para todos os componentes reutilizáveis. Em baixo importamos `ComponentsModule` e injectamolos para o Root module, isto dá-nos um local onde podemos importar todos os componentes de uma app. Estes módulos que requeremos estão desassociados de quaisquer outros módulos e podem portanto ser importados para outra aplicação facilmente.
105 |
106 | ```js
107 | import angular from 'angular';
108 | import CalendarModule from './calendar/calendar.module';
109 | import EventsModule from './events/events.module';
110 |
111 | const ComponentsModule = angular
112 | .module('app.components', [
113 | CalendarModule,
114 | EventsModule
115 | ])
116 | .name;
117 |
118 | export default ComponentsModule;
119 | ```
120 |
121 | **[Voltar ao topo](#tabela-de-conteúdos)**
122 |
123 | ### Common module
124 |
125 | O Common module é aquele que contém referência para todos os componentes específicos à aplicação, que não queremos utilizar numa outra aplicação. Podem ser coisas como Layout, navegação e footers. Ver em baixo como importamos `CommonModule` o e os injetamos para o Root module, isto dá-nos um local onde podemos colocar e importar todos os common components para a app.
126 |
127 | ```js
128 | import angular from 'angular';
129 | import NavModule from './nav/nav.module';
130 | import FooterModule from './footer/footer.module';
131 |
132 | const CommonModule = angular
133 | .module('app.common', [
134 | NavModule,
135 | FooterModule
136 | ])
137 | .name;
138 |
139 | export default CommonModule;
140 | ```
141 |
142 | **[Voltar ao topo](#tabela-de-conteúdos)**
143 |
144 | ### Low-level modules
145 |
146 | Low-level moduels são component modules individuais que contêm a lógica de cada feature block. Cada um deles definirá um modulo, para ser importado para um higher-level module, como um component ou common module tal como exemplificado antes. Sempre lembrar de adicionar o sufixo `.name` a cada `export` ao criar um _novo_ modulo, e não quando referenciando um. Poderá perceber que routing definitions também existiram aqui, iremos abordar este tema mais a frente neste guia.
147 |
148 | ```js
149 | import angular from 'angular';
150 | import uiRouter from 'angular-ui-router';
151 | import CalendarComponent from './calendar.component';
152 |
153 | const CalendarModule = angular
154 | .module('calendar', [
155 | uiRouter
156 | ])
157 | .component('calendar', CalendarComponent)
158 | .config(($stateProvider, $urlRouterProvider) => {
159 | $stateProvider
160 | .state('calendar', {
161 | url: '/calendar',
162 | component: 'calendar'
163 | });
164 | $urlRouterProvider.otherwise('/');
165 | })
166 | .name;
167 |
168 | export default CalendarModule;
169 | ```
170 |
171 | **[Voltar ao topo](#tabela-de-conteúdos)**
172 |
173 | # Convenções para nomes de ficheiros
174 |
175 | Manter tudo simples e lowercase, utilizar o nome do component, i.e. `calendar.*.js*`, `calendar-grid.*.js` - com o tipo do ficheiro no meio. Utilize `*.module.js` para o ficheiro do modulo de definição, para que possa importar o modulo pelo nome da diretoria.
176 |
177 | ```
178 | calendar.module.js
179 | calendar.controller.js
180 | calendar.component.js
181 | calendar.service.js
182 | calendar.directive.js
183 | calendar.filter.js
184 | calendar.spec.js
185 | ```
186 |
187 | **[Voltar ao topo](#tabela-de-conteúdos)**
188 |
189 | ### Estrutura de ficheiros escalável
190 |
191 | Estrutura de ficheiros é extremamente importante, aqui descrevemos uma estrutura escalável e previsível. Um exemplo de uma estrutura que ilustra uma arquitetura modular de componentes.
192 |
193 | ```
194 | ├── app/
195 | │ ├── components/
196 | │ │ ├── calendar/
197 | │ │ │ ├── calendar.module.js
198 | │ │ │ ├── calendar.controller.js
199 | │ │ │ ├── calendar.component.js
200 | │ │ │ ├── calendar.service.js
201 | │ │ │ ├── calendar.spec.js
202 | │ │ │ └── calendar-grid/
203 | │ │ │ ├── calendar-grid.module.js
204 | │ │ │ ├── calendar-grid.controller.js
205 | │ │ │ ├── calendar-grid.component.js
206 | │ │ │ ├── calendar-grid.directive.js
207 | │ │ │ ├── calendar-grid.filter.js
208 | │ │ │ └── calendar-grid.spec.js
209 | │ │ ├── events/
210 | │ │ │ ├── events.module.js
211 | │ │ │ ├── events.controller.js
212 | │ │ │ ├── events.component.js
213 | │ │ │ ├── events.directive.js
214 | │ │ │ ├── events.service.js
215 | │ │ │ ├── events.spec.js
216 | │ │ │ └── events-signup/
217 | │ │ │ ├── events-signup.module.js
218 | │ │ │ ├── events-signup.controller.js
219 | │ │ │ ├── events-signup.component.js
220 | │ │ │ ├── events-signup.service.js
221 | │ │ │ └── events-signup.spec.js
222 | │ │ └── components.module.js
223 | │ ├── common/
224 | │ │ ├── nav/
225 | │ │ │ ├── nav.module.js
226 | │ │ │ ├── nav.controller.js
227 | │ │ │ ├── nav.component.js
228 | │ │ │ ├── nav.service.js
229 | │ │ │ └── nav.spec.js
230 | │ │ ├── footer/
231 | │ │ │ ├── footer.module.js
232 | │ │ │ ├── footer.controller.js
233 | │ │ │ ├── footer.component.js
234 | │ │ │ ├── footer.service.js
235 | │ │ │ └── footer.spec.js
236 | │ │ └── common.module.js
237 | │ ├── app.module.js
238 | │ └── app.component.js
239 | └── index.html
240 | ```
241 |
242 | A primeira diretoria de topo apenas contém um `index.html` e `app/`, a diretoria onde todos os nossos root, component, common e low-level modules estão inseridos.
243 |
244 | **[Voltar ao topo](#tabela-de-conteúdos)**
245 |
246 | # Componentes
247 |
248 | ### Componentes Teoria
249 |
250 | Componentes são essencialmente templates com controllers. Eles _não_ são diretivas, nem se deve tentar substituir diretivas com componentes, a não ser que esteja a atualizar "template Directives" com controllers, que funcionam melhor como componentes. Componentes também contêm bindings que definem inputs e outputs para dados e eventos, lifecycle hooks e a habilidade de utilizar fluxo de dados unidirecional e Objetos de eventos para enviar dados para o topo para um parent component. Estes são os novos standards para AngularJS 1.5 e para cima. Tudo o que for template e controller driven que criarmos e muito provável que seja um componente, que pode ser stateful, stateless ou um routed component. Pode-se pensar num componente como um pedaço de código completo e não só o `.component()` definition Object. Vamos analisar algumas boas práticas e conselhos para componentes, e depois entrar em como se deve organizá-los em conceitos de stateful, stateless e routed components.
251 |
252 | **[Voltar ao topo](#tabela-de-conteúdos)**
253 |
254 | ### Propriedades suportadas
255 |
256 | Estas são as propriedades suportadas para `.component()` que podem ser utilizadas :
257 |
258 | | Propriedade | Suporte |
259 | |---|---|
260 | | bindings | Sim, usar `'@'`, `'<'`, `'&'` apenas |
261 | | controller | Sim |
262 | | controllerAs | Sim, default é `$ctrl` |
263 | | require | Sim (new Object syntax) |
264 | | template | Sim |
265 | | templateUrl | Sim |
266 | | transclude | Sim |
267 |
268 | **[Voltar ao topo](#tabela-de-conteúdos)**
269 |
270 | ### Controllers
271 |
272 | Controllers devem ser utilizados juntamente com componentes, e nunca em qualquer outro sitio. Se pensamos que precisamos de um controller, o que realmente precisamos é um stateless component para gerir este pedaço de comportamento.
273 |
274 | Algumas conselhos para utilizar `Class` para controllers:
275 |
276 | * Utilizar sempre o `constructor` para propósitos de dependency injection
277 | * Não exportar a `Class` diretamnete mas sim o nome para permitir `$inject` annotations
278 | * Se for preciso aceder ao lexical scope utilizar arrow functions
279 | * Como alternativa a arrow functions `let ctrl = this;` também é aceitavel e pode até fazer mais sentido dependendo do use caso
280 | * Fazer Bind the todas as funções públicas direcatamente à `Class`
281 | * Utilizar os lifecycles hooks apropriados,`$onInit`, `$onChanges`, `$postLink` e `$onDestroy`
282 | * Nota: `$onChanges` é chamado antes de `$onInit`, ver secção [recursos](#recursos) para artigoes detalhando melhor este comportamento.
283 | * Utilizar `require` juntamente com `$onInit` para referenciar qualquer logica herdada
284 | * Não substituir o default alias `$ctrl` para utilização da sintaxe `controllerAs`, ou seja não utilizar `controllerAs` em lado nenhum
285 |
286 | **[Voltar ao topo](#tabela-de-conteúdos)**
287 |
288 | ### Fluxo de dados unidireccional e Eventos
289 |
290 | Fluxo de dados unidireccional foi introduzido com AngularJS 1.5 e redefine a comunicação entre componentes
291 |
292 | Alguns conselhos ao utilizar fluxo de dados unidireccional:
293 |
294 | * Em componentes que recebem dados, utilizar sempre a sintaxe de one-way databinding `'<'`
295 | * _Não_ utilizar `'='` sintaxe de two-way databinding em lado nenhum, nunca
296 | * Componentes que tenham `bindings` devem utilizar `$onChanges` para clonar os dados passados via one-way binding e partir Objectos passados por referência e actualizar os dados dos seus parents
297 | * Utilizar `$event` com argumento de função no método pai (ver examplo de stateful abaixo `$ctrl.addTodo($event)`)
298 | * Passar um objecto `$event: {}` para cima de um stateless component (ver exemplo de stateless abaixo `this.onAddTodo`).
299 | * Bonus : Utilizar um wrapper `EventEmitter` com `.value()` para replicar Angular2, evita criação manual do objecto `$event`
300 | * Porquê? De forma a replicar o comportamento de Angular2 e manter consistência dentro de cada componente. Mantém também o estado (state) previsível.
301 |
302 | **[Voltar ao topo](#tabela-de-conteúdos)**
303 |
304 | ### Stateful components
305 |
306 | Vamos definir o que chamamos de "stateful component".
307 |
308 | * Requer state (estado da aplicação), essencialmente comunicando com uma API backend através de um serviço
309 | * Não altera estado da aplicação diretamente
310 | * Faz o render de componentes filhos que alteram estado
311 | * Também referidos como smart/container components
312 |
313 | Um exemplo de um stateful component, completo com a sua definição low-level (apenas como demonstração, algum código foi omitido por razões de brevidade):
314 |
315 | ```js
316 | /* ----- todo/todo.component.js ----- */
317 | import controller from './todo.controller';
318 |
319 | const TodoComponent = {
320 | controller,
321 | template: `
322 |
323 |
326 |
328 |
329 | `
330 | };
331 |
332 | export default TodoComponent;
333 |
334 | /* ----- todo/todo.controller.js ----- */
335 | class TodoController {
336 | constructor(TodoService) {
337 | this.todoService = TodoService;
338 | }
339 | $onInit() {
340 | this.newTodo = {
341 | title: '',
342 | selected: false
343 | };
344 | this.todos = [];
345 | this.todoService.getTodos().then(response => this.todos = response);
346 | }
347 | addTodo({ todo }) {
348 | if (!todo) return;
349 | this.todos.unshift(todo);
350 | this.newTodo = {
351 | title: '',
352 | selected: false
353 | };
354 | }
355 | }
356 |
357 | TodoController.$inject = ['TodoService'];
358 |
359 | export default TodoController;
360 |
361 | /* ----- todo/todo.module.js ----- */
362 | import angular from 'angular';
363 | import TodoComponent from './todo.component';
364 |
365 | const TodoModule = angular
366 | .module('todo', [])
367 | .component('todo', TodoComponent)
368 | .name;
369 |
370 | export default TodoModule;
371 | ```
372 |
373 | Este exemplo demonstra um stateful component, que requer estado dentro de um controller, através de um serviço, e o passa para os seus filhos stateless components. De notar que não existem quaisquer directivas a serem utilizadas como `ng-repeat` dentro do template. Em vez disso, dados e funções são delegadas para `` e `` stateless components.
374 |
375 | **[Voltar ao topo](#tabela-de-conteúdos)**
376 |
377 | ### Stateless components
378 |
379 | Vamos definir o que chamamos de "stateless component".
380 |
381 | * Tem input e outputs definidos utilizando `bindings: {}`
382 | * Dados entram no componente através de attribute bindings (inputs)
383 | * Dados saem do componente através de eventos (outputs)
384 | * Altera estado, passa dados para cima quando necessário (como um click ou um evento submit)
385 | * Não lhe interessa de onde os dados vêm originalmente, é stateless
386 | * São componentes altamente reutilizáveis
387 | * Também referidos como componentes dumb / de apresentação
388 |
389 | Um exemplo de um stateless component (vamos utilizar `` como exemplo), completo com as suas definições de módulos low-level (apenas como demonstração, algum código foi omitido por razões de brevidade):
390 |
391 | ```js
392 | /* ----- todo/todo-form/todo-form.component.js ----- */
393 | import controller from './todo-form.controller';
394 |
395 | const TodoFormComponent = {
396 | bindings: {
397 | todo: '<',
398 | onAddTodo: '&'
399 | },
400 | controller,
401 | template: `
402 |
406 | `
407 | };
408 |
409 | export default TodoFormComponent;
410 |
411 | /* ----- todo/todo-form/todo-form.controller.js ----- */
412 | class TodoFormController {
413 | constructor(EventEmitter) {
414 | this.EventEmitter = EventEmitter;
415 | }
416 | $onChanges(changes) {
417 | if (changes.todo) {
418 | this.todo = Object.assign({}, this.todo);
419 | }
420 | }
421 | onSubmit() {
422 | if (!this.todo.title) return;
423 | // with EventEmitter wrapper
424 | this.onAddTodo(
425 | this.EventEmitter({
426 | todo: this.todo
427 | })
428 | );
429 | // without EventEmitter wrapper
430 | this.onAddTodo({
431 | $event: {
432 | todo: this.todo
433 | }
434 | });
435 | }
436 | }
437 |
438 | TodoFormController.$inject = ['EventEmitter'];
439 |
440 | export default TodoFormController;
441 |
442 | /* ----- todo/todo-form/todo-form.module.js ----- */
443 | import angular from 'angular';
444 | import TodoFormComponent from './todo-form.component';
445 |
446 | const TodoFormModule = angular
447 | .module('todo.form', [])
448 | .component('todoForm', TodoFormComponent)
449 | .value('EventEmitter', payload => ({ $event: payload}))
450 | .name;
451 |
452 | export default TodoFormModule;
453 | ```
454 |
455 | De notar como o componente `` não requer qualquer estado, apenas o recebe, muta (altera) um Objecto através do seu controller com a lógica associada, e o passa de volta para o componente pai através de property bindings. Neste exemplo lo lifecycle hook `$onChanges` faz um clone do binding inicial de `this.todo` e o reatribui, o que significa que os dados do componente pai não serão afetados até submetermos o form, juntamente com a nova sintaxe de fluxo de dados unidirecional `'<'`.
456 |
457 | **[Voltar ao topo](#tabela-de-conteúdos)**
458 |
459 | ### Routed components
460 |
461 | Vamos definir o que chamamos de "routed component".
462 |
463 | * É essencialmente um stateful component, com definições de routing
464 | * Não se utilizam mais ficheiros `router.js`
465 | * Utilizamos Routed components para definir a sua lógica de routing
466 | * Inputs para o componente é feito através de route resolve (opcional, mas ainda disponível no controller com chamadas a serviços)
467 |
468 | Para este exemplo, vamos utilizar o componente ``, redefini-lo para utilizar uma definição de routing e `bindings` no componente que recebe dados (o segredo aqui como `ui-router` é o `resolve` das propriedades que criamos, neste caso `todoData` diretamente mapeado para `bindings`)
469 | . Tratamo-lo como routed component porque é essencialmente uma "view":
470 |
471 | ```js
472 | /* ----- todo/todo.component.js ----- */
473 | import controller from './todo.controller';
474 |
475 | const TodoComponent = {
476 | bindings: {
477 | todoData: '<'
478 | },
479 | controller,
480 | template: `
481 |
482 |
485 |
487 |
488 | `
489 | };
490 |
491 | export default TodoComponent;
492 |
493 | /* ----- todo/todo.controller.js ----- */
494 | class TodoController {
495 | constructor() {}
496 | $onInit() {
497 | this.newTodo = {
498 | title: '',
499 | selected: false
500 | };
501 | }
502 | $onChanges(changes) {
503 | if (changes.todoData) {
504 | this.todos = Object.assign({}, this.todoData);
505 | }
506 | }
507 | addTodo({ todo }) {
508 | if (!todo) return;
509 | this.todos.unshift(todo);
510 | this.newTodo = {
511 | title: '',
512 | selected: false
513 | };
514 | }
515 | }
516 |
517 | export default TodoController;
518 |
519 | /* ----- todo/todo.service.js ----- */
520 | class TodoService {
521 | constructor($http) {
522 | this.$http = $http;
523 | }
524 | getTodos() {
525 | return this.$http.get('/api/todos').then(response => response.data);
526 | }
527 | }
528 |
529 | TodoService.$inject = ['$http'];
530 |
531 | export default TodoService;
532 |
533 | /* ----- todo/todo.module.js ----- */
534 | import angular from 'angular';
535 | import uiRouter from 'angular-ui-router';
536 | import TodoComponent from './todo.component';
537 | import TodoService from './todo.service';
538 |
539 | const TodoModule = angular
540 | .module('todo', [
541 | uiRouter
542 | ])
543 | .component('todo', TodoComponent)
544 | .service('TodoService', TodoService)
545 | .config(($stateProvider, $urlRouterProvider) => {
546 | $stateProvider
547 | .state('todos', {
548 | url: '/todos',
549 | component: 'todo',
550 | resolve: {
551 | todoData: TodoService => TodoService.getTodos()
552 | }
553 | });
554 | $urlRouterProvider.otherwise('/');
555 | })
556 | .name;
557 |
558 | export default TodoModule;
559 | ```
560 |
561 | **[Voltar ao topo](#tabela-de-conteúdos)**
562 |
563 | # Diretivas
564 |
565 | ### Diretivas Teoria
566 |
567 | Diretivas dão-nos `template`, `scope` bindings, `bindToController`, `link` e muitas outras coisas. A utilização delas deve ser agora cuidadosamente avaliada sendo que existe `.component()`. Diretivas não devem criar templates e controllers, nem receber dados através de bindings. Diretivas devem ser utilizadas unicamente para decoração da DOM. Com isto, significa estender HTML existente - criado com `.component()`. De uma forma simples, se precisamos de DOM events/API's custom e lógica, utilizamos diretivas e fazemos bind a um template dentro de um componente. Se precisamos de pouca quantidade de manipulação da DOM existe o lifecycle hook `$postLink` a considerar, no entanto não é o local para onde se deve migrar toda a manipulação da DOM, utilizando uma Diretiva se possível para coisas que não sejam do âmbito de Angular.
568 |
569 | Alguns conselhos quando utilizando diretivas:
570 |
571 | * Nunca utilizar templates, scope, bindToController ou controllers
572 | * Utilizar sempre `restrict: 'A'` com diretivas
573 | * Utilizar compile e link quando necessário
574 | * Lembrar de destruir e fazer unbind aos event handlers dentro de `$scope.$on('$destroy', fn);`
575 |
576 | **[Voltar ao topo](#tabela-de-conteúdos)**
577 |
578 | ### Propriedades recomendadas
579 |
580 | Devido ao facto de suportarem a maioria do que `.component()` suporta (template directives eram o componente original), recomendo limitar o Objecto de definição de directivas para apenas estas propriedades para evitar utilização de directivas incorrectamente:
581 |
582 | | Propriedade | Utilizar? | Porquê |
583 | |---|---|---|
584 | | bindToController | Não | Utilizar `bindings` em componentes |
585 | | compile | Sim | Para pre-compile DOM manipulation/events |
586 | | controller | Não | Utilizar um componente |
587 | | controllerAs | Não | Utilizar um componente |
588 | | link functions | Sim | Para pre/post DOM manipulation/events |
589 | | multiElement | Sim | [Ver docs](https://docs.angularjs.org/api/ng/service/$compile#-multielement-) |
590 | | priority | Sim | [Ver docs](https://docs.angularjs.org/api/ng/service/$compile#-priority-) |
591 | | require | Não | Utilizar um componente |
592 | | restrict | Sim | Definir utilização de diretivas, utilizar sempre `'A'` |
593 | | scope | Não | Utilizar um componente |
594 | | template | Não | Utilizar um componente |
595 | | templateNamespace | Sim (se necessário) | [Ver docs](https://docs.angularjs.org/api/ng/service/$compile#-templatenamespace-) |
596 | | templateUrl | Não | Utilizar um componente |
597 | | transclude | Não | Utilizar um componente |
598 |
599 | **[Voltar ao topo](#tabela-de-conteúdos)**
600 |
601 | ### Classes e Constantes
602 |
603 | Existem algumas formas de abordar ES2015 e diretivas, sendo com arrow function e easier assignment, ou utilizando uma ES2015 `Class`. Recomenda-se escolher que for melhor para a equipa, tendo em mente que Angular utiliza `Class`.
604 |
605 | Um exemplo de utilização de uma constante com arrow function `() => ({})` devolvendo um Object literal (de notar as diferenças dentro de `.directive()`):
606 |
607 | ```js
608 | /* ----- todo/todo-autofocus.directive.js ----- */
609 | import angular from 'angular';
610 |
611 | const TodoAutoFocus = ($timeout) => ({
612 | restrict: 'A',
613 | link($scope, $element, $attrs) {
614 | $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => {
615 | if (!newValue) {
616 | return;
617 | }
618 | $timeout(() => $element[0].focus());
619 | });
620 | }
621 | });
622 |
623 | TodoAutoFocus.$inject = ['$timeout'];
624 |
625 | export default TodoAutoFocus;
626 |
627 | /* ----- todo/todo.module.js ----- */
628 | import angular from 'angular';
629 | import TodoComponent from './todo.component';
630 | import TodoAutofocus from './todo-autofocus.directive';
631 |
632 | const TodoModule = angular
633 | .module('todo', [])
634 | .component('todo', TodoComponent)
635 | .directive('todoAutofocus', TodoAutoFocus)
636 | .name;
637 |
638 | export default TodoModule;
639 | ```
640 |
641 | Ou utilizando ES2015 `Class` (de notar a chamada manual de `new TodoAutoFocus` ao registar a diretiva) para criar o Objecto:
642 |
643 | ```js
644 | /* ----- todo/todo-autofocus.directive.js ----- */
645 | import angular from 'angular';
646 |
647 | class TodoAutoFocus {
648 | constructor($timeout) {
649 | this.restrict = 'A';
650 | this.$timeout = $timeout;
651 | }
652 | link($scope, $element, $attrs) {
653 | $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => {
654 | if (!newValue) {
655 | return;
656 | }
657 | this.$timeout(() => $element[0].focus());
658 | });
659 | }
660 | }
661 |
662 | TodoAutoFocus.$inject = ['$timeout'];
663 |
664 | export default TodoAutoFocus;
665 |
666 | /* ----- todo/todo.module.js ----- */
667 | import angular from 'angular';
668 | import TodoComponent from './todo.component';
669 | import TodoAutofocus from './todo-autofocus.directive';
670 |
671 | const TodoModule = angular
672 | .module('todo', [])
673 | .component('todo', TodoComponent)
674 | .directive('todoAutofocus', ($timeout) => new TodoAutoFocus($timeout))
675 | .name;
676 |
677 | export default TodoModule;
678 | ```
679 |
680 | **[Voltar ao topo](#tabela-de-conteúdos)**
681 |
682 | # Serviços
683 |
684 | ### Serviços Teoria
685 |
686 | Serviços são essencialmente containers com lógica de negócio que os nossos componentes não deviam requisitar diretamente. Serviços contêm outros serviços built-in como `$http`, que depois podemos injetar para controllers de componentes noutros locais da nossa aplicação. Existem duas formas de utilizar serviços, através de `.service()` ou `.factory()`. Com ES2015 `Class`, devemos utilizar apenas`.service()`, completo com dependency injection annotation utilizando `$inject`.
687 |
688 | **[Voltar ao topo](#tabela-de-conteúdos)**
689 |
690 | ### Classes para Serviços
691 |
692 | Aqui está um exemplo de implementação da nossa `` app utilizando ES2015 `Class`:
693 |
694 | ```js
695 | /* ----- todo/todo.service.js ----- */
696 | class TodoService {
697 | constructor($http) {
698 | this.$http = $http;
699 | }
700 | getTodos() {
701 | return this.$http.get('/api/todos').then(response => response.data);
702 | }
703 | }
704 |
705 | TodoService.$inject = ['$http'];
706 |
707 | export default TodoService;
708 |
709 | /* ----- todo/todo.module.js ----- */
710 | import angular from 'angular';
711 | import TodoComponent from './todo.component';
712 | import TodoService from './todo.service';
713 |
714 | const TodoModule = angular
715 | .module('todo', [])
716 | .component('todo', TodoComponent)
717 | .service('TodoService', TodoService)
718 | .name;
719 |
720 | export default TodoModule;
721 | ```
722 |
723 | **[Voltar ao topo](#tabela-de-conteúdos)**
724 |
725 | # ES2015 e Tooling
726 |
727 | ##### ES2015
728 |
729 | * Utilizar [Babel](https://babeljs.io/) para compilar código ES2015+ e outros polyfills
730 | * Considerar utilizar [TypeScript](http://www.typescriptlang.org/) para abrir caminho para qualquer upgrade para Angular
731 |
732 | ##### Tooling
733 | * Utilizar `ui-router` [ultima alpha](https://github.com/angular-ui/ui-router) (ver Readme) se queremos suportar component routing
734 | * Caso contrário está preso a `template: ''` sem quaisquer `bindings`
735 | * Considerar utilizar [Webpack](https://webpack.github.io/) para compilar o código ES2015
736 | * Utilizar [ngAnnotate](https://github.com/olov/ng-annotate) para automaticamente anotar propriedades com `$inject`
737 | * Como utilizar [ngAnnotate com ES6](https://www.timroes.de/2015/07/29/using-ecmascript-6-es6-with-angularjs-1-x/)
738 |
739 | **[Voltar ao topo](#tabela-de-conteúdos)**
740 |
741 | # State management
742 |
743 | Considerar utilizar Redux com AngularJS 1.5 para data management.
744 |
745 | * [Angular Redux](https://github.com/angular-redux/ng-redux)
746 |
747 | **[Voltar ao topo](#tabela-de-conteúdos)**
748 |
749 | # Recursos
750 |
751 | * [Compreender o método .component()](https://toddmotto.com/exploring-the-angular-1-5-component-method/)
752 | * [Utilizar "require" com $onInit](https://toddmotto.com/on-init-require-object-syntax-angular-component/)
753 | * [Compreender todos os lifecycle hooks, $onInit, $onChanges, $postLink, $onDestroy](https://toddmotto.com/angular-1-5-lifecycle-hooks)
754 | * [Utilizando "resolve" em routes](https://toddmotto.com/resolve-promises-in-angular-routes/)
755 | * [Redux e Angular para state management](http://blog.rangle.io/managing-state-redux-angular/)
756 | * [Exemplos de Aplicativos da Comunidade](https://github.com/chihab/angular-styleguide-sample)
757 |
758 | **[Voltar ao topo](#tabela-de-conteúdos)**
759 |
760 | # Documentação
761 | Para qualquer outra informação, incluindo referências de API, consultar [Documentação de AngularJS](//docs.angularjs.org/api).
762 |
763 | # Contribuições
764 |
765 | Abrir primeiro um issue para discutir potenciais alterações/adições. Por favor não abrir issues para questões.
766 |
767 | ## Licença
768 |
769 | #### (The MIT License)
770 |
771 | Copyright (c) 2016 Todd Motto
772 |
773 | Permission is hereby granted, free of charge, to any person obtaining
774 | a copy of this software and associated documentation files (the
775 | 'Software'), to deal in the Software without restriction, including
776 | without limitation the rights to use, copy, modify, merge, publish,
777 | distribute, sublicense, and/or sell copies of the Software, and to
778 | permit persons to whom the Software is furnished to do so, subject to
779 | the following conditions:
780 |
781 | The above copyright notice and this permission notice shall be
782 | included in all copies or substantial portions of the Software.
783 |
784 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
785 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
786 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
787 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
788 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
789 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
790 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
791 |
--------------------------------------------------------------------------------
/i18n/ru-ru.md:
--------------------------------------------------------------------------------
1 | # AngularJS руководство по стилю кода (ES2015)
2 |
3 | ### Архитектура, структура файлов, компоненты, односторонний обмен данными и лучшие практики
4 |
5 | *Осмысленное руководство по стилю кода для разработчиков от [@toddmotto](//twitter.com/toddmotto)*
6 |
7 | Архитектура и руководство были переписаны с нуля с использованием ES2015, изменений в AngularJS 1.5+, чтобы в будущем можно было легко обновить ваш проект на Angular. Это руководство сожержит лучшие практики по использованию одностороннего обмена данными, делегацию событий, компонентную архитектуру и маршрутизацию.
8 |
9 | Старое руководство вы можете прочитать [тут](https://github.com/toddmotto/angular-styleguide/tree/angular-old-es5), и рассуждения о новом руководстве по стилю кода можно найти [тут](https://toddmotto.com/rewriting-angular-styleguide-angular-2).
10 |
11 | > Присоединяйтесь к Ultimate AngularJS курсу, чтобы пройти путь от новичка к профессионалу и узнать про крутые возможности в Angular, а так же научиться создавать крутые, современные приложения, которые быстрые и масштабируемые.
12 |
13 |
14 |
15 | ## Содержание
16 |
17 | 1. [Модульная архитектура](#Модульная-архитектура)
18 | 1. [Теория](#Теория-о-модуле)
19 | 1. [Главный/Корневой модуль](#Корневой-модуль)
20 | 1. [Модуль Component](#Модуль-component)
21 | 1. [Модуль Common](#Модуль-common)
22 | 1. [Низкоуровневые модули](#Низкоуровневые-модули)
23 | 1. [Конвенция наименования файлов](#Конвенция-наименования-файлов)
24 | 1. [Масштабируемая структура файлов/папок](#Масштабируемая-структура-файлов)
25 | 1. [Компоненты](#Компоненты)
26 | 1. [Теория](#Теория-о-компонентах)
27 | 1. [Поддерживаемые свойства](#Поддерживаемые-свойства)
28 | 1. [Контроллеры](#Контроллеры)
29 | 1. [События и односторонний обмен данными](#События-и-Односторонний-обмен-данными)
30 | 1. [Stateful компоненты](#stateful-компоненты)
31 | 1. [Stateless компоненты](#stateless-компоненты)
32 | 1. [Компоненты маршрутизации](#Компоненты-маршрутизации)
33 | 1. [Директивы](#Директивы)
34 | 1. [Теория](#Теория-о-директивах)
35 | 1. [Рекомендуемые свойства](#Рекомендуемые-свойства)
36 | 1. [Константы или Классы](#Константы-или-Классы)
37 | 1. [Сервисы](#Сервисы)
38 | 1. [Теория](#Теория-о-сервисах)
39 | 1. [Используем классы в сервисе](#Используем-классы-в-сервисе)
40 | 1. [ES2015 и Утилиты](#ES2015-и-Утилиты)
41 | 1. [Управление состоянием](#Управление-состоянием)
42 | 1. [Ресурсы](#Ресурсы)
43 | 1. [Документация](#Документация)
44 | 1. [Помощь проекту](#Помощь-проекту)
45 |
46 | # Модульная архитектура
47 |
48 | Каждый модуль в Angular приложении - это модульный компонент. Модульный компонент - это определение для модуля который инкапсулирует логику, шаблоны, маршрутизацию и дочерние компоненты.
49 |
50 | ### Теория о модуле
51 |
52 | Проектирование модуля напрямую зависит от структуры папки, которая проста в поддерживании и легко читаемая. В идеальном случае у нас должно быть три корневых модуля: root, component и common. Root модуль является нашим базовым модулем который собирает наше приложение в одно целое и соответствующие шаблоны. Мы импортируем наши component'ы и common модули в root модуль, чтобы подключить все зависимости. А component и common модули в свою очередь подключают низкоуровневые модули-компоненты, которые содержут наши компоненты, контроллеры, сервисы, директивы, фильтры и тесты для каждой фичи которую можно многоразово использовать.
53 |
54 |
55 | **[Наверх](#содержание)**
56 |
57 | ### Корневой модуль
58 |
59 | Корневой модуль состоит из корневого компоненты который задает базовый элемент для всего приложения, с исходящей маршрутизацией, пример показывает использование `ui-view` из `ui-router`.
60 |
61 | ```js
62 | // app.component.js
63 | const AppComponent = {
64 | template: `
65 |
66 | Hello world
67 |
68 |
69 |
70 |
71 |
74 | `
75 | };
76 |
77 | export default AppComponent;
78 | ```
79 |
80 | Далее корневой модуль создан и экспортирован как`AppComponent`, после его импорта он так же зарегистрирован как `.component('app', AppComponent)`. Дальнейшие импорты сабмодулей(component и common модулей) сделаны, чтобы включить все необходимые компоненты для нашего приложения.
81 |
82 | ```js
83 | // app.module.js
84 | import angular from 'angular';
85 | import uiRouter from 'angular-ui-router';
86 | import AppComponent from './app.component';
87 | import ComponentsModule from './components/components.module';
88 | import CommonModule from './common/common.module';
89 |
90 | const AppModule = angular
91 | .module('app', [
92 | ComponentsModule,
93 | CommonModule,
94 | uiRouter
95 | ])
96 | .component('app', AppComponent)
97 | .name;
98 |
99 | export default AppModule;
100 | ```
101 |
102 | **[Наверх](#содержание)**
103 |
104 | ### Модуль Component
105 |
106 | Модуль Component - это контейнер с ссылками на все переиспользуемые компоненты. Смотрите ниже как мы импортируем `ComponentsModule` и внедряем его в Root модуль, таким образом мы получили единое место для импорта всех компонентов в приложение. Эти модули не связаны с другими модулями и это дает нам возможность переиспользовать их легко в других приложениях.
107 |
108 | ```js
109 | import angular from 'angular';
110 | import CalendarModule from './calendar/calendar.module';
111 | import EventsModule from './events/events.module';
112 |
113 | const ComponentsModule = angular
114 | .module('app.components', [
115 | CalendarModule,
116 | EventsModule
117 | ])
118 | .name;
119 |
120 | export default ComponentsModule;
121 | ```
122 |
123 | **[Наверх](#содержание)**
124 |
125 | ### Модуль Common
126 |
127 | Модуль Common - это контейнер для всех компонентов с логикой для вашего приложения, эти компоненты мы не будем переиспользовать в другом приложении. Например, это могут быть вещи такие как: layout, навигация и footers. Смотрите далее как мы импортируем `CommonModule` и внедряем его в Root модуль, это дает нам возможность импортировать все наши common компоненты в одном месте.
128 |
129 | ```js
130 | import angular from 'angular';
131 | import NavModule from './nav/nav.module';
132 | import FooterModule from './footer/footer.module';
133 |
134 | const CommonModule = angular
135 | .module('app.common', [
136 | NavModule,
137 | FooterModule
138 | ])
139 | .name;
140 |
141 | export default CommonModule;
142 | ```
143 |
144 | **[Наверх](#содержание)**
145 |
146 | ### Низкоуровневые модули
147 |
148 | Низкоуровневые модули - это отдельные компонентные модули которые содержат логику для конкретного блока в приложении, для фичи. Мы определяем модуль, который далее будет импортирован в выше стоящий модуль, такой как component или common модуль, смотрите пример ниже. Помните всегда добавлять суффикс `.name` к каждому `export` когда создаете _новый_ модуль, а не когда ссылаетесь на модуль. Так же вы можете заметить определение маршрутизации в примере, мы рассмотрим этот момент более детально в следующих главах этого руководства.
149 |
150 | ```js
151 | import angular from 'angular';
152 | import uiRouter from 'angular-ui-router';
153 | import CalendarComponent from './calendar.component';
154 |
155 | const CalendarModule = angular
156 | .module('calendar', [
157 | uiRouter
158 | ])
159 | .component('calendar', CalendarComponent)
160 | .config(($stateProvider, $urlRouterProvider) => {
161 | $stateProvider
162 | .state('calendar', {
163 | url: '/calendar',
164 | component: 'calendar'
165 | });
166 | $urlRouterProvider.otherwise('/');
167 | })
168 | .name;
169 |
170 | export default CalendarModule;
171 | ```
172 |
173 | **[Наверх](#содержание)**
174 |
175 | ### Конвенция наименования файлов
176 |
177 | Используйте простые имена в нижнем регистре, используйте имя компонента, например `calendar.*.js*`, `calendar-grid.*.js` - вместе с типом файла в середине. Используйте `*.module.js` как точка входа в модуль, так что вы сможете импортировать модуль используя имя директории.
178 |
179 | ```
180 | calendar.module.js
181 | calendar.controller.js
182 | calendar.component.js
183 | calendar.service.js
184 | calendar.directive.js
185 | calendar.filter.js
186 | calendar.spec.js
187 | ```
188 |
189 | **[Наверх](#содержание)**
190 |
191 | ### Масштабируемая структура файлов
192 |
193 | Структура файлов проекта очень важная часть, эта секция описывает масштабируемую и предсказуемую структуру. Ниже показан пример файловой структуры для модульной компонентной архитектуры.
194 |
195 | ```
196 | ├── app/
197 | │ ├── components/
198 | │ │ ├── calendar/
199 | │ │ │ ├── calendar.module.js
200 | │ │ │ ├── calendar.controller.js
201 | │ │ │ ├── calendar.component.js
202 | │ │ │ ├── calendar.service.js
203 | │ │ │ ├── calendar.spec.js
204 | │ │ │ └── calendar-grid/
205 | │ │ │ ├── calendar-grid.module.js
206 | │ │ │ ├── calendar-grid.controller.js
207 | │ │ │ ├── calendar-grid.component.js
208 | │ │ │ ├── calendar-grid.directive.js
209 | │ │ │ ├── calendar-grid.filter.js
210 | │ │ │ └── calendar-grid.spec.js
211 | │ │ ├── events/
212 | │ │ │ ├── events.module.js
213 | │ │ │ ├── events.controller.js
214 | │ │ │ ├── events.component.js
215 | │ │ │ ├── events.directive.js
216 | │ │ │ ├── events.service.js
217 | │ │ │ ├── events.spec.js
218 | │ │ │ └── events-signup/
219 | │ │ │ ├── events-signup.module.js
220 | │ │ │ ├── events-signup.controller.js
221 | │ │ │ ├── events-signup.component.js
222 | │ │ │ ├── events-signup.service.js
223 | │ │ │ └── events-signup.spec.js
224 | │ │ └── components.module.js
225 | │ ├── common/
226 | │ │ ├── nav/
227 | │ │ │ ├── nav.module.js
228 | │ │ │ ├── nav.controller.js
229 | │ │ │ ├── nav.component.js
230 | │ │ │ ├── nav.service.js
231 | │ │ │ └── nav.spec.js
232 | │ │ ├── footer/
233 | │ │ │ ├── footer.module.js
234 | │ │ │ ├── footer.controller.js
235 | │ │ │ ├── footer.component.js
236 | │ │ │ ├── footer.service.js
237 | │ │ │ └── footer.spec.js
238 | │ │ └── common.module.js
239 | │ ├── app.module.js
240 | │ └── app.component.js
241 | └── index.html
242 | ```
243 |
244 | В корне проекта находится `index.html` и `app/`, директория в которой находятся все наши root, component, common и низкоуровневые модули.
245 |
246 | **[BНаверх](#содержание)**
247 |
248 | # Компоненты
249 |
250 | ### Теория о компонентах
251 |
252 | Проще говоря, Компоненты - это шаблоны с контроллером. Компоненты _не являются_ директивами, и вы не должны заменять директивы компонентами, до тех пор пока вы не столкнулись с ситуацией когда надо улучшить шиблон директивы и добавить контроллер, которые лучше использовать вместе с компонентами. Компоненты так же имеют "связки" которые определяют направления для данных и событий, хуки и возможность использовать односторонний обмен данными и объекты события, для отправки данных обратно в родительский компонент. Это новый стандарт _по умолчанию_ начиная с AngularJS 1.5 и ниже. Все что имеет шаблон и контроллер, с уверенностью на 99%, должно быть компонентом, который может быть stateful, stateless(не зависимый от состояния) или маршрутизатором. Думайте о "компоненте", как о полноценном куске кода, не только как определение объекта `.component()`. Давайте рассмотрим лучшие практики и советы по использованию компонентов, и далее копнем глубже, чтобы понять как вы могли бы использовать компоненты как stateful, stateless и маршрутизатор.
253 |
254 | **[Наверз](#содержание)**
255 |
256 | ### Поддерживаемые свойства
257 |
258 | Ниже приведен список поддерживаемых свойст у `.component()` который вам следуем использовать:
259 |
260 | | Свойство | Поддержка |
261 | |---|---|
262 | | bindings | Да, используйте только `'@'`, `'<'`, `'&'` |
263 | | controller | Да |
264 | | controllerAs | Да, по умолчанию это `$ctrl` |
265 | | require | Да (new Object синтакс) |
266 | | template | Да |
267 | | templateUrl | Да |
268 | | transclude | Да |
269 |
270 | **[Наверх](#содержание)**
271 |
272 | ### Контроллеры
273 |
274 | Контролеры должны использоваться вместе с компонентами, и больше нигде. Если вы видите, что вам нужен контроллер, знайте, вам на самом деле нужен stateless компонент, чтобы описать то или иное свецифическое поведение.
275 |
276 | Далее вы найдете некоторые советы по использованию `Class` для контроллеров:
277 |
278 | * Всегда используйте `constructor` если у вас есть внешние зависимости
279 | * Не экспортируйте `Class` напрямую, экспортируйте имя для использования с`$inject` аннотациями
280 | * Если вам необходимо достучаться до lexical scope, используйте arrow функции
281 | * Как альтернатива для arrow функций, `let ctrl = this;` так же приемлемо и может быть более прозрачным в использовании
282 | * Определите все публичные функции прямо в `Class`
283 | * Используйте необходимые lifecycle хуки, `$onInit`, `$onChanges`, `$postLink` и `$onDestroy`
284 | * Внимание: `$onChanges` вызывается до `$onInit`, смотрите статью с дисскучией на эту тему в секции [ресурсы](#resources)
285 | * Используйте `require` вместе с `$onInit` для ссылок на внутреннию/наследованную логику
286 | * Не переопределяйте алиас `$ctrl` если используете `controllerAs` синтакс, но лучше забудьте про `controllerAs`
287 |
288 | **[Наверх](#содержание)**
289 |
290 | ### События и Односторонний обмен данными
291 |
292 | Односторонний обмен данными появился в AngularJS 1.5, и переопределяет то как взаимодействуют компоненты.
293 |
294 | Далее приведен список советов по использованию одностороннего обмена данными:
295 |
296 | * В компонентах которые получают данные, всегда используйте синтаксис одностороннего обмена данными `'<'`
297 | * _Не_ используйте синтакс двухстороннего обмена данными`'='` больше нигде и никогда
298 | * Компоненты в которых используется `bindings` должны использовать `$onChanges` и клонировать переданные данные путем одностороннего обмена данными, для удаления
299 | ссылки на переданные объекты и таким образом предвидеть обновление переданного объекта в родительском скопе.
300 | * Используйте `$event` как аргумент функции в родительском методе (смотрите пример stateful компонента ниже `$ctrl.addTodo($event)`)
301 | * Возвращайте `$event: {}` объект обратно из stateless компонента (смотрите пример stateless компонента ниже `this.onAddTodo`).
302 | * Бонус: Используйте `EventEmitter` обертку вместе с `.value()` прямо как в Angular, что позволит избежать ручного создания `$event` объекта
303 | * Почему? Потому что так же сделано в Angular и это позволяет сохранить консистентность компонентов. Так же это позволяет предсказать состояние.
304 |
305 | **[Наверх](#содержание)**
306 |
307 | ### Stateful компоненты
308 |
309 | Давайте определим, что такое "stateful компонент".
310 |
311 | * Получает состояние, в большинстве случаем через обзение с серверным API через сервис
312 | * Напрямую не изменяет состояние
313 | * Отрисовывает нижестоящие компоненты которые изменяют состояние
314 | * Так же может называться умным компонентом или компонентом контейнером
315 |
316 | Пример stateful компонента, вместе с определением низкоуровневого модуля (этот пример создан только для демонстрации, и лишний код был убран для краткости):
317 |
318 | ```js
319 | /* ----- todo/todo.component.js ----- */
320 | import controller from './todo.controller';
321 |
322 | const TodoComponent = {
323 | controller,
324 | template: `
325 |
326 |
329 |
331 |
332 | `
333 | };
334 |
335 | export default TodoComponent;
336 |
337 | /* ----- todo/todo.controller.js ----- */
338 | class TodoController {
339 | constructor(TodoService) {
340 | this.todoService = TodoService;
341 | }
342 | $onInit() {
343 | this.newTodo = {
344 | title: '',
345 | selected: false
346 | };
347 | this.todos = [];
348 | this.todoService.getTodos().then(response => this.todos = response);
349 | }
350 | addTodo({ todo }) {
351 | if (!todo) return;
352 | this.todos.unshift(todo);
353 | this.newTodo = {
354 | title: '',
355 | selected: false
356 | };
357 | }
358 | }
359 |
360 | TodoController.$inject = ['TodoService'];
361 |
362 | export default TodoController;
363 |
364 | /* ----- todo/todo.module.js ----- */
365 | import angular from 'angular';
366 | import TodoComponent from './todo.component';
367 |
368 | const TodoModule = angular
369 | .module('todo', [])
370 | .component('todo', TodoComponent)
371 | .name;
372 |
373 | export default TodoModule;
374 | ```
375 |
376 | Этот пример показывает stateful компонент, который получает состояние внутри контроллера, с помощью сервиса, и передает изменения далее в ниже стоящие stateless компоненты. Заметьте как мы обошлись без использования директив, таких как `ng-repeat` и его друзей внутри шаблона. Вместо этого, данные и функции переданы в `` и `` - stateless компоненты.
377 |
378 | **[Наверх](#содержание)**
379 |
380 | ### Stateless компоненты
381 |
382 | Давайте определим, что такое "stateless компонент".
383 |
384 | * Имеет определение точек входа и выхода данных через `bindings: {}`
385 | * Данные поступают в компонент через аттрибуты (inputs)
386 | * Данные уходят из компонента путем событий (outputs)
387 | * Изменяет состояние, передает данные обратно вверх по запросу (к примеру клик или событие сабмита формы)
388 | * Ему все равно откуда пришли данные, этот компонент stateless
389 | * Такой компонент является переиспользуемым, можно использовать в других приложениях
390 | * Так же известен как простой компонент или компонент вида(вьюшки, презентации)
391 |
392 | Пример stateless компонента (давайте возьмем `` как пример), вместе с низкоуровневым определением модуля (этот пример создан только для демонстрации, и лишний код был убран для краткости):
393 |
394 | ```js
395 | /* ----- todo/todo-form/todo-form.component.js ----- */
396 | import controller from './todo-form.controller';
397 |
398 | const TodoFormComponent = {
399 | bindings: {
400 | todo: '<',
401 | onAddTodo: '&'
402 | },
403 | controller,
404 | template: `
405 |
409 | `
410 | };
411 |
412 | export default TodoFormComponent;
413 |
414 | /* ----- todo/todo-form/todo-form.controller.js ----- */
415 | class TodoFormController {
416 | constructor(EventEmitter) {
417 | this.EventEmitter = EventEmitter;
418 | }
419 | $onChanges(changes) {
420 | if (changes.todo) {
421 | this.todo = Object.assign({}, this.todo);
422 | }
423 | }
424 | onSubmit() {
425 | if (!this.todo.title) return;
426 | // с EventEmitter оберткой
427 | this.onAddTodo(
428 | this.EventEmitter({
429 | todo: this.todo
430 | })
431 | );
432 | // без EventEmitter обертки
433 | this.onAddTodo({
434 | $event: {
435 | todo: this.todo
436 | }
437 | });
438 | }
439 | }
440 |
441 | TodoFormController.$inject = ['EventEmitter'];
442 |
443 | export default TodoFormController;
444 |
445 | /* ----- todo/todo-form/todo-form.module.js ----- */
446 | import angular from 'angular';
447 | import TodoFormComponent from './todo-form.component';
448 |
449 | const TodoFormModule = angular
450 | .module('todo.form', [])
451 | .component('todoForm', TodoFormComponent)
452 | .value('EventEmitter', payload => ({ $event: payload}))
453 | .name;
454 |
455 | export default TodoFormModule;
456 | ```
457 |
458 | Обратите внимание как `` компонент использует состояние просто получив его, изменяет Object путем логики контроллера ассоциированной с ним, и передает обратно в родительский компонент через свойство в bindings. В данном примере `$onChanges` хук клонирует `this.todo` Object и переприсваивает его. С помощью такого финта мы убираем ссылку на объект в родительском компоненте, чтобы случайно не обновить его. Мы используем синтаксис одностороннего обмена данным `'<'`.
459 |
460 | **[Наверх](#содержание)**
461 |
462 | ### Компоненты маршрутизации
463 |
464 | Давайте определим, что такое "routed component".
465 |
466 | * В целом это stateful компонент, с определением маршрутов/навигации
467 | * Забудьте про `router.js` файлы
468 | * В компонентах маршрутизации мы можем писать логику связанную с навигацией и маршрутизацией
469 | * Данные поступают в компонент через route resolve (опционально, можно продолжить использовать сервисы в контроллере)
470 |
471 | Для примера мы возьмем `` компонент, изменим его добавив маршруты и `bindings` для компонентов которые получают данные (фишка с `ui-router` в использовании свойства `resolve`, в нашем примере `todoData` напрямую передается через `bindings`). Мы называем этот компонент маршрутизатором потому, что по существу это "view":
472 |
473 | ```js
474 | /* ----- todo/todo.component.js ----- */
475 | import controller from './todo.controller';
476 |
477 | const TodoComponent = {
478 | bindings: {
479 | todoData: '<'
480 | },
481 | controller,
482 | template: `
483 |
484 |
487 |
489 |
490 | `
491 | };
492 |
493 | export default TodoComponent;
494 |
495 | /* ----- todo/todo.controller.js ----- */
496 | class TodoController {
497 | constructor() {}
498 | $onInit() {
499 | this.newTodo = {
500 | title: '',
501 | selected: false
502 | };
503 | }
504 | $onChanges(changes) {
505 | if (changes.todoData) {
506 | this.todos = Object.assign({}, this.todoData);
507 | }
508 | }
509 | addTodo({ todo }) {
510 | if (!todo) return;
511 | this.todos.unshift(todo);
512 | this.newTodo = {
513 | title: '',
514 | selected: false
515 | };
516 | }
517 | }
518 |
519 | export default TodoController;
520 |
521 | /* ----- todo/todo.service.js ----- */
522 | class TodoService {
523 | constructor($http) {
524 | this.$http = $http;
525 | }
526 | getTodos() {
527 | return this.$http.get('/api/todos').then(response => response.data);
528 | }
529 | }
530 |
531 | TodoService.$inject = ['$http'];
532 |
533 | export default TodoService;
534 |
535 | /* ----- todo/todo.module.js ----- */
536 | import angular from 'angular';
537 | import uiRouter from 'angular-ui-router';
538 | import TodoComponent from './todo.component';
539 | import TodoService from './todo.service';
540 |
541 | const TodoModule = angular
542 | .module('todo', [
543 | uiRouter
544 | ])
545 | .component('todo', TodoComponent)
546 | .service('TodoService', TodoService)
547 | .config(($stateProvider, $urlRouterProvider) => {
548 | $stateProvider
549 | .state('todos', {
550 | url: '/todos',
551 | component: 'todo',
552 | resolve: {
553 | todoData: TodoService => TodoService.getTodos()
554 | }
555 | });
556 | $urlRouterProvider.otherwise('/');
557 | })
558 | .name;
559 |
560 | export default TodoModule;
561 | ```
562 |
563 | **[Наверх](#содержание)**
564 |
565 | # Директивы
566 |
567 | ### Теория о директивах
568 |
569 | Директивы дают нам возможность использовать `template`, `scope` связки, `bindToController`, `link` и множество других вещей. Использование всех этих свойств должно быть тщательно обдуманно, так как теперь есть `.component()`. Директивы больше не должны определять шаблоны и контроллеры или получать данные через bindings. Директивы должны быть использованы теперь только для декорирования DOM. Т.е. расширение существующего HTML - созданного с помощью `.component()`. Проще говоря, если вам нужны специфические DOM события/API и логика, используйте директиву и присоедините ее внутри шаблона который используется в компоненте. Если вам нужно внушительное количество манипуляций с DOM, используйте `$postLink` хук, но помните, что это не то место куда надо перенести все ваши манипуляции с DOM. Используйте директиву, если можете, для не-Angular фишек.
570 |
571 | Список некоторых полезных советов по использованию директив:
572 |
573 | * Больше никогда не используйте templates, scope, bindToController или controllers
574 | * Испоьзуйте всегда `restrict: 'A'` вместе с директивами
575 | * Используйте compile и link где необходимо
576 | * Помните destroy и unbind обрабочики событий внутри `$scope.$on('$destroy', fn);`
577 |
578 | **[Наверх](#содержание)**
579 |
580 | ### Рекомендуемые свойства
581 |
582 | Из-за того что директивы поддерживают большинство свойст из `.component()` (шаблон директивы был изначально компонентом), я рекомендую ограничить стек свойст директивы для использования, чтобы использовать директивы правильно:
583 |
584 | | Свойство | Использовать? | Почему |
585 | |---|---|---|
586 | | bindToController | Нет | Используйте `bindings` в компонентах |
587 | | compile | Да | Для pre-compile DOM манипуляций и событий |
588 | | controller | Нет | Используйте компонент |
589 | | controllerAs | Нет | Используйте компонент |
590 | | link functions | Да | Для pre/post DOM манипуляций и событий |
591 | | multiElement | Да | [Смотрите документацию](https://docs.angularjs.org/api/ng/service/$compile#-multielement-) |
592 | | priority | Да | [Смотрите документацию](https://docs.angularjs.org/api/ng/service/$compile#-priority-) |
593 | | require | Нет | Используйте компонент |
594 | | restrict | Да | Определяет как будет использоваться директива, всегда используйте `'A'` |
595 | | scope | Нет | Используйте компонент |
596 | | template | Нет | Используйте компонент |
597 | | templateNamespace | Да (если не обойтись уж никак) | [Смотрите документацию](https://docs.angularjs.org/api/ng/service/$compile#-templatenamespace-) |
598 | | templateUrl | Нет | Используйте компонент |
599 | | transclude | Нет | Используйте компонент |
600 |
601 | **[Наверх](#содержание)**
602 |
603 | ### Константы или Классы
604 |
605 | Существует несколько путей совместного использования ES2015 и директив, с помощью arrow функций и легкого назначения, или используя ES2015 `Class`. Выберите то, что лучше всего подходит вам и вашей команде, но помните, что Angular использует `Class`. Далее рассмотрим пример использования констант с arrow функцией, обертка выражения `() => ({})` возвращает Object литерал (обратите внимание на разницу в использовании внутри `.directive()`):
606 |
607 | ```js
608 | /* ----- todo/todo-autofocus.directive.js ----- */
609 | import angular from 'angular';
610 |
611 | const TodoAutoFocus = ($timeout) => ({
612 | restrict: 'A',
613 | link($scope, $element, $attrs) {
614 | $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => {
615 | if (!newValue) {
616 | return;
617 | }
618 | $timeout(() => $element[0].focus());
619 | });
620 | }
621 | });
622 |
623 | TodoAutoFocus.$inject = ['$timeout'];
624 |
625 | export default TodoAutoFocus;
626 |
627 | /* ----- todo/todo.module.js ----- */
628 | import angular from 'angular';
629 | import TodoComponent from './todo.component';
630 | import TodoAutofocus from './todo-autofocus.directive';
631 |
632 | const TodoModule = angular
633 | .module('todo', [])
634 | .component('todo', TodoComponent)
635 | .directive('todoAutofocus', TodoAutoFocus)
636 | .name;
637 |
638 | export default TodoModule;
639 | ```
640 |
641 | Или с использование ES2015 ключего слова `Class` (обратите внимание на ручное создание объекта с помощью `new TodoAutoFocus` когда регистрируем директиву) для создания объекта:
642 |
643 | ```js
644 | /* ----- todo/todo-autofocus.directive.js ----- */
645 | import angular from 'angular';
646 |
647 | class TodoAutoFocus {
648 | constructor($timeout) {
649 | this.restrict = 'A';
650 | this.$timeout = $timeout;
651 | }
652 | link($scope, $element, $attrs) {
653 | $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => {
654 | if (!newValue) {
655 | return;
656 | }
657 | this.$timeout(() => $element[0].focus());
658 | });
659 | }
660 | }
661 |
662 | TodoAutoFocus.$inject = ['$timeout'];
663 |
664 | export default TodoAutoFocus;
665 |
666 | /* ----- todo/todo.module.js ----- */
667 | import angular from 'angular';
668 | import TodoComponent from './todo.component';
669 | import TodoAutofocus from './todo-autofocus.directive';
670 |
671 | const TodoModule = angular
672 | .module('todo', [])
673 | .component('todo', TodoComponent)
674 | .directive('todoAutofocus', ($timeout) => new TodoAutoFocus($timeout))
675 | .name;
676 |
677 | export default TodoModule;
678 | ```
679 |
680 | **[Наверх](#содержание)**
681 |
682 | # Сервисы
683 |
684 | ### Теория о сервисах
685 |
686 | Сервисы по существу это контейнеры для бизнес логики, которую наши компоненты не должны запрашивать/использовать напрямую. Сервисы используют другие встроенные или внешние сервисы, например `$http`, которые мы можем внедрить в контроллер компонентов где угодно в нашем приложении. Существуют два пути создания сервисов, используя `.service()` или `.factory()`. Используя ES2015 ключевое слово `Class`, мы должны использовать только `.service()`, вместе с dependency injection аннотацией используя `$inject`.
687 |
688 | **[Назад](#содержание)**
689 |
690 | ### Используем классы в сервисе
691 |
692 | Ниже приведен пример нашего `` используя ES2015 ключевое слово`Class`:
693 |
694 | ```js
695 | /* ----- todo/todo.service.js ----- */
696 | class TodoService {
697 | constructor($http) {
698 | this.$http = $http;
699 | }
700 | getTodos() {
701 | return this.$http.get('/api/todos').then(response => response.data);
702 | }
703 | }
704 |
705 | TodoService.$inject = ['$http'];
706 |
707 | export default TodoService;
708 |
709 | /* ----- todo/todo.module.js ----- */
710 | import angular from 'angular';
711 | import TodoComponent from './todo.component';
712 | import TodoService from './todo.service';
713 |
714 | const TodoModule = angular
715 | .module('todo', [])
716 | .component('todo', TodoComponent)
717 | .service('TodoService', TodoService)
718 | .name;
719 |
720 | export default TodoModule;
721 | ```
722 |
723 | **[Наверх](#содержание)**
724 |
725 | # ES2015 и Утилиты
726 |
727 | ##### ES2015
728 |
729 | * Используйте [Babel](https://babeljs.io/) чтобы скомпилировать ES2015+ код и любые полифилы
730 | * Не стесняйтесь использовать [TypeScript](http://www.typescriptlang.org/) чтобы легче прошел переход на Angular
731 |
732 | ##### Утилиты
733 | * Используйте `ui-router` [последняя alpha](https://github.com/angular-ui/ui-router) (смотрите Readme) если вы хотите использовать компонентную маршрутизацию
734 | * В другом случае вы застряли с `template: ''` без `bindings`
735 | * Начинайте использовать [Webpack](https://webpack.github.io/) для компиляции вашего ES2015 кода
736 | * Используйте [ngAnnotate](https://github.com/olov/ng-annotate) для автоматической аннотации `$inject` свойств
737 | * Как использовать [ngAnnotate вместе с ES6](https://www.timroes.de/2015/07/29/using-ecmascript-6-es6-with-angularjs-1-x/#ng-annotate)
738 |
739 | **[Наверх](#содержание)**
740 |
741 | # Управление состоянием
742 |
743 | Начните использовать Redux вместе с AngularJS 1.5 для управления данными.
744 |
745 | * [Angular Redux](https://github.com/angular-redux/ng-redux)
746 |
747 | **[Назад](#содержание)**
748 |
749 | # Ресурсы
750 |
751 | * [Объяснение метода .component()](https://toddmotto.com/exploring-the-angular-1-5-component-method/)
752 | * [Использование "require" вместе с $onInit](https://toddmotto.com/on-init-require-object-syntax-angular-component/)
753 | * [Объяснение всех lifecycle хуков, $onInit, $onChanges, $postLink, $onDestroy](https://toddmotto.com/angular-1-5-lifecycle-hooks)
754 | * [Использование "resolve" в маршрутах](https://toddmotto.com/resolve-promises-in-angular-routes/)
755 | * [Управление состоянием в Angular с помощью Redux](http://blog.rangle.io/managing-state-redux-angular/)
756 | * [Sample Application from Community](https://github.com/chihab/angular-styleguide-sample)
757 |
758 | **[Наверх](#содержание)**
759 |
760 | # Документация
761 | Все что было упущено, можно найти в [AngularJS документации](//docs.angularjs.org/api).
762 |
763 | # Помощь проекту
764 |
765 | Создайте issue, чтобы обсудить возможные изменения/добавления. Пожалуйста, не создавайте issue ради вопросов.
766 |
767 | ## Лицензия
768 |
769 | #### (The MIT License)
770 |
771 | Copyright (c) 2016 Todd Motto
772 |
773 | Permission is hereby granted, free of charge, to any person obtaining
774 | a copy of this software and associated documentation files (the
775 | 'Software'), to deal in the Software without restriction, including
776 | without limitation the rights to use, copy, modify, merge, publish,
777 | distribute, sublicense, and/or sell copies of the Software, and to
778 | permit persons to whom the Software is furnished to do so, subject to
779 | the following conditions:
780 |
781 | The above copyright notice and this permission notice shall be
782 | included in all copies or substantial portions of the Software.
783 |
784 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
785 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
786 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
787 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
788 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
789 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
790 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
791 |
--------------------------------------------------------------------------------
/i18n/zh-cn.md:
--------------------------------------------------------------------------------
1 | # AngularJS 编码风格指南(ES2015)
2 |
3 | ### 架构, 文件结构, 组件, 单向数据流以及最佳实践
4 |
5 | *来自 [@toddmotto](//twitter.com/toddmotto) 团队的编码指南*
6 |
7 | Angular 的编码风格以及架构已经使用ES2015进行重写,这些在AngularJS 1.5+的变化可以更好帮助您的更好的升级到Angular2.。
8 | 这份指南包括了新的单向数据流,事件委托,组件架构和组件路由。
9 |
10 | 老版本的指南你可以在[这里](https://github.com/toddmotto/angular-styleguide/tree/angular-old-es5)找到, 在[这里](https://toddmotto.com/rewriting-angular-styleguide-angular-2)你能看到最新的.
11 |
12 | > 加入我们无与伦比的angularjs学习体验中来,构建更加快速和易于扩展的App.
13 |
14 |
15 |
16 | ## 目录
17 |
18 | 1. [模块架构](#module-architecture)
19 | 1. [基本概念](#module-theory)
20 | 1. [根 模块](#root-module)
21 | 1. [组件模块](#component-module)
22 | 1. [公共模块](#common-module)
23 | 1. [低级别模块](#low-level-modules)
24 | 1. [可扩展的文件结构](#scalable-file-structure)
25 | 1. [文件命名规范](#file-naming-conventions)
26 | 1. [组件](#components)
27 | 1. [基本概念](#component-theory)
28 | 1. [支持的属性](#supported-properties)
29 | 1. [控制器](#controllers)
30 | 1. [单向数据流和事件](#one-way-dataflow-and-events)
31 | 1. [状态组件](#stateful-components)
32 | 1. [无状态组件](#stateless-components)
33 | 1. [路由组件](#routed-components)
34 | 1. [指令](#directives)
35 | 1. [基本概念](#directive-theory)
36 | 1. [建议的属性](#recommended-properties)
37 | 1. [常量和类](#constants-or-classes)
38 | 1. [服务](#services)
39 | 1. [基本概念](#service-theory)
40 | 1. [服务](#classes-for-service)
41 | 1. [ES2015 和工具推荐](#es2015-and-tooling)
42 | 1. [状态管理](#state-management)
43 | 1. [资源](#resources)
44 | 1. [文档](#documentation)
45 | 1. [贡献](#contributing)
46 |
47 | # Modular architecture
48 |
49 | Angular 中的每一个模块都是一个模块组件。一个模块组件囊括了逻辑,模版,路由和子组件。
50 |
51 | ### Module 基本概念
52 |
53 | 在模块的设计直接反映到我们的文件夹结构,从而保证我们项目的可维护性和可预测性。
54 | 我们最好应该有三个高层次的模块:根,组件和常用模块。根模块定义了用于启动App和相应的模板的基本架子。
55 | 然后,我们导入需要依赖的组件和通用模块。组件和通用模块然后需要低级别的组件模块,其中包含我们的组件,控制器,服务,指令,过滤器和给可重复使用的功能进行测试。
56 |
57 | **[返回顶部](#table-of-contents)**
58 |
59 | ### 根模块
60 |
61 | 根模块会启动一个根组件,整个组件主要定义了整个应用的基本的元素和路由出口,例如使用`ui-view`和`ui-router`。
62 |
63 | ```js
64 | // app.component.js
65 | const AppComponent = {
66 | template: `
67 |
68 | Hello world
69 |
70 |
500 | `
501 | };
502 |
503 | export default TodoComponent;
504 |
505 | /* ----- todo/todo.controller.js ----- */
506 | class TodoController {
507 | constructor() {}
508 | $onInit() {
509 | this.newTodo = {
510 | title: '',
511 | selected: false
512 | };
513 | }
514 | $onChanges(changes) {
515 | if (changes.todoData) {
516 | this.todos = Object.assign({}, this.todoData);
517 | }
518 | }
519 | addTodo({ todo }) {
520 | if (!todo) return;
521 | this.todos.unshift(todo);
522 | this.newTodo = {
523 | title: '',
524 | selected: false
525 | };
526 | }
527 | }
528 |
529 | export default TodoController;
530 |
531 | /* ----- todo/todo.service.js ----- */
532 | class TodoService {
533 | constructor($http) {
534 | this.$http = $http;
535 | }
536 | getTodos() {
537 | return this.$http.get('/api/todos').then(response => response.data);
538 | }
539 | }
540 |
541 | TodoService.$inject = ['$http'];
542 |
543 | export default TodoService;
544 |
545 | /* ----- todo/todo.module.js ----- */
546 | import angular from 'angular';
547 | import uiRouter from 'angular-ui-router';
548 | import TodoComponent from './todo.component';
549 | import TodoService from './todo.service';
550 |
551 | const TodoModule = angular
552 | .module('todo', [
553 | uiRouter
554 | ])
555 | .component('todo', TodoComponent)
556 | .service('TodoService', TodoService)
557 | .config(($stateProvider, $urlRouterProvider) => {
558 | $stateProvider
559 | .state('todos', {
560 | url: '/todos',
561 | component: 'todo',
562 | resolve: {
563 | todoData: TodoService => TodoService.getTodos()
564 | }
565 | });
566 | $urlRouterProvider.otherwise('/');
567 | })
568 | .name;
569 |
570 | export default TodoModule;
571 | ```
572 |
573 | **[返回目录](#table-of-contents)**
574 |
575 | # 指令
576 |
577 | ### 基本概念
578 |
579 | 指令给予了我们的模板,scope ,与控制器绑定,链接和许多其他的事情。这些的使用使我们慎重考虑 `.component()`的存在。指令不应在声明模板和控制器了,或者通过绑定接收数据。指令应该仅仅是为了装饰DOM使用。这样,就意味着扩展现有的HTML - 如果用`.component()`创建。简而言之,如果你需要自定义DOM事件/ API和逻辑,使用一个指令并将其绑定到一个组件内的模板。如果你需要的足够的数量的 DOM变化,`postLink`生命周期钩子值得考虑,但是这并不是迁移所有的的DOM操作。你可以给一个无需Angular的地方使用directive
580 |
581 | 使用指令的小建议:
582 |
583 | * 不要使用模板 ,scope,控制器
584 | * 一直设置 `restrict: 'A'`
585 | * 在需要的地方使用 `compile` and `link`
586 | * 记得 `$scope.$on('$destroy', fn) 进行销毁和事件解除;`
587 |
588 |
589 |
590 | **[返回目录](#table-of-contents)**
591 |
592 | ### 推荐的属性
593 |
594 | 由于指令支持了大多数 `.component()` 的语法 (模板指令就是最原始的组件), 建议限制指令中的的 Object,以及避免使用错误的指令方法。
595 |
596 | | Property | Use it? | Why |
597 | |---|---|---|
598 | | bindToController | No | 在组件中使用 `bindings` |
599 | | compile | Yes | 预编译 DOM 操作/事件 |
600 | | controller | No | 使用一个组件 |
601 | | controllerAs | No | 使用一个组件 |
602 | | link functions | Yes | 对于 DOM操作/事件 的前后|
603 | | multiElement | Yes | [文档](https://docs.angularjs.org/api/ng/service/$compile#-multielement-) |
604 | | priority | Yes | [文档](https://docs.angularjs.org/api/ng/service/$compile#-priority-) |
605 | | require | No | 使用一个组件 |
606 | | restrict | Yes | 定义一个组件并使用 `A` |
607 | | scope | No | 使用一个组件 |
608 | | template | No | 使用一个组件 |
609 | | templateNamespace | Yes (if you must) | [See docs](https://docs.angularjs.org/api/ng/service/$compile#-templatenamespace-) |
610 | | templateUrl | No | 使用一个组件|
611 | | transclude | No | 使用一个组件 |
612 |
613 | **[返回目录](#table-of-contents)**
614 |
615 | ### 常量 和 类
616 |
617 | 下面有几个使用es2015和指令的方法,无论是带有箭头函数,更容易的操作,或使用ES2015`Class`。记住选择最适合自己或者团队的方法,并且记住 Angular 中使用 Class.
618 |
619 |
620 |
621 | 下面是一个恒在箭头函数的表达式`()=>({})`使用常量的例子,它返回一个对象面(注意里面与`.directive`的使用差异()):
622 |
623 |
624 | ```js
625 | /* ----- todo/todo-autofocus.directive.js ----- */
626 | import angular from 'angular';
627 |
628 | const TodoAutoFocus = ($timeout) => ({
629 | restrict: 'A',
630 | link($scope, $element, $attrs) {
631 | $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => {
632 | if (!newValue) {
633 | return;
634 | }
635 | $timeout(() => $element[0].focus());
636 | });
637 | }
638 | });
639 |
640 | TodoAutoFocus.$inject = ['$timeout'];
641 |
642 | export default TodoAutoFocus;
643 |
644 | /* ----- todo/todo.module.js ----- */
645 | import angular from 'angular';
646 | import TodoComponent from './todo.component';
647 | import TodoAutofocus from './todo-autofocus.directive';
648 |
649 | const TodoModule = angular
650 | .module('todo', [])
651 | .component('todo', TodoComponent)
652 | .directive('todoAutofocus', TodoAutoFocus)
653 | .name;
654 |
655 | export default TodoModule;
656 | ```
657 |
658 | 或者用ES2015 Class(注意在注册指令时手动调用 `new TodoAutoFocus`)来创建对象:
659 |
660 | ```js
661 | /* ----- todo/todo-autofocus.directive.js ----- */
662 | import angular from 'angular';
663 |
664 | class TodoAutoFocus {
665 | constructor($timeout) {
666 | this.restrict = 'A';
667 | this.$timeout = $timeout;
668 | }
669 | link($scope, $element, $attrs) {
670 | $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => {
671 | if (!newValue) {
672 | return;
673 | }
674 | this.$timeout(() => $element[0].focus());
675 | });
676 | }
677 | }
678 |
679 | TodoAutoFocus.$inject = ['$timeout'];
680 |
681 | export default TodoAutoFocus;
682 |
683 | /* ----- todo/todo.module.js ----- */
684 | import angular from 'angular';
685 | import TodoComponent from './todo.component';
686 | import TodoAutofocus from './todo-autofocus.directive';
687 |
688 | const TodoModule = angular
689 | .module('todo', [])
690 | .component('todo', TodoComponent)
691 | .directive('todoAutofocus', ($timeout) => new TodoAutoFocus($timeout))
692 | .name;
693 |
694 | export default TodoModule;
695 | ```
696 |
697 | **[返回目录](#table-of-contents)**
698 |
699 | # 服务
700 |
701 | ### 基本理论
702 |
703 | 服务本质上是包含业务逻辑的容器,而我们的组件不应该直接进行请求。服务包含其它内置或外部服务,如`$http`,我们可以随时随地的在应用程序注入到组件控制器。我们在开发服务有两种方式,`.service()` 以及 `.factory()`。使用ES2015`Class`,我们应该只使用`.service()`,通过$inject完成依赖注入。
704 |
705 | **[返回目录](#table-of-contents)**
706 |
707 | ### 构建服务Class
708 |
709 | 下面的 `` 就是使用 ES2015 `Class`:
710 |
711 | ```js
712 | /* ----- todo/todo.service.js ----- */
713 | class TodoService {
714 | constructor($http) {
715 | this.$http = $http;
716 | }
717 | getTodos() {
718 | return this.$http.get('/api/todos').then(response => response.data);
719 | }
720 | }
721 |
722 | TodoService.$inject = ['$http'];
723 |
724 | export default TodoService;
725 |
726 | /* ----- todo/todo.module.js ----- */
727 | import angular from 'angular';
728 | import TodoComponent from './todo.component';
729 | import TodoService from './todo.service';
730 |
731 | const TodoModule = angular
732 | .module('todo', [])
733 | .component('todo', TodoComponent)
734 | .service('TodoService', TodoService)
735 | .name;
736 |
737 | export default TodoModule;
738 | ```
739 |
740 | **[返回目录](#table-of-contents)**
741 |
742 | # ES2015 以及相关工具
743 |
744 | ##### ES2015
745 |
746 | * 使用 [Babel](https://babeljs.io/) 将ES2015进行转换为当前浏览器所支持的代码
747 | * 考虑使用 [TypeScript](http://www.typescriptlang.org/) 让你更好的迁移到Angular2
748 |
749 | ##### 工具
750 | * 使用 `ui-router` [latest alpha](https://github.com/angular-ui/ui-router) (查看 Readme) 如果你希望支持路由钻
751 | * 你可能会在 `template: ''` 以及 不需要 `bindings`中遇到一些挫折
752 | * 考虑使用 [Webpack](https://webpack.github.io/) 来编译es2016的代码
753 | * 使用 [ngAnnotate](https://github.com/olov/ng-annotate) 自动完成 `$inject` 属性注入
754 | * 如何使用[ngAnnotate with ES6](https://www.timroes.de/2015/07/29/using-ecmascript-6-es6-with-angularjs-1-x/)
755 |
756 | **[返回目录](#table-of-contents)**
757 |
758 | # 状态管理
759 |
760 | 考虑使用 Redux 用于 数据管理.
761 |
762 | * [Angular Redux](https://github.com/angular-redux/ng-redux)
763 |
764 | **[返回目录](#table-of-contents)**
765 |
766 | # 资源
767 |
768 | * [理解 .component() 方法](https://toddmotto.com/exploring-the-angular-1-5-component-method/)
769 | * [使用 "require" 与 $onInit](https://toddmotto.com/on-init-require-object-syntax-angular-component/)
770 | * [理解生命周期钩子, $onInit, $onChanges, $postLink, $onDestroy](https://toddmotto.com/angular-1-5-lifecycle-hooks)
771 | * [在路由中使用 "resolve"](https://toddmotto.com/resolve-promises-in-angular-routes/)
772 | * [Redux 以及 Angular 状态管理](http://blog.rangle.io/managing-state-redux-angular/)
773 | * [Sample Application from Community](https://github.com/chihab/angular-styleguide-sample)
774 |
775 | **[返回目录](#table-of-contents)**
776 |
777 | # 文档
778 | 关于Angular API [AngularJS documentation](//docs.angularjs.org/api).
779 |
780 | # 贡献
781 |
782 | 打开issues,讨论可能的更改/添加。请不要在issues提任何问题.
783 |
784 | ## License
785 |
786 | #### (The MIT License)
787 |
788 | Copyright (c) 2016 Todd Motto
789 |
790 | Permission is hereby granted, free of charge, to any person obtaining
791 | a copy of this software and associated documentation files (the
792 | 'Software'), to deal in the Software without restriction, including
793 | without limitation the rights to use, copy, modify, merge, publish,
794 | distribute, sublicense, and/or sell copies of the Software, and to
795 | permit persons to whom the Software is furnished to do so, subject to
796 | the following conditions:
797 |
798 | The above copyright notice and this permission notice shall be
799 | included in all copies or substantial portions of the Software.
800 |
801 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
802 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
803 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
804 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
805 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
806 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
807 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
808 |
--------------------------------------------------------------------------------
/typescript/README.md:
--------------------------------------------------------------------------------
1 | # Angular 1.x styleguide (TypeScript)
2 |
3 | ### Architecture, file structure, components, one-way dataflow and best practices
4 |
5 | *A sensible styleguide for teams by [@toddmotto](//twitter.com/toddmotto)*
6 |
7 | This architecture and styleguide has been rewritten from the ground up for ES2015, the changes in Angular 1.5+ for future-upgrading your application to Angular 2. This guide includes new best practices for one-way dataflow, event delegation, component architecture and component routing.
8 |
9 | You can find the old styleguide [here](https://github.com/toddmotto/angular-styleguide/tree/angular-old-es5), and the reasoning behind the new one [here](https://toddmotto.com/rewriting-angular-styleguide-angular-2).
10 |
11 | > Join the Ultimate AngularJS learning experience to fully master beginner and advanced Angular features to build real-world apps that are fast, and scale.
12 |
13 |
14 |
15 | ## Table of Contents
16 |
17 | 1. [Modular architecture](#modular-architecture)
18 | 1. [Theory](#module-theory)
19 | 1. [Root module](#root-module)
20 | 1. [Component module](#component-module)
21 | 1. [Common module](#common-module)
22 | 1. [Low-level modules](#low-level-modules)
23 | 1. [File naming conventions](#file-naming-conventions)
24 | 1. [Scalable file structure](#scalable-file-structure)
25 | 1. [Components](#components)
26 | 1. [Theory](#component-theory)
27 | 1. [Supported properties](#supported-properties)
28 | 1. [Controllers](#controllers)
29 | 1. [One-way dataflow and Events](#one-way-dataflow-and-events)
30 | 1. [Stateful Components](#stateful-components)
31 | 1. [Stateless Components](#stateless-components)
32 | 1. [Routed Components](#routed-components)
33 | 1. [Directives](#directives)
34 | 1. [Theory](#directive-theory)
35 | 1. [Recommended properties](#recommended-properties)
36 | 1. [Constants or Classes](#constants-or-classes)
37 | 1. [Services](#services)
38 | 1. [Theory](#service-theory)
39 | 1. [Classes for Service](#classes-for-service)
40 | 1. [Styles](#styles)
41 | 1. [TypeScript and Tooling](#typescript-and-tooling)
42 | 1. [State management](#state-management)
43 | 1. [Resources](#resources)
44 | 1. [Documentation](#documentation)
45 | 1. [Contributing](#contributing)
46 |
47 | # Modular architecture
48 |
49 | Each module in an Angular app is a module component. A module component is the root definition for that module that encapsulates the logic, templates, routing and child components.
50 |
51 | ### Module theory
52 |
53 | The design in the modules maps directly to our folder structure, which keeps things maintainable and predictable. We should ideally have three high-level modules: root, component and common. The root module defines the base module that bootstraps our app, and the corresponding template. We then import our component and common modules into the root module to include our dependencies. The component and common modules then require lower-level component modules, which contain our components, controllers, services, directives, filters and tests for each reusable feature.
54 |
55 | **[Back to top](#table-of-contents)**
56 |
57 | ### Root module
58 |
59 | A root module begins with a root component that defines the base element for the entire application, with a routing outlet defined, example shown using `ui-view` from `ui-router`.
60 |
61 | ```ts
62 | // app.component.ts
63 | export const AppComponent: angular.IComponentOptions = {
64 | template: `
65 |
66 | Hello world
67 |
68 |
69 |
70 |
71 |
74 | `
75 | };
76 |
77 | ```
78 |
79 | A root module is then created, with `AppComponent` imported and registered with `.component('app', AppComponent)`. Further imports for submodules (component and common modules) are made to include all components relevant for the application. You'll noticed styles are also being imported here, we'll come onto this in later chapters in this guide.
80 |
81 | ```ts
82 | // app.ts
83 | import angular from 'angular';
84 | import uiRouter from 'angular-ui-router';
85 | import { AppComponent } from './app.component';
86 | import { ComponentsModule } from './components/components.module';
87 | import { CommonModule } from './common/common.module';
88 | import './app.scss';
89 |
90 | const root = angular
91 | .module('app', [
92 | ComponentsModule,
93 | CommonModule,
94 | uiRouter
95 | ])
96 | .component('app', AppComponent)
97 | .name;
98 |
99 | export default root;
100 | ```
101 |
102 | **[Back to top](#table-of-contents)**
103 |
104 | ### Component module
105 |
106 | A Component module is the container reference for all reusable components. See above how we import `Components` and inject them into the Root module, this gives us a single place to import all components for the app. These modules we require are decoupled from all other modules and thus can be moved into any other application with ease.
107 |
108 | ```ts
109 | import angular from 'angular';
110 | import { CalendarModule } from './calendar/calendar.module';
111 | import { EventsModule } from './events/events.module';
112 |
113 | export const ComponentsModule = angular
114 | .module('app.components', [
115 | CalendarModule,
116 | EventsModule
117 | ])
118 | .name;
119 |
120 | ```
121 |
122 | **[Back to top](#table-of-contents)**
123 |
124 | ### Common module
125 |
126 | The Common module is the container reference for all application specific components, that we don't want to use in another application. This can be things like layout, navigation and footers. See above how we import `Common` and inject them into the Root module, this gives us a single place to import all common components for the app.
127 |
128 | ```ts
129 | import angular from 'angular';
130 | import { NavModule } from './nav/nav.module';
131 | import { FooterModule } from './footer/footer.module';
132 |
133 | export const CommonModule = angular
134 | .module('app.common', [
135 | NavModule,
136 | FooterModule
137 | ])
138 | .name;
139 |
140 | ```
141 |
142 | **[Back to top](#table-of-contents)**
143 |
144 | ### Low-level modules
145 |
146 | Low-level modules are individual component modules that contain the logic for each feature block. These will each define a module, to be imported to a higher-level module, such as a component or common module, an example below. Always remember to add the `.name` suffix to each `export` when creating a _new_ module, not when referencing one. You'll noticed routing definitions also exist here, we'll come onto this in later chapters in this guide.
147 |
148 | ```ts
149 | import angular from 'angular';
150 | import uiRouter from 'angular-ui-router';
151 | import { CalendarComponent } from './calendar.component';
152 | import './calendar.scss';
153 |
154 | export const CalendarModule = angular
155 | .module('calendar', [
156 | uiRouter
157 | ])
158 | .component('calendar', CalendarComponent)
159 | .config(($stateProvider: angular.ui.IStateProvider,
160 | $urlRouterProvider: angular.ui.IUrlRouterProvider) => {
161 | $stateProvider
162 | .state('calendar', {
163 | url: '/calendar',
164 | component: 'calendar'
165 | });
166 | $urlRouterProvider.otherwise('/');
167 | })
168 | .name;
169 |
170 | ```
171 |
172 | **[Back to top](#table-of-contents)**
173 |
174 | ### File naming conventions
175 |
176 | Keep it simple and lowercase, use the component name, e.g. `calendar.*.ts*`, `calendar-grid.*.ts` - with the name of the type of file in the middle. Use `index.ts` for the module definition file, so you can import the module by directory name.
177 |
178 | ```
179 | calendar.module.ts
180 | calendar.component.ts
181 | calendar.service.ts
182 | calendar.directive.ts
183 | calendar.filter.ts
184 | calendar.spec.ts
185 | calendar.html
186 | calendar.scss
187 | ```
188 |
189 | **[Back to top](#table-of-contents)**
190 |
191 | ### Scalable file structure
192 |
193 | File structure is extremely important, this describes a scalable and predictable structure. An example file structure to illustrate a modular component architecture.
194 |
195 | ```
196 | ├── app/
197 | │ ├── components/
198 | │ │ ├── calendar/
199 | │ │ │ ├── calendar.module.ts
200 | │ │ │ ├── calendar.component.ts
201 | │ │ │ ├── calendar.service.ts
202 | │ │ │ ├── calendar.spec.ts
203 | │ │ │ ├── calendar.html
204 | │ │ │ ├── calendar.scss
205 | │ │ │ └── calendar-grid/
206 | │ │ │ ├── calendar-grid.module.ts
207 | │ │ │ ├── calendar-grid.component.ts
208 | │ │ │ ├── calendar-grid.directive.ts
209 | │ │ │ ├── calendar-grid.filter.ts
210 | │ │ │ ├── calendar-grid.spec.ts
211 | │ │ │ ├── calendar-grid.html
212 | │ │ │ └── calendar-grid.scss
213 | │ │ ├── events/
214 | │ │ │ ├── events.module.ts
215 | │ │ │ ├── events.component.ts
216 | │ │ │ ├── events.directive.ts
217 | │ │ │ ├── events.service.ts
218 | │ │ │ ├── events.spec.ts
219 | │ │ │ ├── events.html
220 | │ │ │ ├── events.scss
221 | │ │ │ └── events-signup/
222 | │ │ │ ├── events-signup.module.ts
223 | │ │ │ ├── events-signup.controller.ts
224 | │ │ │ ├── events-signup.component.ts
225 | │ │ │ ├── events-signup.service.ts
226 | │ │ │ ├── events-signup.spec.ts
227 | │ │ │ ├── events-signup.html
228 | │ │ │ └── events-signup.scss
229 | │ │ └── components.module.ts
230 | │ ├── common/
231 | │ │ ├── nav/
232 | │ │ │ ├── nav.module.ts
233 | │ │ │ ├── nav.component.ts
234 | │ │ │ ├── nav.service.ts
235 | │ │ │ ├── nav.spec.ts
236 | │ │ │ ├── nav.html
237 | │ │ │ └── nav.scss
238 | │ │ ├── footer/
239 | │ │ │ ├── footer.module.ts
240 | │ │ │ ├── footer.component.ts
241 | │ │ │ ├── footer.service.ts
242 | │ │ │ ├── footer.spec.ts
243 | │ │ │ ├── footer.html
244 | │ │ │ └── footer.scss
245 | │ │ └── common.module.ts
246 | │ ├── app.module.ts
247 | │ ├── app.component.ts
248 | │ └── app.scss
249 | └── index.html
250 | ```
251 |
252 | The high level folder structure simply contains `index.html` and `app/`, a directory in which all our root, component, common and low-level modules live.
253 |
254 | **[Back to top](#table-of-contents)**
255 |
256 | # Components
257 |
258 | ### Component theory
259 |
260 | Components are essentially templates with a controller. They are _not_ Directives, nor should you replace Directives with Components, unless you are upgrading "template Directives" with controllers, which are best suited as a component. Components also contain bindings that define inputs and outputs for data and events, lifecycle hooks and the ability to use one-way data flow and event Objects to get data back up to a parent component. These are the new defacto standard in Angular 1.5 and above. Everything template and controller driven that we create will likely be a component, which may be a stateful, stateless or routed component. You can think of a "component" as a complete piece of code, not just the `.component()` definition Object. Let's explore some best practices and advisories for components, then dive into how you should be structuring them via stateful, stateless and routed component concepts.
261 |
262 | **[Back to top](#table-of-contents)**
263 |
264 | ### Supported properties
265 |
266 | These are the supported properties for `.component()` that you can/should use:
267 |
268 | | Property | Support |
269 | |---|---|
270 | | bindings | Yes, use `'@'`, `'<'`, `'&'` only |
271 | | controller | Yes |
272 | | controllerAs | Yes, default is `$ctrl` |
273 | | require | Yes (new Object syntax) |
274 | | template | Yes |
275 | | templateUrl | Yes |
276 | | transclude | Yes |
277 |
278 | **[Back to top](#table-of-contents)**
279 |
280 | ### Controllers
281 |
282 | Controllers should only be used alongside components, never anywhere else. If you feel you need a controller, what you really need is likely a stateless component to manage that particular piece of behaviour.
283 |
284 | Here are some advisories for using `Class` for controllers:
285 |
286 | * Always use the `constructor` for dependency injection purposes
287 | * Don't export the `Class` directly, export it's name to allow `$inject` annotations
288 | * If you need to access the lexical scope, use arrow functions
289 | * Alternatively to arrow functions, `let ctrl = this;` is also acceptable and may make more sense depending on the use case
290 | * Bind all public functions directly to the `Class`
291 | * Make use of the appropriate lifecycle hooks, `$onInit`, `$onChanges`, `$postLink` and `$onDestroy`
292 | * Note: `$onChanges` is called before `$onInit`, see [resources](#resources) section for articles detailing this in more depth
293 | * Use `require` alongside `$onInit` to reference any inherited logic
294 | * Do not override the default `$ctrl` alias for the `controllerAs` syntax, therefore do not use `controllerAs` anywhere
295 |
296 | **[Back to top](#table-of-contents)**
297 |
298 | ### One-way dataflow and Events
299 |
300 | One-way dataflow was introduced in Angular 1.5, and redefines component communication.
301 |
302 | Here are some advisories for using one-way dataflow:
303 |
304 | * In components that receive data, always use one-way databinding syntax `'<'`
305 | * _Do not_ use `'='` two-way databinding syntax anymore, anywhere
306 | * Components that have `bindings` should use `$onChanges` to clone the one-way binding data to break Objects passing by reference and updating the parent data
307 | * Use `$event` as a function argument in the parent method (see stateful example below `$ctrl.addTodo($event)`)
308 | * Pass an `$event: {}` Object back up from a stateless component (see stateless example below `this.onAddTodo`).
309 | * Bonus: Use an `EventEmitter` wrapper with `.value()` to mirror Angular 2, avoids manual `$event` Object creation
310 | * Why? This mirrors Angular 2 and keeps consistency inside every component. It also makes state predictable.
311 |
312 | **[Back to top](#table-of-contents)**
313 |
314 | ### Stateful components
315 |
316 | Let's define what we'd call a "stateful component".
317 |
318 | * Fetches state, essentially communicating to a backend API through a service
319 | * Does not directly mutate state
320 | * Renders child components that mutate state
321 | * Also referred to as smart/container components
322 |
323 | An example of a stateful component, complete with it's low-level module definition (this is only for demonstration, so some code has been omitted for brevity):
324 |
325 | ```ts
326 | /* ----- todo/todo.component.ts ----- */
327 | import { TodoController } from './todo.controller';
328 | import { TodoService } from './todo.service';
329 | import { TodoItem } from '../common/model/todo';
330 |
331 | export const TodoComponent: angular.IComponentOptions = {
332 | controller: TodoController,
333 | template: `
334 |
335 |
338 |
340 |
341 | `
342 | };
343 |
344 |
345 | /* ----- todo/todo.controller.ts ----- */
346 | export class TodoController {
347 | static $inject: string[] = ['TodoService'];
348 | todos: TodoItem[];
349 |
350 | constructor(private todoService: TodoService) { }
351 |
352 | $onInit() {
353 | this.newTodo = new TodoItem('', false);
354 | this.todos = [];
355 | this.todoService.getTodos().then(response => this.todos = response);
356 | }
357 | addTodo({ todo }) {
358 | if (!todo) return;
359 | this.todos.unshift(todo);
360 | this.newTodo = new TodoItem('', false);
361 | }
362 | }
363 |
364 | /* ----- todo/todo.module.ts ----- */
365 | import angular from 'angular';
366 | import { TodoComponent } from './todo.component';
367 |
368 |
369 | export const TodoModule = angular
370 | .module('todo', [])
371 | .component('todo', TodoComponent)
372 | .name;
373 |
374 | /* ----- todo/todo.service.ts ----- */
375 | export class TodoService {
376 | static $inject: string[] = ['$http'];
377 |
378 | constructor(private $http: angular.IHttpService) { }
379 |
380 | getTodos() {
381 | return this.$http.get('/api/todos').then(response => response.data);
382 | }
383 | }
384 |
385 |
386 | /* ----- common/model/todo.ts ----- */
387 | export class TodoItem {
388 | constructor(
389 | public title: string,
390 | public completed: boolean) { }
391 | }
392 |
393 |
394 | ```
395 |
396 | This example shows a stateful component, that fetches state inside the controller, through a service, and then passes it down into stateless child components. Notice how there are no Directives being used such as `ng-repeat` and friends inside the template. Instead, data and functions are delegated into `` and `` stateless components.
397 |
398 | **[Back to top](#table-of-contents)**
399 |
400 | ### Stateless components
401 |
402 | Let's define what we'd call a "stateless component".
403 |
404 | * Has defined inputs and outputs using `bindings: {}`
405 | * Data enters the component through attribute bindings (inputs)
406 | * Data leaves the component through events (outputs)
407 | * Mutates state, passes data back up on-demand (such as a click or submit event)
408 | * Doesn't care where data comes from, it's stateless
409 | * Are highly reusable components
410 | * Also referred to as dumb/presentational components
411 |
412 | An example of a stateless component (let's use `` as an example), complete with it's low-level module definition (this is only for demonstration, so some code has been omitted for brevity):
413 |
414 | ```ts
415 | /* ----- todo/todo-form/todo-form.component.ts ----- */
416 | import { TodoFormController } from './todo-form.controller';
417 |
418 | export const TodoFormComponent: angular.IComponentOptions = {
419 | bindings: {
420 | todo: '<',
421 | onAddTodo: '&'
422 | },
423 | controller: TodoFormController,
424 | template: `
425 |
429 | `
430 | };
431 |
432 |
433 | /* ----- todo/todo-form/todo-form.controller.ts ----- */
434 | import { EventEmitter } from '../common/event-emitter';
435 | import { Event } from '../common/event';
436 |
437 | export class TodoFormController {
438 | static $inject = ['EventEmitter'];
439 |
440 | constructor(private eventEmitter: EventEmitter) {}
441 | $onChanges(changes) {
442 | if (changes.todo) {
443 | this.todo = Object.assign({}, this.todo);
444 | }
445 | }
446 | onSubmit() {
447 | if (!this.todo.title) return;
448 | // with EventEmitter wrapper
449 | this.onAddTodo(
450 | eventEmitter({
451 | todo: this.todo
452 | });
453 | );
454 | // without EventEmitter wrapper
455 | this.onAddTodo(new Event({
456 | todo: this.todo
457 | }));
458 | }
459 | }
460 |
461 | /* ----- common/event.ts ----- */
462 | export class Event {
463 | constructor(public $event: any){ }
464 | }
465 |
466 | /* ----- common/event-emitter.ts ----- */
467 | import { Event } from './event';
468 |
469 | export function EventEmitter(payload: any): Event {
470 | return new Event(payload);
471 | }
472 |
473 | /* ----- todo/todo-form/index.ts ----- */
474 | import angular from 'angular';
475 | import { EventEmitter } from './common/event-emitter';
476 | import { TodoFormComponent } from './todo-form.component';
477 |
478 | export const TodoFormModule = angular
479 | .module('todo.form', [])
480 | .component('todoForm', TodoFormComponent)
481 | .value('EventEmitter', EventEmitter)
482 | .name;
483 |
484 | ```
485 |
486 | Note how the `` component fetches no state, it simply receives it, mutates an Object via the controller logic associated with it, and passes it back to the parent component through the property bindings. In this example, the `$onChanges` lifecycle hook makes a clone of the initial `this.todo` binding Object and reassigns it, which means the parent data is not affected until we submit the form, alongside one-way data flow new binding syntax `'<'`.
487 |
488 | **[Back to top](#table-of-contents)**
489 |
490 | ### Routed components
491 |
492 | Let's define what we'd call a "routed component".
493 |
494 | * It's essentially a stateful component, with routing definitions
495 | * No more `router.ts` files
496 | * We use Routed components to define their own routing logic
497 | * Data "input" for the component is done via the route resolve (optional, still available in the controller with service calls)
498 |
499 | For this example, we're going to take the existing `` component, refactor it to use a route definition and `bindings` on the component which receives data (the secret here with `ui-router` is the `resolve` properties we create, in this case `todoData` directly map across to `bindings` for us). We treat it as a routed component because it's essentially a "view":
500 |
501 | ```ts
502 | /* ----- todo/todo.component.ts ----- */
503 | import { TodoController } from './todo.controller';
504 |
505 | export const TodoComponent: angular.IComponentOptions = {
506 | bindings: {
507 | todoData: '<'
508 | },
509 | controller: TodoController,
510 | template: `
511 |
512 |
515 |
517 |
518 | `
519 | };
520 |
521 | /* ----- todo/todo.controller.ts ----- */
522 | import { TodoItem } from '../common/model/todo';
523 |
524 | export class TodoController {
525 | todos: TodoItem[] = [];
526 |
527 | $onInit() {
528 | this.newTodo = new TodoItem();
529 | }
530 |
531 | $onChanges(changes) {
532 | if (changes.todoData) {
533 | this.todos = Object.assign({}, this.todoData);
534 | }
535 | }
536 |
537 | addTodo({ todo }) {
538 | if (!todo) return;
539 | this.todos.unshift(todo);
540 | this.newTodo = new TodoItem();
541 | }
542 | }
543 |
544 |
545 | /* ----- common/model/todo.ts ----- */
546 | export class TodoItem {
547 | constructor(
548 | public title: string = '',
549 | public completed: boolean = false) { }
550 | }
551 |
552 |
553 | /* ----- todo/todo.service.ts ----- */
554 | export class TodoService {
555 | static $inject: string[] = ['$http'];
556 |
557 | constructor(private $http: angular.IHttpService) { }
558 |
559 | getTodos() {
560 | return this.$http.get('/api/todos').then(response => response.data);
561 | }
562 | }
563 |
564 |
565 | /* ----- todo/index.ts ----- */
566 | import angular from 'angular';
567 | import { TodoComponent } from './todo.component';
568 | import { TodoService } from './todo.service';
569 |
570 | export const TodoModule = angular
571 | .module('todo', [])
572 | .component('todo', TodoComponent)
573 | .service('TodoService', TodoService)
574 | .config(($stateProvider: angular.ui.IStateProvider, $urlRouterProvider: angular.ui.IUrlRouterProvider) => {
575 | $stateProvider
576 | .state('todos', {
577 | url: '/todos',
578 | component: 'todo',
579 | resolve: {
580 | todoData: TodoService => TodoService.getTodos();
581 | }
582 | });
583 | $urlRouterProvider.otherwise('/');
584 | })
585 | .name;
586 |
587 | ```
588 |
589 | **[Back to top](#table-of-contents)**
590 |
591 | # Directives
592 |
593 | ### Directive theory
594 |
595 | Directives gives us `template`, `scope` bindings, `bindToController`, `link` and many other things. The usage of these should be carefully considered now `.component()` exists. Directives should not declare templates and controllers anymore, or receive data through bindings. Directives should be used solely for decorating the DOM. By this, it means extending existing HTML - created with `.component()`. In a simple sense, if you need custom DOM events/APIs and logic, use a Directive and bind it to a template inside a component. If you need a sensible amount of DOM manipulation, there is also the `$postLink` lifecycle hook to consider, however this is not a place to migrate all your DOM manipulation to, use a Directive if you can for non-Angular things.
596 |
597 | Here are some advisories for using Directives:
598 |
599 | * Never use templates, scope, bindToController or controllers
600 | * Always `restrict: 'A'` with Directives
601 | * Use compile and link where necessary
602 | * Remember to destroy and unbind event handlers inside `$scope.$on('$destroy', fn);`
603 |
604 | **[Back to top](#table-of-contents)**
605 |
606 | ### Recommended properties
607 |
608 | Due to the fact directives support most of what `.component()` does (template directives were the original component), I'm recommending limiting your directive Object definitions to only these properties, to avoid using directives incorrectly:
609 |
610 | | Property | Use it? | Why |
611 | |---|---|---|
612 | | bindToController | No | Use `bindings` in components |
613 | | compile | Yes | For pre-compile DOM manipulation/events |
614 | | controller | No | Use a component |
615 | | controllerAs | No | Use a component |
616 | | link functions | Yes | For pre/post DOM manipulation/events |
617 | | multiElement | Yes | [See docs](https://docs.angularjs.org/api/ng/service/$compile#-multielement-) |
618 | | priority | Yes | [See docs](https://docs.angularjs.org/api/ng/service/$compile#-priority-) |
619 | | require | No | Use a component |
620 | | restrict | Yes | Defines directive usage, always use `'A'` |
621 | | scope | No | Use a component |
622 | | template | No | Use a component |
623 | | templateNamespace | Yes (if you must) | [See docs](https://docs.angularjs.org/api/ng/service/$compile#-templatenamespace-) |
624 | | templateUrl | No | Use a component |
625 | | transclude | No | Use a component |
626 |
627 | **[Back to top](#table-of-contents)**
628 |
629 | ### Constants or Classes
630 |
631 | There are a few ways to approach using TypeScript and directives, either with an arrow function and easier assignment, or using an TypeScript `Class`. Choose what's best for you or your team, keep in mind Angular 2 uses `Class`.
632 |
633 | Here's an example using a constant with an Arrow function an expression wrapper `() => ({})` returning an Object literal (note the usage differences inside `.directive()`):
634 |
635 | ```ts
636 | /* ----- todo/todo-autofocus.directive.ts ----- */
637 | import angular from 'angular';
638 |
639 | export const TodoAutoFocus = ($timeout: angular.ITimeoutService) => ( {
640 | restrict: 'A',
641 | link($scope, $element, $attrs) {
642 | $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => {
643 | if (!newValue) {
644 | return;
645 | }
646 | $timeout(() => $element[0].focus());
647 | });
648 | }
649 | });
650 |
651 | TodoAutoFocus.$inject = ['$timeout'];
652 |
653 | /* ----- todo/index.ts ----- */
654 | import angular from 'angular';
655 | import { TodoComponent } from './todo.component';
656 | import { TodoAutofocus } from './todo-autofocus.directive';
657 |
658 | export const TodoModule = angular
659 | .module('todo', [])
660 | .component('todo', TodoComponent)
661 | .directive('todoAutofocus', TodoAutoFocus)
662 | .name;
663 |
664 | ```
665 |
666 | Or using TypeScript `Class` (note manually calling `new TodoAutoFocus` when registering the directive) to create the Object:
667 |
668 | ```ts
669 | /* ----- todo/todo-autofocus.directive.ts ----- */
670 | import angular from 'angular';
671 |
672 | export class TodoAutoFocus implements angular.IDirective {
673 | static $inject: string[] = ['$timeout'];
674 | restrict: string;
675 |
676 | constructor(private $timeout: angular.ITimeoutService) {
677 | this.restrict = 'A';
678 | }
679 |
680 | link($scope, $element: HTMLElement, $attrs) {
681 | $scope.$watch($attrs.todoAutofocus, (newValue, oldValue) => {
682 | if (!newValue) {
683 | return;
684 | }
685 |
686 | $timeout(() => $element[0].focus());
687 | });
688 | }
689 | }
690 |
691 |
692 | /* ----- todo/index.ts ----- */
693 | import angular from 'angular';
694 | import { TodoComponent } from './todo.component';
695 | import { TodoAutofocus } from './todo-autofocus.directive';
696 |
697 | export const TodoModule = angular
698 | .module('todo', [])
699 | .component('todo', TodoComponent)
700 | .directive('todoAutofocus', ($timeout: angular.ITimeoutService) => new TodoAutoFocus($timeout))
701 | .name;
702 |
703 | ```
704 |
705 | **[Back to top](#table-of-contents)**
706 |
707 | # Services
708 |
709 | ### Service theory
710 |
711 | Services are essentially containers for business logic that our components shouldn't request directly. Services contain other built-in or external services such as `$http`, that we can then inject into component controllers elsewhere in our app. We have two ways of doing services, using `.service()` or `.factory()`. With TypeScript `Class`, we should only use `.service()`, complete with dependency injection annotation using `$inject`.
712 |
713 | **[Back to top](#table-of-contents)**
714 |
715 | ### Classes for Service
716 |
717 | Here's an example implementation for our `` app using TypeScript `Class`:
718 |
719 | ```ts
720 | /* ----- todo/todo.service.ts ----- */
721 | export class TodoService {
722 | static $inject: string[] = ['$http'];
723 |
724 | constructor(private $http: angular.IHttpService) { }
725 | getTodos() {
726 | return this.$http.get('/api/todos').then(response => response.data);
727 | }
728 | }
729 |
730 |
731 | /* ----- todo/index.ts ----- */
732 | import angular from 'angular';
733 | import { TodoComponent } from './todo.component';
734 | import { TodoService } from './todo.service';
735 |
736 | export const todo = angular
737 | .module('todo', [])
738 | .component('todo', TodoComponent)
739 | .service('TodoService', TodoService)
740 | .name;
741 |
742 | ```
743 |
744 | **[Back to top](#table-of-contents)**
745 |
746 | # Styles
747 |
748 | Using [Webpack](https://webpack.github.io/) we can now use `import` statements on our `.scss` files in our `*.module.js` to let Webpack know to include that file in our stylesheet. Doing this lets us keep our components isolated for both functionality and style, it also aligns more closely to how stylesheets are declared for use in Angular 2. Doing this won't isolate our styles to just that component like it does with Angular 2, the styles will still be usable application wide but its more manageable and makes our applications structure easier to reason about.
749 |
750 | If you have some variables or globally used styles like form input elements then these files should still be placed into the root `scss` folder. e.g. `scss/_forms.scss`. These global styles can the be `@imported` into your root module (`app.module.js`) stylesheet like you would normally do.
751 |
752 | **[Back to top](#table-of-contents)**
753 |
754 | # TypeScript and Tooling
755 |
756 | ##### TypeScript
757 |
758 | * Use [Babel](https://babeljs.io/) to compile your TypeScript code and any polyfills
759 | * Consider using [TypeScript](http://www.typescriptlang.org/) to make way for any Angular 2 upgrades
760 |
761 | ##### Tooling
762 | * Use `ui-router` [latest alpha](https://github.com/angular-ui/ui-router) (see the Readme) if you want to support component-routing
763 | * Otherwise you're stuck with `template: ''` and no `bindings`
764 | * Consider using [Webpack](https://webpack.github.io/) for compiling your TypeScript code
765 | * Use [ngAnnotate](https://github.com/olov/ng-annotate) to automatically annotate `$inject` properties
766 | * How to use [ngAnnotate with TypeScript (same as ES6)](https://www.timroes.de/2015/07/29/using-ecmascript-6-es6-with-angularjs-1-x/#ng-annotate)
767 |
768 | **[Back to top](#table-of-contents)**
769 |
770 | # State management
771 |
772 | Consider using Redux with Angular 1.5 for data management.
773 |
774 | * [Angular Redux](https://github.com/angular-redux/ng-redux)
775 |
776 | **[Back to top](#table-of-contents)**
777 |
778 | # Resources
779 |
780 | * [Understanding the .component() method](https://toddmotto.com/exploring-the-angular-1-5-component-method/)
781 | * [Using "require" with $onInit](https://toddmotto.com/on-init-require-object-syntax-angular-component/)
782 | * [Understanding all the lifecycle hooks, $onInit, $onChange, $postLink, $onDestroy](https://toddmotto.com/angular-1-5-lifecycle-hooks)
783 | * [Using "resolve" in routes](https://toddmotto.com/resolve-promises-in-angular-routes/)
784 | * [Redux and Angular state management](http://blog.rangle.io/managing-state-redux-angular/)
785 |
786 | **[Back to top](#table-of-contents)**
787 |
788 | # Documentation
789 | For anything else, including API reference, check the [Angular documentation](//docs.angularjs.org/api).
790 |
791 | # Contributing
792 |
793 | Open an issue first to discuss potential changes/additions. Please don't open issues for questions.
794 |
795 | ## License
796 |
797 | #### (The MIT License)
798 |
799 | Copyright (c) 2016 Todd Motto
800 |
801 | Permission is hereby granted, free of charge, to any person obtaining
802 | a copy of this software and associated documentation files (the
803 | 'Software'), to deal in the Software without restriction, including
804 | without limitation the rights to use, copy, modify, merge, publish,
805 | distribute, sublicense, and/or sell copies of the Software, and to
806 | permit persons to whom the Software is furnished to do so, subject to
807 | the following conditions:
808 |
809 | The above copyright notice and this permission notice shall be
810 | included in all copies or substantial portions of the Software.
811 |
812 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
813 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
814 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
815 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
816 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
817 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
818 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
819 |
--------------------------------------------------------------------------------