Book Review Site via Angular and a Google Form


I had an interesting conversation with a pair of elementary school teachers. It led to me saying I’d put our two older kids on Good Reads in lieu of some less pleasant ways of documenting their reading. It’s not a place I’d necessarily drop a group of 3rd graders. As we spoke, I figured I could build a little version of one very quickly with Angular and a Google Form.

It’s worth noting that once you set up one Angular page tied to a Google Sheet, you can just copy that over and make websites very, very quickly. It’s just a matter of re-aligning the json data.

I hadn’t done anything with Angular in a while and while doing this I found out a few other neat little tricks that’ll be of use at some point.

One was this slick star rating CSS option. It will show the star rating based on a div percentage. I really liked it but my rating scale was 1 to 5 and Google didn’t really support doing something with percentages. However, I didn’t realize you could do math right in Angular. It turns out you can and I did it like so.

<span style="width:{{ (entry.gsx$_cyevm.$t/5)*100 }}%" class="rating">

The entry.gsx$_cyevm.$t part is just the name of the data chunk that holds the 1 to 5 rating scale. I can divide it by 5 and multiply it by 100 right there.

Slick.

You can see the working site here. Feel free to throw some book reviews up there to try it out.

Anyway, here’s the majority of the page (minus the CSS).

<body ng-app="myApp" ng-controller="SuperCtrl">
<div class="container-fluid">
    <h3>Reviewing Books at RRISD</h3>
    	<div class="row searchrow">
    		<div class="col-md-12">
    			<form class="form-inline">
                <div class="search">
    			<input data-ng-model='search.content.$t' name="sort" id="sort" type="text" placeholder="Filter by anything" autofocus>   
                </div>             
               <div class="nonfiction search">
                Non-Fiction:
                <select data-ng-model="search.gsx$nonfiction.$t">
                    <option value="">All</option>
                    <option value="Autobiography">Biography/Autobiography</option>
                    <option value="Journalism">Journalism</option>
                    <option value="Memoir">Memoir</option>
                    <option value="Reference">Reference book</option>
                    <option value="Speech">Speech</option>
                    <option value="Textbook">Textbook</option>
                </select>  
                </div>
                <div class="fiction search">
                Fiction:
                <select data-ng-model="search.gsx$fiction.$t">
                    <option value="">All</option>
                    <option value="Classic">Classic</option>
                    <option value="Humour">Humour</option>
                    <option value="Drama">Drama</option>
                    <option value="Historical fiction">Historical fiction</option>
                    <option value="Fable">Fable</option>
                    <option value="Fantasy">Fantasy</option>
                    <option value="Comic/Graphic Novel">Comic/Graphic Novel</option>
                    <option value="Crime/Detective">Crime/Detective</option>
                    <option value="Fairy tale">Fairy tale</option>
                    <option value="Horror">Horror</option>
                    <option value="Mystery">Mystery</option>
                    <option value="Mythology">Mythology</option>
                    <option value="Science fiction">Science fiction</option>
                    <option value="Short story">Short story</option>
                    <option value="Suspense/Thriller">Suspense/Thriller</option>
                    <option value="Tall tale">Tall tale</option>
                    <option value="Western">Western</option>
                </select>  
                </div>
                <div class="ratings search">
                Ratings:
                 <select data-ng-model="search.gsx$_cyevm.$t">
                    <option value="">All</option>
                    <option value="1">1 Star</option>
                    <option value="2">2 Stars</option>
                    <option value="3">3 Stars</option>
                    <option value="4">4 Stars</option>
                    <option value="5">5 Stars</option>
                </select>
                </div>
                <a href="https://docs.google.com/forms/d/1sVhUGWwJ46ZjOUj4MXKVFPgyYP1ry2u7HptTOOI-45w/viewform" class="btn btn-default btn-lg btn-right">Submit a review</a>             
                </form>
            </div>	
         </div>   
    <div class="row">
     <div ng-repeat="entry in entries | filter: search | orderBy:'gsx$timestamp.$t' : 'reverse'"  class="col-md-3 review">
          <h2>{{ entry.gsx$booktitle.$t }} </h2>
          <div class="reviewer">Reviewed by - {{ entry.gsx$yournamealias.$t }} </div>
          <div class="star-ratings-sprite"><span style="width:{{ (entry.gsx$_cyevm.$t/5)*100 }}%" class="rating"></span></div>
          <div class="rating">  </div> 
          <div class="bookcover" id="book" style="background-image: url('{{ entry.gsx$theurlforacoverimage.$t }}')"></div>
             <div ng-bind-html="entry.gsx$whatdidyouthinkofthebook.$t">         
                {{ entry.gsx$whatdidyouthinkofthebook.$t }}
            </div>
        </div>
      </div>
     </div>  
    </div> 

<script>
var app = angular.module('myApp', ['ngSanitize']);
$gid = "1NMuLhjdsjR2ktORbtAt0LVukSHFUr20v9IhEw9C0ALQ"
//$gid = $_GET['id'];
$gURL = "https://spreadsheets.google.com/feeds/list/" + $gid + "/1/public/values?alt=json";
app.controller('SuperCtrl', function($scope, $http) {
    $http.get($gURL)
    .success(function(response) {$scope.entries = response.feed.entry;});
    $scope.Math = window.Math;
});
</script>



Comments on this post

  1. Brian said on February 29, 2016 at 12:22 pm

    This does seem slick. I need to pay some attention to Angular.

    • Tom Woodward said on February 29, 2016 at 3:13 pm

      The mustache templating and the data binding makes for some really fast creation. I’m just scratching the surface and don’t fully understand it but given WP’s drift to json it seems a really attractive future way to build all sorts of stuff.

Leave a Reply

Trackbacks and Pingbacks on this post

No trackbacks.

TrackBack URL