-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b0e0e48
commit b1f141b
Showing
12 changed files
with
434 additions
and
418 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
### Managing state with Angular 1.5+ components | ||
|
||
JS frameworks attempt to provide some ways to store your state, as well as your methods to change that state, in an organized way. If you're familiar with BackboneJS, it organized these things using models, collections, and views. With Angular, you have "components" (formerly directives), which are not much more than some config that ties together an HTML template and a controller class. | ||
|
||
The banner template: | ||
```html | ||
<div> | ||
<h1>{{ banner.message }}</h1> | ||
<button>Update message</button> | ||
</div> | ||
``` | ||
|
||
The banner component: | ||
```javascript | ||
import template from '../templates/banner.html' | ||
|
||
class BannerCtrl { | ||
constructor() { | ||
this.message = 'Some default message' | ||
} | ||
} | ||
|
||
export default angular.module('bannerComponent', []) | ||
.component('banner', { | ||
template, | ||
controller: BannerCtrl, | ||
controllerAs: 'banner' | ||
}) | ||
``` | ||
|
||
Notice how the values aren't stored in the HTML anymore, but on the controller instance here (e.g. `this.message`). The template (view) refers to that value using `{{ }}` braces, and Angular makes sure that the HTML is auto-updated any time that value changes. To make those changes, I'd provide a method on the controller class and reference it in the template, like this: | ||
|
||
```javascript | ||
import template from '../templates/banner.html' | ||
|
||
const messages = [ | ||
'Default message for the banner', | ||
'A totally different random message', | ||
'This message maybe came from an async API call', | ||
'Wow this is another message' | ||
] | ||
|
||
class BannerCtrl { | ||
constructor() { | ||
this.message = 'Some default message' | ||
} | ||
|
||
updateMessage() { | ||
const message = messages.shift() | ||
messages.push(this.message) | ||
this.message = message | ||
} | ||
} | ||
|
||
export default angular.module('bannerComponent', []) | ||
.component('banner', { | ||
template, | ||
controller: BannerCtrl, | ||
controllerAs: 'banner' | ||
}) | ||
``` | ||
|
||
And then reference the new controller method from the template: | ||
```html | ||
<div> | ||
<h1>{{ banner.message }}</h1> | ||
<button ng-click="banner.updateMessage()">Update message</button> | ||
</div> | ||
``` | ||
|
||
This uses angular's `ng-click` attribute to tap into the click event, during which I reference the controller as `banner` and call the new `updateMessage` method which rotates through the messages. Because the template is bound to a value stored in our state, when that controller value gets updated, Angular updates the HTML. | ||
|
||
This works, but it's a good idea to move the message handling logic out of the controller (better organization, reuse, etc). Here's a simple message service: | ||
|
||
```javascript | ||
const messages = [ | ||
'Default message for the banner', | ||
'A totally different random message', | ||
'This message maybe came from an async API call', | ||
'Wow this is another message' | ||
] | ||
|
||
export default class { | ||
|
||
get() { | ||
const msg = messages.shift() | ||
messages.push(msg) | ||
return msg | ||
} | ||
|
||
} | ||
``` | ||
|
||
Now the component can import the message service, depend on it in the angular module and then use it in the component controller's `updateMessage` method: | ||
|
||
```javascript | ||
import angular from 'angular' | ||
import template from '../templates/banner.html' | ||
import messageService from '../services/messages' | ||
|
||
class BannerCtrl { | ||
constructor(messages) { | ||
this.message = '' | ||
this.buttonText = 'Update message' | ||
this.messages = messages | ||
this.updateMessage() | ||
} | ||
|
||
updateMessage() { | ||
this.message = this.messages.get() | ||
} | ||
} | ||
|
||
export default angular.module('bannerComponent', [messageService.name]) | ||
.component('banner', { | ||
template, | ||
controller: BannerCtrl, | ||
controllerAs: 'banner' | ||
}) | ||
|
||
``` | ||
|
||
Take a look at [the Angular example code](../angular) to see how this all works together. | ||
|
||
Next: [React is kind of like Angular](react.md) |
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
## Managing state with plain JavaScript and jQuery | ||
|
||
HTML works for a lot of things on the web, and if you're okay with making new requests to a server for any change, it'll do most anything. Say you have a totally useless page with a dumb banner on it. | ||
|
||
```html | ||
<body> | ||
<section> | ||
<h1>Default message for this banner</h1> | ||
</section> | ||
<footer> | ||
Brought to you by html | ||
</footer> | ||
</body> | ||
``` | ||
|
||
What HTML isn't so good at is changing things. If (for some reason) you wanted to let a user change the banner message here with just HTML, you'd probably create a new HTML page for each different banner, and then use anchor links to move between the individual pages. Or you could use JavaScript. | ||
|
||
```html | ||
<!-- add a button and some ids --> | ||
<section> | ||
<h1 id='banner'>Default message for this banner</h1> | ||
<button id='changeMessage'>Update message</button> | ||
</section> | ||
``` | ||
|
||
```javascript | ||
const banner = document.getElementById('banner') | ||
const button = document.getElementById('changeMessage') | ||
|
||
button.addEventListener('click', function(e) { | ||
banner.innerHTML = 'Some different message' | ||
}) | ||
``` | ||
|
||
 | ||
|
||
The reason we need JavaScript here is because we've introduced ✨ state ✨ to the page. State is a value or list of values that you want to keep track of. In this example we now want the banner to display "a message" value, and to have a user action that can update that value and display the change in the page. With this simple JS example, I'm storing the state in the HTML itself. This was also usually how jQuery kept track of state, too. | ||
|
||
```javascript | ||
const $ = jQuery | ||
const messages = [ | ||
'A totally different random message', | ||
'This message maybe came from an async API call', | ||
'Wow this is another message' | ||
]; | ||
|
||
$(document).ready(function () { | ||
const banner = $("#banner") | ||
$('#changeMessage').on('click', function() { | ||
const oldMessage = banner.text() | ||
banner.text(messages.shift()) | ||
messages.push(oldMessage) | ||
}) | ||
}) | ||
``` | ||
|
||
In both of these examples so far, the "current message" is just whatever's in the HTML at a given time. There's no reason to worry about keeping the state in sync with the HTML--they're already in sync by default. For a lot of reasons that become clear as you build a bigger and bigger app, this kind of state management became unwieldy, so JavaScript frameworks emerged to help out. | ||
|
||
See more of [the plain JS example code](../plain-js). | ||
See more of [the jQuery example code](../jquery). | ||
|
||
Next: [Managing state with Angular](angular.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
### How React is kind of like Angular (\*ducks\*) | ||
|
||
A React app also starts with a component. Like Angular, it's basically a "template" and a "controller", but it's all contained in one class. The "template" part is represented here by the JSX returned in the class's `render` method (👋 Backbone), while the "controller" is basically the rest of the class. | ||
|
||
If JSX freaks you out, read [Facebook's explanation](https://facebook.github.io/react/docs/jsx-in-depth.html). | ||
|
||
Here's the example banner component from before, as a React class component: | ||
```javascript | ||
import React, { Component } from 'react' | ||
|
||
export default class Banner extends Component { | ||
|
||
constructor(props) { | ||
super(props) | ||
this.state = { | ||
message: 'Default message' | ||
} | ||
} | ||
|
||
render() { | ||
return ( | ||
<div> | ||
<h1>{this.state.message}</h1> | ||
<button>Update message</button> | ||
</div> | ||
) | ||
} | ||
|
||
} | ||
``` | ||
|
||
This component reads from `this.state.message` and makes changes to that message using `this.setState({ message: 'new message' })`. Whenever the state is updated, the render method is called to re-render the "view". To complete the example from before, we can add an updateMessage method and move the message handling out to some service. | ||
|
||
The service might look like this: | ||
```js | ||
const messages = [ | ||
'Default message for the banner', | ||
'A totally different random message', | ||
'This message maybe came from an async API call', | ||
'Wow this is another message' | ||
] | ||
|
||
export default { | ||
get() { | ||
const message = messages.shift() | ||
messages.push(message) | ||
return message | ||
} | ||
} | ||
``` | ||
_\*fwiw React doesn't have any "official" services like Angular, but encapsulating this kind of thing would still be a good idea, whatever you called it._ | ||
|
||
And the refactored component: | ||
```javascript | ||
import React, { Component } from 'react' | ||
import messages from '../services/messages' | ||
|
||
export default class Banner extends Component { | ||
constructor() { | ||
super() | ||
this.state = { | ||
message: 'The original message', | ||
buttonText: 'Update message' | ||
} | ||
} | ||
|
||
updateMessage() { | ||
this.setState({ message: messages.get() }) | ||
} | ||
|
||
render() { | ||
return ( | ||
<div className='banner'> | ||
<h1>{this.state.message}</h1> | ||
<button onClick={() => this.updateMessage()}>{this.state.buttonText}</button> | ||
</div> | ||
) | ||
} | ||
} | ||
``` | ||
|
||
And that's the banner example implemented in React. Take a look at [the complete React example](../react) to see more. | ||
|
||
Next: [Why Flux and Redux?](redux.md) |
Oops, something went wrong.