MOBservable + React

Demonstrates mobservables in combination with react and the ObserverMixin

by Alexander Tymchuk

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.1/JSXTransformer.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.1/react-with-addons.js"></script>
<script src="https://rawgit.com/mweststrate/MOBservable/master/dist/mobservable.js"></script>
<script src="https://facebook.github.io/react/js/jsfiddle-integration.js"></script>
    <div id="container">
        <div id="header">
            <h1>MOBservable + React shopping cart demo</h1>
            <a href="https://www.mendix.com/tech-blog/making-react-reactive-pursuit-high-performing-easily-maintainable-react-apps/">Blog post explaing React + observables</a>
            <a href="https://github.com/mweststrate/mobservable">Github repo</a>
            
        </div>
        <div id="mount"></div>
        <div class="logview" id="logview">Rendering history:<br/></div>
        <button onClick="document.getElementById('logview').innerHTML = '';" style="float:right">clear</button>
    </div>

CSS

body {
	font-family: 'Georgia';
    background: url('https://rawgit.com/mweststrate/MOBservable/development/example/bg.jpg') no-repeat 50% 50% fixed;  
    background-size: cover;
}

h1 {
	font-family: 'Arial Black';
}

h2 {
	font-family: 'Arial Black';
	font-size: 12pt;	
}

#header {
	text-align: center;
}

#container {
	margin-left: auto;
	margin-right: auto;
	margin-top: 40px;
	width: 800px;
	padding: 40px;
	border-radius: 10px;
	background:rgba(255,255,255,1);
	box-shadow: 3px 3px 5px 0px rgba(50, 50, 50, 0.7);
}

#container table {
	width: 100%;
}

#container table td {
	width: 50%;
	vertical-align: top;
	padding: 20px;
}

ul {
	list-style: none;
    padding-left: 0px;
}

li {
	display: inline-block;
	width: 100%;
	border-bottom: 1px solid #e2e2e2;
	padding: 10px 0;
}

button {
	float: right;
    margin: 0px 5px;
}

.price {
	float: right;
	font-style: italic;
	margin-right: 10px;
}

.logview {
	background-color: #e2e2e2;
	font-size: 10pt;
	color: black;
	padding: 10px 40px;
}

JavaScript 1.7

var autoNumber = 0;
        /**
         * Data model
         */
        function Article(name, price) {
            this.id = ++autoNumber; // UUID for this article
            // Mobservable.props creates observable values and property accessors to read / write them
            mobservable.props(this, {
                name: name,
                price: price
            });
        }

        function ShoppingCartEntry(article) {
            this.id = ++autoNumber; // UUID for this entry
            mobservable.props(this, {
                article: article,
                amount: 1,
                price: function() {
                    return this.article ? this.article.price * this.amount : 0;
                }
            });
        }

        function ShoppingCart() {
            mobservable.props(this, {
                entries: [],
                total: function() {
                    return this.entries.reduce(function(sum, entry) {
                        return sum + entry.price;
                    }, 0);
                }
            });
        }

        // Some available articles
        var articles = mobservable.array([
            ["Funny Bunnies", 17.63],
            ["Awesome React", 23.95],
            ["Second hand Netbook", 50.00]
        ].map(function(e) {
            return new Article(e[0], e[1]);
        }));

        // Our shopping cart
        var shoppingCart = new ShoppingCart();
        // With a demo item inside
        shoppingCart.entries.push(new ShoppingCartEntry(articles[0]));
           
        /**
         * UI logic
         */
        var ShopDemoView = React.createClass({
            mixins: [mobservable.ObserverMixin, React.addons.PureRenderMixin],
            
            render: function() {
                // all render methods in a component with the ObserverMixin will automatically
                // subscribe to any observable values used in the render function
                // (and clean up...