<!DOCTYPE html>
<html>
<head>
<link data-require="bootstrap@*" data-semver="4.0.5" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" />
<script data-require="rxjs@*" data-semver="5.1.1" src="https://unpkg.com/rxjs@5.1.1/bundles/Rx.min.js"></script>
<script data-require="redux@*" data-semver="3.2.1" src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.2.1/redux.js"></script>
<script data-require="jquery@*" data-semver="3.1.1" src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script data-require="bootstrap@*" data-semver="4.0.5" src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div id="productsContainer">
<fieldset>
<h2>Products</h2>
<div>
<label>Product Type: </label>
<select class="select">
<option value="1">
Cosmetic
</option>
<option value="2">
Grocery
</option>
<option value="3">
Fashion
</option>
</select>
Product Name:
<input id="productName" />
<button id="btnAdd" class="btn btn-success">Add</button>
</div>
<button id="btnSaveToServer" class="btn btn-info">Save to Server</button>
</fieldset>
<br>
<div class="list-group" id="productList">
</div>
</div>
</div>
<script src="rx-store.js"></script>
<script src="products/state.js"></script>
<script src="products/actions.js"></script>
<script src="products/reducer.js"></script>
<script src="products/index.js"></script>
<script src="products/product-selectors.js"></script>
<script src="products/product-controller.js"></script>
<script src="products/product-container-controller.js"></script>
</body>
</html>
/* Styles go here */
function productApp(state = initialState, action = {}) {
switch (action.type) {
case 'ADD':
{
return Object.assign({},state, {
items: [...state.items, action.payload]
});
}
case 'DELETE':
{
return Object.assign({},state, {
items: state.items.filter(i=>i.id!==action.payload.id)
});
}
case 'UPDATE_PRICE':
{
return Object.assign({},state,{items:
state.items.map(i=>{
if(i.id===action.payload.id) {
return {...i, price: action.payload.price};
}
return i;
})
});
}
case 'VOTE':
{
return Object.assign({},state, {
ratings: state.ratings.map(function(r) {
if (r.star === action.payload.star) {
return Object.assign({},r, {
votes: r.votes + 1
});
}
return r;
})
});
}
default:
return state;
}
}
var {
combineReducers
} = Redux;
var reducer = combineReducers({
products: productApp
});
store$.provideStore(reducer);
var productsController = (function(
store$, getRatings, getItems, addAction, voteAction, updatePriceAction, getProducts, productController
) {
var products$ = store$.select(getProducts);
var ratings$ = store$.select(getRatings);
var items$ = store$.select(getItems);
var addClick$ = Rx.Observable.fromEvent(document.getElementById('btnAdd'), 'click');
var saveToServer$ = Rx.Observable.fromEvent(document.getElementById('btnSaveToServer'), 'click');
addClick$.subscribe(addProduct);
saveToServer$.withLatestFrom(products$).map(v => v[1]).subscribe(saveToServer);
ratings$.subscribe(onRatingsChange);
items$.subscribe(onItemsChange);
return {
onPriceChange: onPriceChange,
onDelete: onDelete
};
function onDelete(id) {
store$.dispatch(deleteAction(id));
}
function onPriceChange(id, price) {
store$.dispatch(updatePriceAction(id, price));
}
function onRatingsChange(ratings) {
console.log('new ratings', ratings);
}
function onItemsChange(items) {
console.log('new items', items);
var $products = $('#productList').empty();
items.forEach((item) => {
$products.append(productController.render({
item,onDelete,onPriceChange
}));
});
}
function addProduct() {
var $name = $('#productName');
store$.dispatch(addAction(uniqueKey(), $name.val()));
$name.val('');
}
function saveToServer(allProducts) {
debugger;
// publish event to save server
}
function uniqueKey() {
return '_' + Math.random().toString(36).substr(2, 9);
// https://gist.github.com/gordonbrander/2230317
}
function vote(star) {
store$.dispatch(voteAction(star));
}
})(store$, getRatings, getItems, addAction, voteAction, updatePriceAction, getProducts,productController);
var store$ = (function(createStore) {
var store;
var state;
var _state;
var state$;
return {
dispatch: dispatch,
select: select,
provideStore: provideStore
};
function dispatch(action) {
store.dispatch(action);
}
function select(selectorFunction) {
return state$.map(selectorFunction).distinctUntilChanged();
}
function provideStore(reducers, initialState) {
store = createStore(reducers);
state = initialState | {};
state = store.getState();
_state = new Rx.BehaviorSubject(state);
state$ = _state.asObservable().distinctUntilChanged();
store.subscribe(function() {
_state.next(store.getState());
});
}
})(Redux.createStore);
;
function getProducts(state) {
return state.products;
}
function getRatings(state) {
return getProducts(state).ratings;
}
function getItems(state) {
return getProducts(state).items;
}
var addAction = function(id, name) {
return {
type: 'ADD',
payload: {
id: id,
name: name
}
};
};
var voteAction = (star) => ({
type: 'VOTE',
payload: {
star
}
});
var updatePriceAction = (id, price) => ({
type: 'UPDATE_PRICE',
payload: {
price, id
}
});
var deleteAction = (id) => ({
type: 'DELETE',
payload: {
id
}
});
var initialState = {
items: [{
id: '1',
name: 'computer',
price: '30'
}],
ratings: [{
star: 1,
votes: 0
}, {
star: 2,
votes: 0
}, {
star: 3,
votes: 0
}, {
star: 4,
votes: 0
}, {
star: 5,
votes: 0
}]
};
var productEffects = (function(){
})(store$);
var productController = (function() {
var props;
return {
render: render
};
function render(props) {
props = props;
return $(`
<div class="list-group-item">
<div class="col-md-6">
${props.item.name}
</div>
<div class="col-md-6">
<label>Price in $: </label>
<input class="productPrice"
value=${props.item.price||0} />
<button class="btn btn-danger product-delete">
Delete
</button> </div>
</div>
`)
.on('click', function(e) {
handleClick(props, e);
})
.change(function(e) {
handlePriceChange(props, e);
});
}
function handleClick(props, e) {
var $target = $(e.target);
if ($target.hasClass('product-delete')) {
handleItemDelete(props);
}
e.preventDefault();
}
function handleItemDelete(props) {
props.onDelete(props.item.id);
}
function handlePriceChange(props, e) {
var $target = $(e.target);
props.onPriceChange(props.item.id, $target.val());
e.preventDefault();
}
})();