Skip to content

Instantly share code, notes, and snippets.

@trisweb
Created July 9, 2014 20:09
Show Gist options
  • Save trisweb/182ec22d17ad6d08b065 to your computer and use it in GitHub Desktop.
Save trisweb/182ec22d17ad6d08b065 to your computer and use it in GitHub Desktop.
Angular Survey using Firebase
<!DOCTYPE html>
<html ng-app="MyApp">
<!-- Made by Tristan Harward. BSD Licensed (because Berkeley > MIT) -->
<!--
Copyright (c) 2014, Tristan S. Harward
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
<head>
<title>Item Survey</title>
<!-- Firebase -->
<script src="https://cdn.firebase.com/js/client/1.0.15/firebase.js"></script>
<!-- AngularJS -->
<script src="https://code.angularjs.org/1.2.9/angular.min.js"></script>
<script src="https://code.angularjs.org/1.2.9/angular-cookies.min.js"></script>
<!-- AngularFire Library -->
<script src="https://cdn.firebase.com/libs/angularfire/0.7.1/angularfire.min.js"></script>
<link href="//fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,400,300,700" rel="stylesheet" type="text/css">
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<style>
body {
font-family: 'Open Sans', 'Lucida Grande', Helvetica, Arial, sans-serif;
font-weight: 200;
margin: 0;
padding: 0;
box-sizing: border-box;
margin-top: 85px;
}
.item {
font-size: 28px;
padding: 0;
display: block;
background-color: #eee;
border-bottom: 1px solid #ddd;
text-align: left;
min-height: 80px;
}
.item:hover {
background-color: #f3f3f3;
}
.item .score {
padding: 20px;
float: left;
display: block;
}
.item .name {
padding: 20px;
font-weight: 400;
}
.new-item {
padding: 26px;
background-color: #F7FAFF;
border-bottom: 1px solid #FFF;
text-align: center;
position: fixed;
top: 0;
right: 0;
left: 0;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
.new-item label span {
font-size: 24px;
}
input {
border: 1px solid #ccc;
padding: 5px 10px;
background-color: rgba(255,255,255,0.5);
font-size: 20px;
font-family: 'Open Sans', 'Lucida Grande', Helvetica, Arial, sans-serif;
}
input:focus {
outline: none;
border-color: #009CFF;
background-color: rgba(255,255,255,0.8);
}
input[type='button'] {
padding: 6px 10px;
}
input[type='button']:disabled {
background-color: #ccc;
}
a {
text-decoration: none;
}
.item .buttons {
float: left;
display: block;
width: 100px;
}
.item .buttons .action {
display: block;
padding: 21px;
width: 40px;
}
.btn {
background-color: #009CFF;
border: none;
color: white;
cursor: pointer;
transition: background-color 0.2s linear;
-webkit-transition: background-color 0.2s linear;
}
.btn:hover {
background-color: #5DB34A;
}
.btn:focus {
background-color: #009CFF;
outline: none;
}
.btn.action:focus {
background-color: transparent;
}
.action.delete {
float: right;
display: block;
color: #CC0033;
background-color: transparent;
padding: 21px 15px;
text-align: center;
}
.action.delete:hover {
color: white;
background-color: #CC0033;
}
.action.success {
color: #999;
background-color: transparent;
}
.action.success:hover {
color: #CC0033;
}
</style>
<script>
// SETUP: ENTER YOUR FIREBASE URL with PATH (sign up at www.firebaseio.com)
var firebaseUrl = "https://[YOUR-FIREBASE-NAME].firebaseio.com/[PATH]";
var myApp = angular.module("MyApp", ["firebase", "ngCookies"]);
myApp.filter('object2Array', function() {
return function(input) {
var out = [];
for (i in input) {
if (input[i] && typeof(input[i]) == "object") {
out.push(input[i]);
}
}
return out;
}
});
function guid() {
var u='',i=0;
while(i++<36) {
var c='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'[i-1],r=Math.random()*16|0,v=c=='x'?r:(r&0x3|0x8);
u+=(c=='-'||c=='4')?c:v.toString(16)
}
return u;
}
function ItemController($scope, $firebase, $cookies) {
// Anonymous Client ID for tracking votes
if (!localStorage.getItem("clientId")) {
localStorage.setItem("clientId", guid());
}
var clientId = localStorage.getItem("clientId");
console.log("Your anonymous client ID is: "+clientId);
var itemRef = new Firebase(firebaseUrl);
// Automatically syncs everywhere in realtime
$scope.items = $firebase(itemRef);
function getId(item) {
for (var key in $scope.items) {
if ($scope.items[key] == item) {
return key;
}
}
return null;
}
$scope.resetItem = function() {
$scope.newItem = {name: "", score: 1, votes: [ clientId ], owner: clientId};
}
$scope.resetItem();
$scope.addItem = function() {
// Limit to 200 chars.
if (!$scope.newItem.name) {
alert("You have to type something");
return;
} else if ($scope.newItem.name.length > 200) {
alert("Concise is better than long (Under 200 characters please)");
return;
}
// Check for dups
for (var key in $scope.items) {
if ($scope.items[key] && $scope.items[key].name == $scope.newItem.name) {
$scope.vote($scope.items[key]);
alert("That item already exists, your vote has been counted.");
return;
}
}
// AngularFire $add method
$scope.items.$add($scope.newItem);
$scope.resetItem();
}
$scope.vote = function(item) {
if ($scope.votable(item)) {
item.score += 1;
if (item.votes == null) {
item.votes = [];
}
item.votes.push(clientId);
}
$scope.items.$save();
}
$scope.unvote = function(item) {
if (!$scope.votable(item)) {
item.score -= 1;
if (item.votes == null) {
item.votes = [];
}
var i = item.votes.indexOf(clientId);
if (i > -1) {
item.votes.splice(i, 1);
}
$scope.items.$save();
}
}
$scope.votable = function(item) {
return item.votes == null || item.votes.indexOf(clientId) < 0;
}
$scope.delete = function(item) {
if (confirm("Are you sure you want to remove your line?")) {
if ($scope.deletable(item)) {
$scope.items.$remove(getId(item));
}
}
}
$scope.deletable = function(item) {
return item && item.owner == clientId;
}
}
</script>
</head>
<body ng-controller="ItemController">
<div class="new-item">
<form ng-submit="addItem()">
<!-- SETUP: Change the text title below to whatever you want: -->
<label><span>Dev-wing room names: <strong>Boston-metro-area breweries &amp; distilleries.</strong> Vote on your favorites:&nbsp;&nbsp;</span>
<input type="text" name="itemName" ng-model="newItem.name" style="width: 40%;" maxlength="200">
<input type="button" name="itemNew" ng-click="addItem()" value="+ ADD" class="btn" ng-disabled="!newItem.name">
</label>
</form>
</div>
<div class="item" ng-repeat="item in items | object2Array | orderBy:'score':true">
<div class="buttons">
<span ng-switch="votable(item)">
<a class="action btn" href="" ng-switch-when="true" ng-click="vote(item)" title="Agree"><i class="fa fa-plus"></i></a>
<a class="action btn success" href="" ng-switch-when="false" ng-click="unvote(item)" title="You agree! Click to undo."><i class="fa fa-check"></i></a>
</span>
</div>
<a class="action btn delete" href="" ng-click="delete(item)" ng-show="deletable(item)"><i class="fa fa-times"></i></a>
<div class="score">{{item.score}}</div>
<div class="name">{{item.name}}</div>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment