Note: MVC in this context is a 1978 design pattern, not an ASP.NET related technology that Microsoft released in 2007.

Models in MVC

You should be able to attach two views to the same model (directly or indirectly) and it all just work. Consequentially, either view can update the model, with changes appearing in the other view automatically. You should also be able to compose models from other models, and that all just work.

Angular’s conformance

I’m only testing Angular 1.5 here (not 2.0.0-beta.x or above), and it works as expected.

Take a look at the running application: paulhammant.com/demos/angulars-models-conform-to-the-m-in-mvc/index.html, or play with it inline in an iframe:

Expand one the expandable thingamies and click on the number of calories for the item. You can now edit that (span becomes an input). Note that changes you make percolates to the other tree-table. If you scroll down in the app, the change is also reflected in the under-pinning models. Therefore, M V and C behave as you’d expect.

Note: that I’m composing models, and Angular knows what’s connected to what (see source links below)

Source

See the 73 lines of HTML source here: paulhammant.com/demos/angulars-models-conform-to-the-m-in-mvc/index.html, or inlined:


<!DOCTYPE html>
<html ng-app="project" lang="en-us">
<head>
    <meta charset="UTF-8">
    <title>Meals and Exercise</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-resource.min.js"></script>
    <script src="project.js"></script>
<body ng-controller="TheController">
<h2>1. Meals or exercise ... and dates within those</h2>
<table class="table" style="width: 800px">
    <tr ng-repeat="mOrE in mealsOrExerciseThenDates | orderBy : '-name'">
        <td ng-init="mOrE.$$kcals = 0"><strong></strong><br/>( calories) </td>
        <td>
            <div ng-repeat="date in mOrE.data">
                <table class="table">
                    <tr>
                        <th>
                            <button style="padding: 0px 0px" class="btn btn-primary" ng-show="!date.$$expand" ng-click="date.$$expand = true">&#xff0b;</button>
                            <button style="padding: 0px 0px" class="btn btn-primary" ng-show="date.$$expand" ng-click="date.$$expand = false">&#xff0d;</button>
                             '', date:  ( , tot calories:  )
                        </th>
                    </tr>
                    <tr ng-repeat="item in date.items" ng-show="date.$$expand">
                        <td ng-init="mOrE.$$kcals = mOrE.$$kcals + item.kcal">
                            :
                            <span ng-show="!item.edit" ng-click="item.edit = true"></span>
                            <span ng-show="item.edit"><input type="number" ng-blur="item.edit = false" ng-model="item.kcal"/></span>
                            calories 
                        </td>
                    </tr>
                </table>
            </div>
        </td>
    </tr>
</table>
<h2>2. Dates ... and meals or exercise within those</h2>
<table class="table" style="width: 800px">
    <tr ng-repeat="(key, value) in datesThenMealsOrExercise">
        <td ng-init="value.$$kcals = 0"><strong></strong><br/>( calories total)</td>
        <td>
            <div ng-repeat="mOrE in value">
                <table class="table">
                    <tr>
                        <th>
                            <button style="padding: 0px 0px" class="btn btn-primary" ng-show="!mOrE.$$expand" ng-click="mOrE.$$expand = true">&#xff0b;</button>
                            <button style="padding: 0px 0px" class="btn btn-primary" ng-show="mOrE.$$expand" ng-click="mOrE.$$expand = false">&#xff0d;</button>
                             '', ( , tot calories:  )
                        </th>
                    </tr>
                    <tr ng-repeat="item in mOrE.items" ng-show="mOrE.$$expand">
                        <td ng-init="value.$$kcals = value.$$kcals + item.kcal">
                            :
                            <span ng-show="!item.edit" ng-click="item.edit = true"></span>
                            <span ng-show="item.edit"><input type="number" ng-blur="item.edit = false" ng-model="item.kcal"/></span>
                            calories 
                        </td>
                    </tr>
                </table>
            </div>
        </td>
    </tr>
</table>
<h2>3. Model data as it came from the server (and could go back with a PUT)</h2>
<pre></pre>
<h2>4. Model underpinning view #1</h2>
<pre></pre>
<h2>5. Model underpinning view #2</h2>
<pre></pre>
</body>
</html>

Also take a look at the 58-line JavaScript source for the Angular controller logic; here inline too:


angular.module('project', ['ngResource'])
    .factory('JsonGetter', function ($resource) {
        var JsonGetter = $resource(':q');
        return JsonGetter;
    })
    .controller('TheController', function ($scope, JsonGetter) {
        function insertItems(toIterateOver, type, plural, verb) {
            verb = verb || "consumed";
            plural = plural || "items";
            angular.forEach(toIterateOver, function (date) {
                if (!(date.when in $scope.datesThenMealsOrExercise)) {
                    $scope.datesThenMealsOrExercise[date.when] = [];
                }
                arr = $scope.datesThenMealsOrExercise[date.when];
                arr.push({
                    type: type,
                    plural: plural,
                    verb: verb,
                    name: date.name,
                    items: date.items
                });
            });
        }

        JsonGetter.get({q:'my-data.json'}, function(mne) {
            $scope.mealsAndExercise = mne; // raw
            // Marginally trasnformed for first view
            $scope.mealsOrExerciseThenDates = [
              {
                name:"Meals",
                plural:"items",
                verb:"consumed",
                data: mne.M
              },
              {
                name:"Exercise",
                plural:"exercises",
                verb:"burned",
                data: mne.E
              }
            ];
            $scope.datesThenMealsOrExercise = {};
            // Transformed quite a bit for second view
            insertItems(mne.M, "Meal", "items", "consumed");
            insertItems(mne.E, "Exercise", "exercises", "burned");
        });
        $scope.totCalories = function(toTotal){
            var totalKCal = 0;
            for(var i = 0; i < toTotal.length; i++){
                var item = toTotal[i];
                totalKCal += item.kcal;
            }
            return totalKCal;
        };
        $scope.abs = function(toAbs){
            return Math.abs(toAbs);
        }
    });

And tthe JSON the page loads over HTTP; also inlined here:


{
  "goal" : 1300,
  "M": [
    {
      "when": "2016-02-25",
      "name" : "snack",
      "items": [{
        "item": "Snickers bar", "kcal": 305
      }]
    },
    {
    "when": "2016-02-26",
    "name" : "Breakfast",
    "items": [
      {
        "item": "Nachos", "kcal": 223
      },{
        "item": "Dip", "kcal": 371
      },{
        "item": "Salsa", "kcal": 188        
    }]
  },
  {
    "when": "2016-02-26",
    "name" : "Lunch",
    "items": [{
      "item": "Steak", "kcal": 498
    },{
      "item": "Mash", "kcal": 212
    }]
  }
  ],
  "E": [
  {
    "when": "2016-02-26",
    "name" : "Gym",
    "items": [{
      "item": "Running Machine", "kcal": -210
    },{
      "item": "Step Machine", "kcal": -88
    },{
      "item": "Weights", "kcal": -80
    }]
  },
    {
      "when": "2016-02-25",
      "name" : "Swim",
      "items": [{
        "item": "42 laps", "kcal": -305
      }]
    }
  ]
}

Selling a style of Angular

When I code Angular for demo purposes, I like to completely avoid:

  1. Any build technology
  2. HTML inside javascript (template style)
  3. JsFiddle-like services

I wish more people would too. It’s much easier to pick up or teach. Especially if you are example orientated like me.



Published

March 2nd, 2016
Reads:

Tags