Skip to content

Commit

Permalink
added_notification_serivice_and_data_service
Browse files Browse the repository at this point in the history
  • Loading branch information
vinodh99 committed Nov 19, 2018
1 parent 2c59889 commit 7ac59f7
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 20 deletions.
48 changes: 38 additions & 10 deletions src/App/App.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import React, { Component } from 'react';
import logo from './logo.svg';
// import logo from './logo.svg';
import './App.css';

// components
import Product from '../product/product';
import WishList from '../wishlist/wishlist';

// services
import HttpService from '../services/http-service';

const http = new HttpService();
class App extends Component {

Expand All @@ -13,9 +16,9 @@ class App extends Component {

this.state = {products:[]};
//Bind funictions
this.loadData = this.loadData.bind(this);//the loaddata is bound to here thus referring to this scope rather than refering it to global scope
this.loadData = this.loadData.bind(this);//the loaddata is bound here, thus referring to this scope rather than refering it to global scope
this.productList = this.productList.bind(this);

this.loadData(); //this is where the function is passed which is inside the loaddata variable.

}
Expand All @@ -27,12 +30,12 @@ class App extends Component {
self.setState({products:data})
// we are inside of a promise here and
// "then" here is screwing up our "this" hence a reference is created
// before the promise is loaded. self is referring to the component
// before th e promise is loaded. self is referring to the component
// here. But if self is not used and this is placed along with the
// setState then this referes to the promise and no longer to the
// component because it is asynchronous.
// everytime setState is called, it will render that component again and
// all the components inside of it but not outside of it
// everytime setState is called, it will render that component and
// all the components inside of it but not outside of it again
},err => {

});
Expand All @@ -41,7 +44,8 @@ class App extends Component {
productList = () => {
const list = this.state.products.map((product) =>
<div className = "col-sm-4" key={product._id}>
<Product title={product.title} price ={product.price} imgURL={product.imgURL}/>
<Product product={product}/>
{/* the whole product is passed as props */}
</div>
);
return (list);
Expand All @@ -53,9 +57,19 @@ class App extends Component {
<div className="App-header">
<h2>welcome suckers, find your best deals</h2>
</div>
<div className="container App-main">
<div className="container-fluid App-main">
<div className="row">
<div className="col-sm-8">
<div className="row">
{this.productList()}

</div>
</div>
{/* a row is taken first which contains two columns, products
and wishlist.Then within the products column productsList is inserted */}
<div className="col-sm-4">
<WishList />
</div>
</div>

</div>
Expand All @@ -65,4 +79,18 @@ class App extends Component {
}

export default App;

// the complete description
/* we load the data and when the data is loaded we set the state, refresh the Ui with data.
when you see .then we can no longer use this unless we grab a reference to it
such as self setstate Here. in this example products is refreshed
which we get from the server goes into the products array and the render
function is called again. this.productlist function is called which uses
javascript mapping array .we prettymuch use map function whenever we have a list
in react. map goes to every item in the list that we have in the
products array in this state, frop that product into the "product" parameter
and grab the element out of it, put it into our component, we pass the elements
into different properties and then we return the list which is rendered
to the screen. Now in the product.js, we access the list by saying this.props.imgUrl
which is coming from the parent. when the setstate is called, product.js is also
rendered
*/
3 changes: 3 additions & 0 deletions src/product-condensed/product-condensed.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.pc-condensed a{
margin-right:25px;
}
15 changes: 15 additions & 0 deletions src/product-condensed/product-condensed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React, { Component } from 'react'//we are grabbing one specific thing 'component' from react,hence curly braces
import './product-condensed.css';

class ProductCondensed extends Component{
render(){
return (
<li className = "list-group-item pc-condensed">
<a className= "btn btn-outline-danger">X</a>
<p>{this.props.product.title} | <b>${this.props.product.price}</b></p>
</li>
)
}
}

export default ProductCondensed;
4 changes: 2 additions & 2 deletions src/product/product.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.Product {
width:20rem;
max-width:15rem;
}

.Product img {
max-height:15rem;
max-height:10rem;
}
26 changes: 22 additions & 4 deletions src/product/product.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
import React, { Component } from 'react'//we are grabbing one specific thing 'component' from react,hence curly braces
import './product.css';
import DataService from '../services/data-service';

let ds = new DataService();

class Product extends Component{

constructor(props){
super(props);
this.onButtonClicked = this.onButtonClicked.bind(this);
}
onButtonClicked = () =>{
ds.addWishListItem(this.props.product);
// when we add whishlist item, then we enter the dataservice, it added the item,
// and the notification is posted which is passing the brand new wishlist with
// all the items. Now, since wishlist.js file is listening, it is going to pass
// in as a newWishlist and then we set the state for the new items in the wishlist.

}

render(){

return (
<div className = "card">
<img className = "card-img-top" src = {this.props.imgURL}alt="Product"></img>
<img className = "card-img-top" src = {this.props.product.imgUrl}alt="Product"></img>
{/* curly braces is a special syntax to insert javascript */}
<div className="card-block">
<h4 className="card-title">{this.props.title}</h4>
<p className="card-text">Price: ${this.props.price}</p>
<a href="#" className="btn btn-primary">Add to wishlist</a>
<h4 className="card-title">{this.props.product.title}</h4>
<p className="card-text">Price: ${this.props.product.price}</p>
<a href="#" onClick = {()=> this.onButtonClicked()}className="btn btn-primary">Add to wishlist</a>

</div>
</div>
Expand Down
56 changes: 56 additions & 0 deletions src/services/data-service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import NotificationService, {NOTIF_WISHLIST_CHANGED} from './notification-service';

let ns = new NotificationService();
// no matter how many times a new object of type notificationservice is created,
// it will still reference to the same one in memory
let instance = null;
var wishList = [];

class DataService{
constructor(){
if(!instance){
instance = this;
// if instance is created for the very first time, we are
// making it not null and create once in memory at "this" point in time
// and we store in the instance permanently
// now next time an object of the class is created, it checks that
// the object is not null and thus returns the same instance

}
return instance;
}
addWishListItem = item => {
wishList.push(item);
ns.postNotification(NOTIF_WISHLIST_CHANGED,wishList);
// posts notification with the new item in the wish list
}
removeWishListItem = item => {
for(var x=0; x<wishList.length;x++){
if (wishList[x]._id === item._id){
wishList.splice(x,1);
ns.postNotification(NOTIF_WISHLIST_CHANGED,wishList);
break;
}
}
}

}
export default DataService;
// we are gonna use it for the application so that it has only one dataservice
// that can be accessed. If we have more than one different data services, the data is
// inconsistent and






// class Car {
// // engine size
// // color
// }

// car1 = Car();
// car2 = Car();
// a singleton allows only one instance of the car in the memory.
// it can never be more than one
8 changes: 4 additions & 4 deletions src/services/http-service.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'whatwg-fetch';
// import 'whatwg-fetch';

class HttpService {
getProducts = () =>{
Expand All @@ -7,14 +7,14 @@ class HttpService {
// 2
fetch('http://localhost:3002/product')
.then(res => {
// 4
//then is chained to whatever fetch is returning
// 4
resolve(res.json());
//josn takes the response and converts to json
//json takes the response and converts to json
})
});
// 3
return promise;
//then is chained to whatever fetch is returning
}
}

Expand Down
66 changes: 66 additions & 0 deletions src/services/notification-service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// we could also make our http service a singleton which is not needed here.
// If there is app that has multipe components and those components needs to access the
// HTTP service we would want to make it a singleton.
export const NOTIF_WISHLIST_CHANGED = "notif_wishlist_changed";
// in our data-service, we need to start posting notifications, but before posting
// notifications, we need to have a central place where we can store them
var observers = {};
let instance = null;
class NotificationService {
constructor(){
if (!instance){
instance = this;
}

return instance;
}

postNotification = (notifName, data) => {
let obs = observers[notifName];
for (var x = 0; x < obs.length; x++){
var obj = obs[x];
obj.callBack(data);
}
}
removeObserver = (observer,notifName) => {
var obs = observers[notifName];
if(obs) {
for (var x=0; x<obs.length; x++){
if(observer === obs[x].observer){
obs.splice(x,1);
observers[notifName] = obs;
break;
// *important*
// multiple things can hold on to the same memory address, to the same
// memory spot. If there is a component created by react and it is on
// the screen, it existed but if it is also stored in observers, now there are
// two places that are referencing or pointing to the same space in memory
// Here in this program, it is being checked whether the observer is the exact same one
// one thats in memory at that address location
}
}
}
}

addObserver = (notifName, observer, callBack) => {
let obs = observers[notifName];

if(!obs){
observers[notifName] = [];
//if there is no array in there,
// that means we have never registered for that notification
// before. So we are creating an
// empty array at tha slot for that specific notification
// name that is passed to this function
}
let obj = {observer: observer, callBack: callBack};
observers[notifName].push(obj);
// notification name is used as a special key on our observers list
// and pushed to that array
}
}

export default NotificationService;
// we store a list of observers. An observer is a class or a component. An observer is a class or a component that says hello, I would like to listen
// (kind of like register to vote, here class registers to observe) and when it is time to be notified. Obbserver will register and the system will send
// back notifications when it is time to be notified.
Empty file added src/wishlist/wishlist.css
Empty file.
61 changes: 61 additions & 0 deletions src/wishlist/wishlist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, { Component } from 'react'//we are grabbing one specific thing 'component' from react,hence curly braces
import './wishlist.css';
import ProductCondensed from '../product-condensed/product-condensed'
import DataService from '../services/data-service';
import NotificationService,{NOTIF_WISHLIST_CHANGED} from '../services/notification-service'

let ns = new NotificationService();
class WishList extends Component{

constructor(props){
super(props);

this.state = {wishList:[]};
// bind functions
this.createWishList = this.createWishList.bind(this);
this.onWishListChanged = this.onWishListChanged.bind(this);
}

// when the component is mounting or it is about to load or if it did just load on
// the screen we can do something, same goes with unmount, we can do something when
// it goes out of memory
componentDidMount() {
ns.addObserver(NOTIF_WISHLIST_CHANGED, this, this.onWishListChanged)
// we can add ourselves an observer here
}

componentWillUnmount(){
ns.removeObserver(this, NOTIF_WISHLIST_CHANGED);
// we can remove ourelves an observer here. If we dont remove ourselves
// an observer here, we could have a memory leak on our app. The notification
// service will still hold on to this entire component even though it is not
// on the screen anymore.
}

onWishListChanged(newWishList){
this.setState({wishList: newWishList})
}

createWishList = () => {
const list = this.state.wishList.map((product) =>
<ProductCondensed product= {product} key={product._id} />

)
return (list);
}

render(){
return (
<div className = "card">
<div className = "card-block">
<h4 className = "card-title">wish List</h4>
<ul className = "list-group">
{this.createWishList()}
</ul>
</div>
</div>
);
}
}

export default WishList;

0 comments on commit 7ac59f7

Please sign in to comment.