Redux, challenging, rapidly evolving ecosystem. relies on packages on the side which changes quickly.
on top of redux, you have whole host of technologies...
need ES6 syntax.
What is redux?
What is it different from backbone, angular?
Redux: predictable state container for JS applications.
Suppose we have an app that displays list of books on the left and details on the main (author, pages, genre).
Consider structure of app. Both on view layer, and on data layer.
Data layer (data contained in the appliation): List of books and currently selected book
View layer: List View, List item, Detail View.
We want to separate view from data. Both come together to make a working, usable app.
Data layer - Redux is the data contained in this application box
View - React - views contained in the application box.
Redux, a state container, a collection of all the data that describes the app. Not only list of books, but also meta level properties, like what is the currently selected book.
React represent views, which translate the app's data as something that can be displayed on the screen and user can interact with.
What's different? Difference is that we centralize all the application data in a single object. Other js libraries always have collection of data. Backbone has collections, flux has different stores...angular..
redux centralize all these data in a central single object which we refer to as the state. Application level state. Global, big object which contain all data of our application.
Example, a counter. Add one button. Remove one button. Label count.
Data contained in app: current count
Views contained in app: current count, plus and minus button.
React in charge of showing the buttons components. Redux keep tracks of current count.
Monday, August 29, 2016
Sunday, August 28, 2016
2. Ajax Requests with React
Youtube Search Response
Need to make a call to youtube api to get information.
Which component should retrieve the list of videos? Downwards data flow: the most parent component should fetch the data.
index is the most parent component we have.
import React from 'react';
import ReactDOM from 'react-dom';
import SearchBar from './components/search_bar';
import YTSearch from 'youtube-api-search';
const API_KEY = 'AIzaSyC2AOEumcukxg2H2E5TOYBfeV5--GWOeyU';
YTSearch({key: API_KEY, term: 'surfboards'},function(data){
// call back function
console.log(data);
// check console.
});
const App = () => {
return (
<div>
<SearchBar />
</div>
);
}
ReactDOM.render(<App />,document.querySelector('.container'));
Refactoring Functional Components to Class Components
const API_KEY = 'AIzaSyC2AOEumcukxg2H2E5TOYBfeV5--GWOeyU';
class App extends Component {
constructor(props){
super(props);
this.state = {videos: []}; // array of videos
YTSearch({key: API_KEY, term: 'surfboards'}, (videos) => {
this.setState({ videos });
// this.setState({videos: videos});
});
}
render(){
return (
<div>
<SearchBar />
</div>
);
}
}
ReactDOM.render(<App />,document.querySelector('.container'));
Props
Will focus on list of videos
video_list.js
import React from 'react';
// add some classnames to add some styling
// add boostrap link in index.html
const VideoList = (props) => {
return (
<ul className="col-md-4 list-group">
{props.videos.length}
</ul>
);
}
export default VideoList;
in index.js
import VideoList from './components/video_list';
// passing props
render(){
return (
<div>
<SearchBar />
<VideoList videos={this.state.videos} />
</div>
);
}
Building Lists with Map
video_list.js
import React from 'react';
import VideoListItem from './video_list_item'
const VideoList = (props) => {
const videoItems = props.videos.map((video) => {
return <VideoListItem video={video} />
});
return (
<ul className="col-md-4 list-group">
{videoItems}
</ul>
);
};
export default VideoList;
/*
map example
var array = [1,2,3];
array.map((number) => {return number * 2})
array.map((number) => {return '<div>' + number + '</div>'})
*/
video_list_item.js
import React from 'react'
const VideoListItem = (props) => {
return <li>Video</li>
};
export default VideoListItem;
List item keys
in video_list.js add. Improve search efficiency
return <VideoListItem key={video.etag} video={video} />
Video List Items
video_list_item.js
import React from 'react'
const VideoListItem = ({video}) => {
const imageUrl = video.snippet.thumbnails.default.url;
return (
<li className="list-group-item">
<div className='video-list media'>
<div className="media-left">
<img className="media-object" src={imageUrl}/>
</div>
<div className="media-body">
<div className="media-heading">{video.snippet.title}</div>
</div>
</div>
</li>
)
};
export default VideoListItem;
Detail Component and Template Strings
Video Selection
index.js
class App extends Component {
constructor(props){
super(props);
this.state = {
videos: [],
selectedVideo: null
};
YTSearch({key: API_KEY, term: 'surfboards'}, (videos) => {
// when state is set, render will be called again
this.setState({
videos: videos,
selectedVideo: videos[0]
});
});
}
// passing props
render(){
return (
<div>
<SearchBar />
<VideoDetail video={this.state.selectedVideo}/>
<VideoList
onVideoSelect={selectedVideo => this.setState({selectedVideo})}
videos={this.state.videos} />
</div>
);
}
}
ReactDOM.render(<App />,document.querySelector('.container'));
define a function that updates app state. takes a video and updates the selected video
pass this as a property 'onVideoSelect' into videolist
video_list.js
const VideoList = (props) => {
const videoItems = props.videos.map((video) => {
return (
<VideoListItem
onVideoSelect={props.onVideoSelect}
key={video.etag}
video={video} />
)
});
return (
<ul className="col-md-4 list-group">
{videoItems}
</ul>
);
};
export default VideoList;
videolist takes the onVideoSelect property, and pass it into videolistitem
video_list_item.js
import React from 'react'
const VideoListItem = ({video, onVideoSelect}) => {
const imageUrl = video.snippet.thumbnails.default.url;
return (
<li onClick={() => onVideoSelect(video)} className="list-group-item">
<div className='video-list media'>
<div className="media-left">
<img className="media-object" src={imageUrl}/>
</div>
<div className="media-body">
<div className="media-heading">{video.snippet.title}</div>
</div>
</div>
</li>
)
};
export default VideoListItem;
videolistitem takes the onVideoSelect property and says,
whenever i am clicked, call that function with the video that i was passed.
Such a call back shouldn't be more than two level deep'
it can get confusing from index.js all the way to video_list_item
Styling with CSS
style.css
.search-bar{
margin: 20px;
text-align: center;
}
.video-item input{
width: 64px;
}
.video-detail .details{
margin-top: 10px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.list-group-item{
cursor: pointer;
}
.list-group-item:hover{
background-color:#eee;
}
Make sure in search_bar.js, you have
<div className="search-bar">
Searching for videos
Add ability to search for new videos.
class App extends Component {
constructor(props){
super(props);
this.state = {
videos: [],
selectedVideo: null
};
this.videoSearch('surfboards');
}
// passing props
render(){
return (
<div>
<SearchBar onSearchTermChange={(term => this.videoSearch(term))} />
<VideoDetail video={this.state.selectedVideo}/>
<VideoList
onVideoSelect={selectedVideo => this.setState({selectedVideo})}
videos={this.state.videos} />
</div>
);
}
videoSearch(term){
YTSearch({key: API_KEY, term: term}, (videos) => {
// when state is set, render will be called again
this.setState({
videos: videos,
selectedVideo: videos[0]
});
});
}
}
ReactDOM.render(<App />,document.querySelector('.container'));
Refactored youtube search in its own method, VideoSearch. Passed this method onto SearchBar under the property
onSearchTermChange. So Searchbar has to call props.onSearchTermChange.
, pass in a function, will call videoSearch with term.
When onSearchTermChange is called...
search_bar.js
import React from 'react';
class SearchBar extends React.Component{
constructor(props){
super(props);
this.state = {term: 'Starting Value'};
}
render(){
return (
<div className="search-bar">
<input
value = {this.state.term}
onChange={event => this.onInputChange(event.target.value)}
/>
</div>
)
}
onInputChange(term){
this.setState({term});
// fires off the callback function onsearchtermchange.
this.props.onSearchTermChange(term);
}
}
export default SearchBar;
Throttling Search Term Input
Its now laggy because search is called whenever we type something. UPdates are too often and overwhelming for users. WIll be good if we can throttlng.
1. need to install lodash
in terminal,
npm install --save lodash
2.
import _ from 'lodash';
3.
render(){
const videoSearch = _.debounce((term) => {this.videoSearch(term)},300);
return (
<div>
<SearchBar onSearchTermChange={videoSearch} />
<VideoDetail video={this.state.selectedVideo}/>
<VideoList
onVideoSelect={selectedVideo => this.setState({selectedVideo})}
videos={this.state.videos} />
</div>
);
}
want to call function once every ... milliseconds
we created a fat arrow function here and passed it to debounce. Debounce takes this inner function and returns a new function
that can only be called once every 300 milliseconds.
We then pass it into SearchBar.
Need to make a call to youtube api to get information.
Which component should retrieve the list of videos? Downwards data flow: the most parent component should fetch the data.
index is the most parent component we have.
import React from 'react';
import ReactDOM from 'react-dom';
import SearchBar from './components/search_bar';
import YTSearch from 'youtube-api-search';
const API_KEY = 'AIzaSyC2AOEumcukxg2H2E5TOYBfeV5--GWOeyU';
YTSearch({key: API_KEY, term: 'surfboards'},function(data){
// call back function
console.log(data);
// check console.
});
const App = () => {
return (
<div>
<SearchBar />
</div>
);
}
ReactDOM.render(<App />,document.querySelector('.container'));
Refactoring Functional Components to Class Components
const API_KEY = 'AIzaSyC2AOEumcukxg2H2E5TOYBfeV5--GWOeyU';
class App extends Component {
constructor(props){
super(props);
this.state = {videos: []}; // array of videos
YTSearch({key: API_KEY, term: 'surfboards'}, (videos) => {
this.setState({ videos });
// this.setState({videos: videos});
});
}
render(){
return (
<div>
<SearchBar />
</div>
);
}
}
ReactDOM.render(<App />,document.querySelector('.container'));
Props
Will focus on list of videos
video_list.js
import React from 'react';
// add some classnames to add some styling
// add boostrap link in index.html
const VideoList = (props) => {
return (
<ul className="col-md-4 list-group">
{props.videos.length}
</ul>
);
}
export default VideoList;
in index.js
import VideoList from './components/video_list';
// passing props
render(){
return (
<div>
<SearchBar />
<VideoList videos={this.state.videos} />
</div>
);
}
Building Lists with Map
video_list.js
import React from 'react';
import VideoListItem from './video_list_item'
const VideoList = (props) => {
const videoItems = props.videos.map((video) => {
return <VideoListItem video={video} />
});
return (
<ul className="col-md-4 list-group">
{videoItems}
</ul>
);
};
export default VideoList;
/*
map example
var array = [1,2,3];
array.map((number) => {return number * 2})
array.map((number) => {return '<div>' + number + '</div>'})
*/
video_list_item.js
import React from 'react'
const VideoListItem = (props) => {
return <li>Video</li>
};
export default VideoListItem;
List item keys
in video_list.js add. Improve search efficiency
return <VideoListItem key={video.etag} video={video} />
Video List Items
video_list_item.js
import React from 'react'
const VideoListItem = ({video}) => {
const imageUrl = video.snippet.thumbnails.default.url;
return (
<li className="list-group-item">
<div className='video-list media'>
<div className="media-left">
<img className="media-object" src={imageUrl}/>
</div>
<div className="media-body">
<div className="media-heading">{video.snippet.title}</div>
</div>
</div>
</li>
)
};
export default VideoListItem;
Detail Component and Template Strings
Video Selection
index.js
class App extends Component {
constructor(props){
super(props);
this.state = {
videos: [],
selectedVideo: null
};
YTSearch({key: API_KEY, term: 'surfboards'}, (videos) => {
// when state is set, render will be called again
this.setState({
videos: videos,
selectedVideo: videos[0]
});
});
}
// passing props
render(){
return (
<div>
<SearchBar />
<VideoDetail video={this.state.selectedVideo}/>
<VideoList
onVideoSelect={selectedVideo => this.setState({selectedVideo})}
videos={this.state.videos} />
</div>
);
}
}
ReactDOM.render(<App />,document.querySelector('.container'));
define a function that updates app state. takes a video and updates the selected video
pass this as a property 'onVideoSelect' into videolist
video_list.js
const VideoList = (props) => {
const videoItems = props.videos.map((video) => {
return (
<VideoListItem
onVideoSelect={props.onVideoSelect}
key={video.etag}
video={video} />
)
});
return (
<ul className="col-md-4 list-group">
{videoItems}
</ul>
);
};
export default VideoList;
videolist takes the onVideoSelect property, and pass it into videolistitem
video_list_item.js
import React from 'react'
const VideoListItem = ({video, onVideoSelect}) => {
const imageUrl = video.snippet.thumbnails.default.url;
return (
<li onClick={() => onVideoSelect(video)} className="list-group-item">
<div className='video-list media'>
<div className="media-left">
<img className="media-object" src={imageUrl}/>
</div>
<div className="media-body">
<div className="media-heading">{video.snippet.title}</div>
</div>
</div>
</li>
)
};
export default VideoListItem;
videolistitem takes the onVideoSelect property and says,
whenever i am clicked, call that function with the video that i was passed.
Such a call back shouldn't be more than two level deep'
it can get confusing from index.js all the way to video_list_item
Styling with CSS
style.css
.search-bar{
margin: 20px;
text-align: center;
}
.video-item input{
width: 64px;
}
.video-detail .details{
margin-top: 10px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.list-group-item{
cursor: pointer;
}
.list-group-item:hover{
background-color:#eee;
}
Make sure in search_bar.js, you have
<div className="search-bar">
Searching for videos
Add ability to search for new videos.
class App extends Component {
constructor(props){
super(props);
this.state = {
videos: [],
selectedVideo: null
};
this.videoSearch('surfboards');
}
// passing props
render(){
return (
<div>
<SearchBar onSearchTermChange={(term => this.videoSearch(term))} />
<VideoDetail video={this.state.selectedVideo}/>
<VideoList
onVideoSelect={selectedVideo => this.setState({selectedVideo})}
videos={this.state.videos} />
</div>
);
}
videoSearch(term){
YTSearch({key: API_KEY, term: term}, (videos) => {
// when state is set, render will be called again
this.setState({
videos: videos,
selectedVideo: videos[0]
});
});
}
}
ReactDOM.render(<App />,document.querySelector('.container'));
Refactored youtube search in its own method, VideoSearch. Passed this method onto SearchBar under the property
onSearchTermChange. So Searchbar has to call props.onSearchTermChange.
, pass in a function, will call videoSearch with term.
When onSearchTermChange is called...
search_bar.js
import React from 'react';
class SearchBar extends React.Component{
constructor(props){
super(props);
this.state = {term: 'Starting Value'};
}
render(){
return (
<div className="search-bar">
<input
value = {this.state.term}
onChange={event => this.onInputChange(event.target.value)}
/>
</div>
)
}
onInputChange(term){
this.setState({term});
// fires off the callback function onsearchtermchange.
this.props.onSearchTermChange(term);
}
}
export default SearchBar;
Throttling Search Term Input
Its now laggy because search is called whenever we type something. UPdates are too often and overwhelming for users. WIll be good if we can throttlng.
1. need to install lodash
in terminal,
npm install --save lodash
2.
import _ from 'lodash';
3.
render(){
const videoSearch = _.debounce((term) => {this.videoSearch(term)},300);
return (
<div>
<SearchBar onSearchTermChange={videoSearch} />
<VideoDetail video={this.state.selectedVideo}/>
<VideoList
onVideoSelect={selectedVideo => this.setState({selectedVideo})}
videos={this.state.videos} />
</div>
);
}
want to call function once every ... milliseconds
we created a fat arrow function here and passed it to debounce. Debounce takes this inner function and returns a new function
that can only be called once every 300 milliseconds.
We then pass it into SearchBar.
Friday, August 26, 2016
1. Introduction to React
Introduction
1. Get a boilerplate project
get a headstart with react. gonna start with the basics
2. once familar with react, will learn about general data modeling
3. then delve into redux
To get help, go to facebook group, or support@i-ducate.com
Source code:
https://github.com/StephenGrider/ReduxCasts
Purpose of Boilerplate Projects
Modern Javascript Tooling
Our project files
component_1.js
component_2.js
component_3.js
React.js
React.js
// no browser has support for ES6 yet. We will write in ES6 and will compile to ES5 via transpiling.
Tooling (Transpile JSX + ES6 to ES5)
Webpack + babel // transpile ES6 code into ES5 code to run in the browser
will output
index.html
application.js
style.css
that we can run in a browser.
Environment and Project Setup
download boilerproject from https://github.com/StephenGrider/ReduxSimpleStarter
unzip folder.
In Terminal, Go to folder.
run npm install
The Application we are going to built. Will build a youtube clone.
Will use actual youtube api.
npm start
will start the boiler plate package and run a local server. We write js files inside this package, then babel and webpack bundles these files together convert them into ES5 that can run in a browser and makes a local server available to view all those files.
http://localhost:8080/
A Taste of JSX
Now that we have the local server running, lets make some changes.
Open code editor. Make sure server is running You can make server run by npm start in the command line.
In index.html. We have a simple DOM here. head tag, body tag, div, script tag
bundle.js is a compiled js for our entire app.
webpack and babel takes all these files put them together in a single file bundle,js
web server displays index.html
start from scratch
new 'src' folder, new file index.js
What is React?
is a javascript library used to produce html shown to user in a web browser. When we write react code, we write individual components or views. Components are snippets of code that produce html. We write multiple different components and we nest them together by placing them one inside the other in different fashions to make really complex apps relatively simple.
A component is a collection of js functions that produces html. We write components in js but it ultimately produces html.
In index.js,
// Step 1. create a new component. This component should produce some html.
// const is ES6 syntax.
// we declare variables using 'var'
// but when use const, we declare a constant, value not going to change
// function
const App = function(){
return <div>Hi!</div>;
}
/* above JSX code. subset of JS, looks like html but is js.
JSX code cannot be interpreted by browser. Purpose of boiler pack with
webpack and babel is to translate this into vanilla js that can be ran on the browser
What is purpose of JSX? This is js code that produces the html that is inserted into the DOM when we
render this component (place html on to the page).
*/
// Step 2. take this component's generated html and put it on the page (in the DOM)
To see babel translating JSX into plain js code, go to
http://babeljs.io/repl/
JSX code
const App = function(){
return <div>Hi!</div>;
}
js code
"use strict";
var App = function App() {
return React.createElement(
"div",
null,
"Hi!"
);
};
generated js code is ugly and hard to figure out. We can clean concise code.
const App = function(){
return <ol>
<il>1</il>
<il>2</il>
<il>3</il>
</ol>;
}
js code
"use strict";
var App = function App() {
return React.createElement(
"ol",
null,
React.createElement(
"il",
null,
"1"
),
React.createElement(
"il",
null,
"2"
),
React.createElement(
"il",
null,
"3"
)
);
};
Very hard to understand code especially when its nested. Complex code will be almost ineligible. That is the purpose of JSX to make out components more clean and legible to understand.
ES6 Import Statements
Render Targets
// find react library from node_modules folder and assign library to the variable React
import React from 'react';
import ReactDOM from 'react-dom';
const App = function(){
return <div>Hi!</div>;
}
// use reactDOM to interact with the DOM. because we are trying to render something to the DOM.
//Trying to touch the DOM directly
//need to pass in an instance of app by putting in JSX <> around it, not class.
// 2nd argument, the target DOM
ReactDOM.render(<App />,document.querySelector('.container'));
index.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/style/style.css">
<link rel="stylesheet" href="https://cdn.rawgit.com/twbs/bootstrap/v4-dev/dist/css/bootstrap.css">
<script src="https://maps.googleapis.com/maps/api/js"></script>
</head>
dsfsdf
<body>
<div class="container"></div>
</body>
<script src="/bundle.js"></script>
</html>
Component Structure
A react application is made up of many components. A component is a function or object that returns some amount of html. It makes sense that we have different components for different functions.
1. Search bar at top.
2. Video screen, title, description
3.
If all logic inside index.html, it gets really messy really fast. React can render many components at the same and does it really fast. Rather than stuffing all logic into a single component, we spread it out into different components with different purposes.
Comes with practise.
For // show youtube screen
Component 1. search bar at top
Component 2. video player, title, description
Component 3. a single video preview on the right. Will have mulitple previews. Can always have multiple of one component
Component 4. a component that renders a list of component 3. Can nest components inside a components. Often, one component that lists a components.
Component 5. One final big component overall which is our app in index.js that will contains all components.
Building small components like this make it easier for us to build. For can, can reuse search bar component in other application.
Index is the root of our application, all other components will branch from it.
Remember, we always make one component per file.
Create new folder, 'components'.
Create new files,
search_bar.js
video_detail.js
video_list.js
video_list_item.js -
// continue #13
Youtube Search API Signup
Using the youtube API is a two step process. First, you need a API key.
Go to https://console.developers.google.com
Go to API Manager -> Enable API -> Search for youtube -> YouTube Data API v3 -> click on enable API -> Credentials -> Create Credentials -> API Key -> Browser Key -> Provide a name or leave it as default -> Create -> Pop up box with API key (select and copy api key)
index.js
import React from 'react';
import ReactDOM from 'react-dom';
const API_KEY = 'AIzaSyC2AOEumcukxg2H2E5TOYBfeV5--GWOeyU';
const App = () => {
return <div>Hi!</div>;
}
ReactDOM.render(<App />,document.querySelector('.container'));
Next, download an install package that will help us make this search request. Will download and install package youtubeapisearch. It will take an api key and search term and return search results.
Go to project directory
npm install --save youtube-api-search
// --save, please save this to package.json file in project. Its a list of dependencies that the project has.
Will be reflected in package.json file
"dependencies": {
"babel-preset-stage-1": "^6.1.18",
"lodash": "^3.10.1",
"react": "^0.14.3",
"react-dom": "^0.14.3",
"react-redux": "^4.0.0",
"react-router": "^2.0.1",
"redux": "^3.0.4",
"youtube-api-search": "0.0.5"
}
Export Statements
Open search_bar.js
import React from 'react';
const SearchBar = () => {
return <input />;
// Need to import React. React.createElement
};
export default SearchBar;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import SearchBar from './components/search_bar'; // for files that we write ourselves, need to provide folder reference
const API_KEY = 'AIzaSyC2AOEumcukxg2H2E5TOYBfeV5--GWOeyU';
const App = () => {
return ( // use parenthesis for multiple line JSX expression
<div>
<SearchBar />
</div>
);
}
ReactDOM.render(<App />,document.querySelector('.container'));
// app is at the top level, and the first child is search bar
Run and see in browser
Class Based Components
We will explore creating a component not with a function but with a ES6 class.
We have implemented a functional component because its literally is a function. Some info comes in, some JSX comes out. Simple.
Another type is a class based component. Used when you want your component to have an internal recording keeping. To be aware of itself and what happened to it since its been rendered.
Because users will be typing into this component, it needs some ability to check itself what's is typed. It needs to be aware. To upgrade from functional component to class component, where its more aware, more intelligent.
Its an actual js object with properties and methods.
import React from 'react';
//import React, {Component} from 'react';
class SearchBar extends React.Component{ // inherit from all functionalities from React.Component class
render(){ // method used to render to DOM, have to return some JSX
return <input />;
}
}
export default SearchBar;
When to use functional component or class based component? As a general rule, start with functional component, and if you need added functionality that you refactor it into a class. Will illustrate later.
Handling User Events
User trigger event, mouseover, type something in an input. How to respond to events? How to handle?
Two steps.
1. Declare event handler.
2. Pass event handler into t
// html event
// create a new input element and pass it a property onChange with value as this.onInputChange
// easier to tell purpose by name of method
// event Object descirbes context/information about event that has occured
// can use event object to get value of inout
// see developer tools in chrome to view console
// can find out info about other events in react docs
import React from 'react';
//import React, {Component} from 'react';
class SearchBar extends React.Component{
render(){
return <input onChange={this.onInputChange}/>;
}
onInputChange(event){
console.log(event.target.value);
}
}
export default SearchBar;
can change code to ES6.
render(){
return <input onChange={event => console.log(event.target.value)}/>;
}
// can clean up code a lot. concise
Introduction to State
Confusing. Hardest to understand in React.
Definition: State is a plain js object that is used to record and react to user events. Each class based component has its own state object. Whenever a component state is changed, the component immediatelt re-renders and forces all of its children to re-render as well.
Before we use the state object, we need to re-initailize. To do it,
import React from 'react';
//import React, {Component} from 'react';
class SearchBar extends React.Component{
constructor(props){
// calls parent class Component constructor method
super(props);
// define and initialize state
// properties of state. can be any property we want
this.state = {term: ''};
}
render(){
// change our state. Don't use this.state= ...'
return (
<div>
<input onChange={event => this.setState({term: event.target.value})}/>;
Value of Input: {this.state.term}
</div>
)
// when input changes, re-assign value of input to state
}
}
export default SearchBar;
// constructor: first and only function called automatically when new instance of class created
// reserved for doing setting up functions, like initializing variables and states
Controlled Components
constructor(props){
super(props);
this.state = {term: 'Starting Value'};
}
render(){
return (
<div>
<input
value = {this.state.term}
onChange={event => this.setState({term: event.target.value})}
/>;
</div>
)
}
1. Get a boilerplate project
get a headstart with react. gonna start with the basics
2. once familar with react, will learn about general data modeling
3. then delve into redux
To get help, go to facebook group, or support@i-ducate.com
Source code:
https://github.com/StephenGrider/ReduxCasts
Purpose of Boilerplate Projects
Modern Javascript Tooling
Our project files
component_1.js
component_2.js
component_3.js
React.js
React.js
// no browser has support for ES6 yet. We will write in ES6 and will compile to ES5 via transpiling.
Tooling (Transpile JSX + ES6 to ES5)
Webpack + babel // transpile ES6 code into ES5 code to run in the browser
will output
index.html
application.js
style.css
that we can run in a browser.
Environment and Project Setup
download boilerproject from https://github.com/StephenGrider/ReduxSimpleStarter
unzip folder.
In Terminal, Go to folder.
run npm install
The Application we are going to built. Will build a youtube clone.
Will use actual youtube api.
npm start
will start the boiler plate package and run a local server. We write js files inside this package, then babel and webpack bundles these files together convert them into ES5 that can run in a browser and makes a local server available to view all those files.
http://localhost:8080/
A Taste of JSX
Now that we have the local server running, lets make some changes.
Open code editor. Make sure server is running You can make server run by npm start in the command line.
In index.html. We have a simple DOM here. head tag, body tag, div, script tag
bundle.js is a compiled js for our entire app.
webpack and babel takes all these files put them together in a single file bundle,js
web server displays index.html
start from scratch
new 'src' folder, new file index.js
What is React?
is a javascript library used to produce html shown to user in a web browser. When we write react code, we write individual components or views. Components are snippets of code that produce html. We write multiple different components and we nest them together by placing them one inside the other in different fashions to make really complex apps relatively simple.
A component is a collection of js functions that produces html. We write components in js but it ultimately produces html.
In index.js,
// Step 1. create a new component. This component should produce some html.
// const is ES6 syntax.
// we declare variables using 'var'
// but when use const, we declare a constant, value not going to change
// function
const App = function(){
return <div>Hi!</div>;
}
/* above JSX code. subset of JS, looks like html but is js.
JSX code cannot be interpreted by browser. Purpose of boiler pack with
webpack and babel is to translate this into vanilla js that can be ran on the browser
What is purpose of JSX? This is js code that produces the html that is inserted into the DOM when we
render this component (place html on to the page).
*/
// Step 2. take this component's generated html and put it on the page (in the DOM)
To see babel translating JSX into plain js code, go to
http://babeljs.io/repl/
JSX code
const App = function(){
return <div>Hi!</div>;
}
js code
"use strict";
var App = function App() {
return React.createElement(
"div",
null,
"Hi!"
);
};
generated js code is ugly and hard to figure out. We can clean concise code.
const App = function(){
return <ol>
<il>1</il>
<il>2</il>
<il>3</il>
</ol>;
}
js code
"use strict";
var App = function App() {
return React.createElement(
"ol",
null,
React.createElement(
"il",
null,
"1"
),
React.createElement(
"il",
null,
"2"
),
React.createElement(
"il",
null,
"3"
)
);
};
Very hard to understand code especially when its nested. Complex code will be almost ineligible. That is the purpose of JSX to make out components more clean and legible to understand.
ES6 Import Statements
Render Targets
// find react library from node_modules folder and assign library to the variable React
import React from 'react';
import ReactDOM from 'react-dom';
const App = function(){
return <div>Hi!</div>;
}
// use reactDOM to interact with the DOM. because we are trying to render something to the DOM.
//Trying to touch the DOM directly
//need to pass in an instance of app by putting in JSX <> around it, not class.
// 2nd argument, the target DOM
ReactDOM.render(<App />,document.querySelector('.container'));
index.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/style/style.css">
<link rel="stylesheet" href="https://cdn.rawgit.com/twbs/bootstrap/v4-dev/dist/css/bootstrap.css">
<script src="https://maps.googleapis.com/maps/api/js"></script>
</head>
dsfsdf
<body>
<div class="container"></div>
</body>
<script src="/bundle.js"></script>
</html>
Lamda expression (ES6 syntax)
=> 'fat arrow'
const App = function(){
return <div>Hi!</div>;
}
change to
// more compact syntax. might need multiple functions for a component
const App = () => {
return <div>Hi!</div>;
}
return <div>Hi!</div>;
}
change to
// more compact syntax. might need multiple functions for a component
const App = () => {
return <div>Hi!</div>;
}
Component Structure
A react application is made up of many components. A component is a function or object that returns some amount of html. It makes sense that we have different components for different functions.
1. Search bar at top.
2. Video screen, title, description
3.
If all logic inside index.html, it gets really messy really fast. React can render many components at the same and does it really fast. Rather than stuffing all logic into a single component, we spread it out into different components with different purposes.
Comes with practise.
For // show youtube screen
Component 1. search bar at top
Component 2. video player, title, description
Component 3. a single video preview on the right. Will have mulitple previews. Can always have multiple of one component
Component 4. a component that renders a list of component 3. Can nest components inside a components. Often, one component that lists a components.
Component 5. One final big component overall which is our app in index.js that will contains all components.
Building small components like this make it easier for us to build. For can, can reuse search bar component in other application.
Index is the root of our application, all other components will branch from it.
Remember, we always make one component per file.
Create new folder, 'components'.
Create new files,
search_bar.js
video_detail.js
video_list.js
video_list_item.js -
// continue #13
Youtube Search API Signup
Using the youtube API is a two step process. First, you need a API key.
Go to https://console.developers.google.com
Go to API Manager -> Enable API -> Search for youtube -> YouTube Data API v3 -> click on enable API -> Credentials -> Create Credentials -> API Key -> Browser Key -> Provide a name or leave it as default -> Create -> Pop up box with API key (select and copy api key)
index.js
import React from 'react';
import ReactDOM from 'react-dom';
const API_KEY = 'AIzaSyC2AOEumcukxg2H2E5TOYBfeV5--GWOeyU';
const App = () => {
return <div>Hi!</div>;
}
ReactDOM.render(<App />,document.querySelector('.container'));
Next, download an install package that will help us make this search request. Will download and install package youtubeapisearch. It will take an api key and search term and return search results.
Go to project directory
npm install --save youtube-api-search
// --save, please save this to package.json file in project. Its a list of dependencies that the project has.
Will be reflected in package.json file
"dependencies": {
"babel-preset-stage-1": "^6.1.18",
"lodash": "^3.10.1",
"react": "^0.14.3",
"react-dom": "^0.14.3",
"react-redux": "^4.0.0",
"react-router": "^2.0.1",
"redux": "^3.0.4",
"youtube-api-search": "0.0.5"
}
Export Statements
Open search_bar.js
import React from 'react';
const SearchBar = () => {
return <input />;
// Need to import React. React.createElement
};
export default SearchBar;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import SearchBar from './components/search_bar'; // for files that we write ourselves, need to provide folder reference
const API_KEY = 'AIzaSyC2AOEumcukxg2H2E5TOYBfeV5--GWOeyU';
const App = () => {
return ( // use parenthesis for multiple line JSX expression
<div>
<SearchBar />
</div>
);
}
ReactDOM.render(<App />,document.querySelector('.container'));
// app is at the top level, and the first child is search bar
Run and see in browser
Class Based Components
We will explore creating a component not with a function but with a ES6 class.
We have implemented a functional component because its literally is a function. Some info comes in, some JSX comes out. Simple.
Another type is a class based component. Used when you want your component to have an internal recording keeping. To be aware of itself and what happened to it since its been rendered.
Because users will be typing into this component, it needs some ability to check itself what's is typed. It needs to be aware. To upgrade from functional component to class component, where its more aware, more intelligent.
Its an actual js object with properties and methods.
import React from 'react';
//import React, {Component} from 'react';
class SearchBar extends React.Component{ // inherit from all functionalities from React.Component class
render(){ // method used to render to DOM, have to return some JSX
return <input />;
}
}
export default SearchBar;
When to use functional component or class based component? As a general rule, start with functional component, and if you need added functionality that you refactor it into a class. Will illustrate later.
Handling User Events
User trigger event, mouseover, type something in an input. How to respond to events? How to handle?
Two steps.
1. Declare event handler.
2. Pass event handler into t
// html event
// create a new input element and pass it a property onChange with value as this.onInputChange
// easier to tell purpose by name of method
// event Object descirbes context/information about event that has occured
// can use event object to get value of inout
// see developer tools in chrome to view console
// can find out info about other events in react docs
import React from 'react';
//import React, {Component} from 'react';
class SearchBar extends React.Component{
render(){
return <input onChange={this.onInputChange}/>;
}
onInputChange(event){
console.log(event.target.value);
}
}
export default SearchBar;
can change code to ES6.
render(){
return <input onChange={event => console.log(event.target.value)}/>;
}
// can clean up code a lot. concise
Introduction to State
Confusing. Hardest to understand in React.
Definition: State is a plain js object that is used to record and react to user events. Each class based component has its own state object. Whenever a component state is changed, the component immediatelt re-renders and forces all of its children to re-render as well.
Before we use the state object, we need to re-initailize. To do it,
import React from 'react';
//import React, {Component} from 'react';
class SearchBar extends React.Component{
constructor(props){
// calls parent class Component constructor method
super(props);
// define and initialize state
// properties of state. can be any property we want
this.state = {term: ''};
}
render(){
// change our state. Don't use this.state= ...'
return (
<div>
<input onChange={event => this.setState({term: event.target.value})}/>;
Value of Input: {this.state.term}
</div>
)
// when input changes, re-assign value of input to state
}
}
export default SearchBar;
// constructor: first and only function called automatically when new instance of class created
// reserved for doing setting up functions, like initializing variables and states
Controlled Components
constructor(props){
super(props);
this.state = {term: 'Starting Value'};
}
render(){
return (
<div>
<input
value = {this.state.term}
onChange={event => this.setState({term: event.target.value})}
/>;
</div>
)
}
Sunday, August 21, 2016
22. Connecting to the Server
Connecting to the Server
A Simple RESTful API
Building RESTful API is beyond scope of angular since its server side technology like node, ruby on rails etc. But as an angular developer, need to know how to connect to RESTful API. Will connect to a fake RESTful api
https://jsonplaceholder.typicode.com/
or Google jsonplaceholder
Learn how to communicate with server in your angular apps
.e.g
https://jsonplaceholder.typicode.com/posts
Getting Data from the Server
Will use http class in angular. Use it to make ajax calls to server.
get()
post()
put() // for update
patch() //for update
delete()
head() // get meta data about resource. Used to get existence of resource
Should make http calls in Service.
Create new file post.service.ts
import {Http} from '@angular/http';
import 'rxjs/add/operator/map';
export class PostService{
constructor(private _http: Http){
}
getPost(){
return this._http.get("https://jsonplaceholder.typicode.com/posts")
.map(res => res.json())
}
}
Sending Data to the Server
import {Http} from '@angular/http';
import 'rxjs/add/operator/map';
export class PostService{
private _url = "https://jsonplaceholder.typicode.com/posts";
constructor(private _http: Http){
}
getPosts(){
return this._http.get(this._url)
.map(res => res.json())
}
createPost(post){
this._http.post(this._url,JSON.stringify(post))
.map(res => res.json());
}
}
Dependency Injection
Our service is ready. Now need to inject it into our component. First need to mark this class as available for dependency injection.
In post.service.ts, add
import {Injectable} from '@angular/core';
@Injectable()
app.component.ts
import {Component} from '@angular/core';
import {PostService} from './post.service';
import {HTTP_PROVIDERS} from '@angular/http';
@Component({
moduleId: module.id,
selector: 'app-root',
template: `
`,
providers:[PostService, HTTP_PROVIDERS]
})
export class AppComponent {
constructor(private _postService: PostService){
this._postService.getPosts()
.subscribe(posts => console.log(posts));
}
}
Component's Lifecycle Hooks
Even though our code works, doesn't follow the best practises. COnstructor should not contain costly operations.
Components have a lifecycle hooks that we can tap into during the key moments of a component's lifecycle.
OnInit
OnDestroy
DoCheck
OnChanges
AfterContentInit
AfterContentChecked
AfterViewInit
AfterViewChecked
We need to implement one for more interfaces. Each interface has a method that we need to add to our components. When the right moment arrives, Angular will call these methods. For e.g. we can implement the OnInit interface when the component is first instantiated.
import {Component} from '@angular/core';
import {PostService} from './post.service';
import {HTTP_PROVIDERS} from '@angular/http';
import {OnInit} from '@angular/core';
@Component({
moduleId: module.id,
selector: 'app-root',
template: `
`,
providers:[PostService, HTTP_PROVIDERS]
})
export class AppComponent implements OnInit {
constructor(private _postService: PostService){
}
ngOnInit(){
// this method will be called when the component is instantiated. Its called after constructor
// constructor should contain light weight operations. IF need to connect to server, should do so in ngOnInit
this._postService.getPosts()
.subscribe(posts => console.log(posts));
}
}
To implement more than one interface
export class AppComponent implements OnInit, OnDestroy {
Static Type Checking
Create Post interface
export interface Post{
// what fields do we need here?
userId: number;
id?: number;
title: string;
body: string;
}
post.service.ts
import {Http} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import {Injectable} from '@angular/core';
import {Post} from './post';
@Injectable()
export class PostService{
private _url = "https://jsonplaceholder.typicode.com/posts";
constructor(private _http: Http){
}
getPosts() : Observable<Post[]>{
return this._http.get(this._url)
.map(res => res.json())
}
createPost(post : Post){
this._http.post(this._url,JSON.stringify(post))
.map(res => res.json());
}
}
app.component.ts
import {Component} from '@angular/core';
import {PostService} from './post.service';
import {HTTP_PROVIDERS} from '@angular/http';
import {OnInit} from '@angular/core';
@Component({
moduleId: module.id,
selector: 'app-root',
template: `
`,
providers:[PostService, HTTP_PROVIDERS]
})
export class AppComponent implements OnInit {
constructor(private _postService: PostService){
this._postService.createPost({userId: 1, title: "a", body: "b"});
}
ngOnInit(){
// this method will be called when the component is instantiated. Its called after constructor
// constructor should contain light weight operations. IF need to connect to server, should do so in ngOnInit
this._postService.getPosts()
.subscribe(posts => console.log(posts[0].userId));
}
}
Showing a Loader Icon
When getting content from server, useful to show a loader icon to user
import {Component} from '@angular/core';
import {PostService} from './post.service';
import {HTTP_PROVIDERS} from '@angular/http';
import {OnInit} from '@angular/core';
@Component({
moduleId: module.id,
selector: 'app-root',
template: `
<div *ngIf="isLoading">Getting data...</div>
`,
providers:[PostService, HTTP_PROVIDERS]
})
export class AppComponent implements OnInit {
isLoading = true;
constructor(private _postService: PostService){
this._postService.createPost({userId: 1, title: "a", body: "b"});
}
ngOnInit(){
this._postService.getPosts()
.subscribe(posts => {
this.isLoading = false;
console.log(posts[0].userId);
});
}
}
Google 'font awesome'
Go to http://fontawesome.io/get-started/
add to index.html in head tag...
template: `
<div *ngIf="isLoading">
<i class="fa fa-spinner fa-spin fa-3x"></i>
</div>
`,
GitHub Example
app.component.ts
import {Component} from '@angular/core';
import {GitHubProfileComponent} from './github-profile.component';
@Component({
moduleId: module.id,
selector: 'app-root',
directives:[GitHubProfileComponent],
template: `
<github-profile></github-profile>
`
})
export class AppComponent {
}
github.service.ts
import {Injectable} from '@angular/core';
import {Http} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
@Injectable()
export class GitHubService {
private _baseUrl = "https://api.github.com/users/";
constructor(private _http: Http){}
getUser(username){
return this._http.get(this._baseUrl + username)
.map(res => res.json());
}
getFollowers(username){
return this._http.get(this._baseUrl + username + "/followers")
.map(res => res.json());
}
}
github-profile.component.ts
import {Component, OnInit} from '@angular/core';
import {HTTP_PROVIDERS} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/forkJoin';
import {GitHubService} from './github.service';
@Component({
selector: 'github-profile',
styles: [
`
.avatar {
width: 100;
height: 100;
border-radius: 100%;
}
`
],
template: `
<i *ngIf="isLoading" class="fa fa-spinner fa-spin fa-3x"></i>
<h2>@{{ user.login }}</h2>
<img class="avatar" src="{{ user.avatar_url }}">
<h3>Followers</h3>
<div *ngFor="#follower of followers" class="media">
<div class="media-left">
<a href="#">
<img class="media-object avatar" src="{{ follower.avatar_url }}" alt="...">
</a>
</div>
<div class="media-body">
<h4 class="media-heading">{{ follower.login }}</h4>
</div>
</div>
`,
providers: [HTTP_PROVIDERS, GitHubService]
})
export class GitHubProfileComponent implements OnInit {
isLoading = true;
username = "octocat";
user = {};
followers = [];
constructor(private _gitHubService: GitHubService){
}
ngOnInit(){
Observable.forkJoin(
this._gitHubService.getUser(this.username),
this._gitHubService.getFollowers(this.username)
)
.subscribe(
res => {
this.user = res[0];
this.followers = res[1];
},
null,
() => { this.isLoading = false; })
}
}
A Simple RESTful API
Building RESTful API is beyond scope of angular since its server side technology like node, ruby on rails etc. But as an angular developer, need to know how to connect to RESTful API. Will connect to a fake RESTful api
https://jsonplaceholder.typicode.com/
or Google jsonplaceholder
Learn how to communicate with server in your angular apps
.e.g
https://jsonplaceholder.typicode.com/posts
Getting Data from the Server
Will use http class in angular. Use it to make ajax calls to server.
get()
post()
put() // for update
patch() //for update
delete()
head() // get meta data about resource. Used to get existence of resource
Should make http calls in Service.
Create new file post.service.ts
import {Http} from '@angular/http';
import 'rxjs/add/operator/map';
export class PostService{
constructor(private _http: Http){
}
getPost(){
return this._http.get("https://jsonplaceholder.typicode.com/posts")
.map(res => res.json())
}
}
Sending Data to the Server
import {Http} from '@angular/http';
import 'rxjs/add/operator/map';
export class PostService{
private _url = "https://jsonplaceholder.typicode.com/posts";
constructor(private _http: Http){
}
getPosts(){
return this._http.get(this._url)
.map(res => res.json())
}
createPost(post){
this._http.post(this._url,JSON.stringify(post))
.map(res => res.json());
}
}
Dependency Injection
Our service is ready. Now need to inject it into our component. First need to mark this class as available for dependency injection.
In post.service.ts, add
import {Injectable} from '@angular/core';
@Injectable()
app.component.ts
import {Component} from '@angular/core';
import {PostService} from './post.service';
import {HTTP_PROVIDERS} from '@angular/http';
@Component({
moduleId: module.id,
selector: 'app-root',
template: `
`,
providers:[PostService, HTTP_PROVIDERS]
})
export class AppComponent {
constructor(private _postService: PostService){
this._postService.getPosts()
.subscribe(posts => console.log(posts));
}
}
Component's Lifecycle Hooks
Even though our code works, doesn't follow the best practises. COnstructor should not contain costly operations.
Components have a lifecycle hooks that we can tap into during the key moments of a component's lifecycle.
OnInit
OnDestroy
DoCheck
OnChanges
AfterContentInit
AfterContentChecked
AfterViewInit
AfterViewChecked
We need to implement one for more interfaces. Each interface has a method that we need to add to our components. When the right moment arrives, Angular will call these methods. For e.g. we can implement the OnInit interface when the component is first instantiated.
import {Component} from '@angular/core';
import {PostService} from './post.service';
import {HTTP_PROVIDERS} from '@angular/http';
import {OnInit} from '@angular/core';
@Component({
moduleId: module.id,
selector: 'app-root',
template: `
`,
providers:[PostService, HTTP_PROVIDERS]
})
export class AppComponent implements OnInit {
constructor(private _postService: PostService){
}
ngOnInit(){
// this method will be called when the component is instantiated. Its called after constructor
// constructor should contain light weight operations. IF need to connect to server, should do so in ngOnInit
this._postService.getPosts()
.subscribe(posts => console.log(posts));
}
}
To implement more than one interface
export class AppComponent implements OnInit, OnDestroy {
Static Type Checking
Create Post interface
export interface Post{
// what fields do we need here?
userId: number;
id?: number;
title: string;
body: string;
}
post.service.ts
import {Http} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import {Injectable} from '@angular/core';
import {Post} from './post';
@Injectable()
export class PostService{
private _url = "https://jsonplaceholder.typicode.com/posts";
constructor(private _http: Http){
}
getPosts() : Observable<Post[]>{
return this._http.get(this._url)
.map(res => res.json())
}
createPost(post : Post){
this._http.post(this._url,JSON.stringify(post))
.map(res => res.json());
}
}
app.component.ts
import {Component} from '@angular/core';
import {PostService} from './post.service';
import {HTTP_PROVIDERS} from '@angular/http';
import {OnInit} from '@angular/core';
@Component({
moduleId: module.id,
selector: 'app-root',
template: `
`,
providers:[PostService, HTTP_PROVIDERS]
})
export class AppComponent implements OnInit {
constructor(private _postService: PostService){
this._postService.createPost({userId: 1, title: "a", body: "b"});
}
ngOnInit(){
// this method will be called when the component is instantiated. Its called after constructor
// constructor should contain light weight operations. IF need to connect to server, should do so in ngOnInit
this._postService.getPosts()
.subscribe(posts => console.log(posts[0].userId));
}
}
Showing a Loader Icon
When getting content from server, useful to show a loader icon to user
import {Component} from '@angular/core';
import {PostService} from './post.service';
import {HTTP_PROVIDERS} from '@angular/http';
import {OnInit} from '@angular/core';
@Component({
moduleId: module.id,
selector: 'app-root',
template: `
<div *ngIf="isLoading">Getting data...</div>
`,
providers:[PostService, HTTP_PROVIDERS]
})
export class AppComponent implements OnInit {
isLoading = true;
constructor(private _postService: PostService){
this._postService.createPost({userId: 1, title: "a", body: "b"});
}
ngOnInit(){
this._postService.getPosts()
.subscribe(posts => {
this.isLoading = false;
console.log(posts[0].userId);
});
}
}
Google 'font awesome'
Go to http://fontawesome.io/get-started/
add to index.html in head tag...
template: `
<div *ngIf="isLoading">
<i class="fa fa-spinner fa-spin fa-3x"></i>
</div>
`,
GitHub Example
app.component.ts
import {Component} from '@angular/core';
import {GitHubProfileComponent} from './github-profile.component';
@Component({
moduleId: module.id,
selector: 'app-root',
directives:[GitHubProfileComponent],
template: `
<github-profile></github-profile>
`
})
export class AppComponent {
}
github.service.ts
import {Injectable} from '@angular/core';
import {Http} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
@Injectable()
export class GitHubService {
private _baseUrl = "https://api.github.com/users/";
constructor(private _http: Http){}
getUser(username){
return this._http.get(this._baseUrl + username)
.map(res => res.json());
}
getFollowers(username){
return this._http.get(this._baseUrl + username + "/followers")
.map(res => res.json());
}
}
github-profile.component.ts
import {Component, OnInit} from '@angular/core';
import {HTTP_PROVIDERS} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/forkJoin';
import {GitHubService} from './github.service';
@Component({
selector: 'github-profile',
styles: [
`
.avatar {
width: 100;
height: 100;
border-radius: 100%;
}
`
],
template: `
<i *ngIf="isLoading" class="fa fa-spinner fa-spin fa-3x"></i>
<h2>@{{ user.login }}</h2>
<img class="avatar" src="{{ user.avatar_url }}">
<h3>Followers</h3>
<div *ngFor="#follower of followers" class="media">
<div class="media-left">
<a href="#">
<img class="media-object avatar" src="{{ follower.avatar_url }}" alt="...">
</a>
</div>
<div class="media-body">
<h4 class="media-heading">{{ follower.login }}</h4>
</div>
</div>
`,
providers: [HTTP_PROVIDERS, GitHubService]
})
export class GitHubProfileComponent implements OnInit {
isLoading = true;
username = "octocat";
user = {};
followers = [];
constructor(private _gitHubService: GitHubService){
}
ngOnInit(){
Observable.forkJoin(
this._gitHubService.getUser(this.username),
this._gitHubService.getFollowers(this.username)
)
.subscribe(
res => {
this.user = res[0];
this.followers = res[1];
},
null,
() => { this.isLoading = false; })
}
}
Subscribe to:
Comments (Atom)