Sharing giftlists

Currently, our giftlist functionality doesn't really work. We want users to be able to create giftlists which they can then share.

Fleshing out the giftlist model

Since we're using Mongoose to model data for our users, let's also put it to use to model our giftlists. Inside your models folder, create a new file called giftlist.js:

var mongoose = require('mongoose'); 
 
var giftlistSchema = mongoose.Schema({ 
    id: String, 
    user_id: String, 
    name: String, 
    gifts: [{name: String}] 
    sharedWith [{user_id: String}] 
 
}); 
module.exports = mongoose.model('Giftlist',giftlistSchema); 

This model is pretty straightforward. A giftlist has an ID, a name, a list of gift objects, and a user_id field. We will populate the user_id with the ID of the user who owns the giftlist. In a relational database, this would be a foreign key, defining a one-to-many relationship between users and giftlists.

The gifts field is an array of objects expecting only a name property. We also have a list of users with whom we have shared the giftlist. We will leave the sharing functionality for later.

Connecting the UI

The next thing we want to do is to allow users to create new giftlists from our SPA dashboard.

Since we're going to be POSTing data back via Ajax, we need to do a little work to make the CSRF token available to our Angular application. There are two steps to do this; first, we want to pass the token in our dashboard.js route:

var express = require('express'); 
var router = express.Router(); 
var isAuthenticated = require('../utils/authenticated'); 
 
router.get('/', isAuthenticated, function(req, res, next) { 
 
 
    var db = req.db; 
    var collection = db.get('giftlist'); 
        console.log("routing dash for " + req.user._id); 
    collection.find({'owner_id':req.user._id}, {}, function(err,giftlists){ 
        if(err){ 
            res.send(err); 
        }else { 
            giftlists = giftlists || []; 
            res.render('dash/dashboard', {user: req.user, giftlists: giftlists, csrfToken: req.csrfToken()}); 
        } 
    }); 
}); 
 
module.exports = router; 

We pass the token to the render function.

Next, we will add something to our dashboard.ejs template:

<!DOCTYPE html> 
<html ng-app="giftapp"> 
<head > 
    <title>Dashboard for <%= user.firstName %> <%= user.lastName %> </title> 
 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css"> 
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.min.js"></script> 
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-resource.js"></script> 
 
 
</head> 
<body> 
<nav class="nav navbar-default"> 
    <div class="container-fluid"> 
        <div class="navbar-header"> 
            <a class="navbar-brand" href="#"><%= user.firstName %> <%= user.lastName %> Dashboard</a> 
        </div> 
    </div> 
</nav> 
 
 
<div class="container"> 
 
    <div ui-view></div> 
</div> 
<script src="/javascripts/controllers/dashMainController.js"></script> 
<script src="/javascripts/controllers/giftappFormController.js"></script> 
<script src="/javascripts/services/giftlistFactory.js"></script> 
<script>
    angular.module("csrfValue", [])

            .value("csrfToken","<%= csrfToken %>");
</script> 
</body> 
</html> 

We create a new Angular module inside our page and add a value to it. A value is basically an injectable name value pair we can use in our application. We do this in the dashboard template, because we need the server to provide the csrfToken to the UI.

We've also added a script tag to load a new controller script file that we will use to handle processing and submitting the form.

Connecting the form

Next, we need to connect the giftlist form to the controller and have the controller talk to the backend.

Creating the controller

Create a new file in your javascripts/controllers directory called giftappFormController.js:

angular.module('giftappControllers') 
    .controller('GiftappFormController', ['$scope','List','csrfToken', '$state', function($scope, List, csrfToken, $state) { 
        $scope.formData = {}; 
        $scope.formData.items = []; 
        $scope.formData._csrf = csrfToken; 
 
 
        $scope.create = function() { 
            console.log("create"); 
            var myList = new List($scope.formData); 
            myList.$save(function(giftList){ 
                console.log(giftList); 
                $state.go('dash'); 
            }); 
 
        } 
    }]); 

We add a new controller into our giftappControllers module. We inject a number of things into the controller, including our List resource, and $state. We're also injecting csrfToken. We don't have access to that quite yet, but we'll inject its module into our module in a bit.

Inside the controller, we set up an object on $scope called formData. This will hold the data entered by a user on our form. We also add a function to scope, called create, which will be invoked when a user submits the form. We create a new instance of our List resource, add our data to it, and save it to the backend. After saving, we trigger a state change to return to the dashboard.

Since our module is actually defined insidedashMainController.js, this is where we want to inject the module containing our csrfToken value:

angular.module('giftappControllers',['giftlistServices','csrfValue']) 
    .controller('DashMainController', ['$scope','List', function($scope,List) { 
        $scope.lists = List.query(); 
 
    }]); 

Simply by adding the name of the module to our module's dependencies, we get access to the value service inside our module.

Angularizing the form

The next thing we need to do is to add some AngularJS directives to our template at public/templates/dash-add.tpl.html:

<div class="row"> 
    <div class="col-md-12"> 
        <h2>Add a new list</h2> 
        <form class="form-horizontal" ng-submit="create()"> 
            <div class="form-group"> 
                <label for="listname" class="col-sm-2 control-label">List 
                 Name</label> 
                <div class="col-sm-10"> 
                    <input type="text" class="form-control" id="listname"
                     placeholder="Name" ng-model="formData.name"> 
                </div> 
            </div> 
            <div class="form-group"> 
                <label class="col-sm-2 control-label">Item 1</label> 
                <div class="col-sm-10"> 
                    <input type="text" class="form-control" ng-
                     model="formData.items[0]"> 
                </div> 
            </div> 
            <div class="form-group"> 
                <label class="col-sm-2 control-label">Item 2</label> 
                <div class="col-sm-10"> 
                    <input type="text" class="form-control" ng-
                     model="formData.items[1]"> 
                </div> 
            </div> 
            <div class="form-group"> 
                <label class="col-sm-2 control-label">Item 3</label> 
                <div class="col-sm-10"> 
                    <input type="text" class="form-control" ng-
                     model="formData.items[2]"> 
                </div> 
            </div> 
            <div class="form-group"> 
                <div class="col-sm-offset-2 col-sm-10"> 
                    <button type="submit" class="btn btn-default btn-lg btn-
                     block"> 
                        <span class="glyphicon glyphicon-flash"></span> Create
                          List 
                    </button> 
                </div> 
            </div> 
        </form> 
        {{formData}} 
    </div> 
 
</div> 

The first change is adding the ng-submit directive to the form. On submitting the form, the controller's $scope.create() function will be invoked.

We then connect the inputs to the $scope.formdata using ng-model directives. This creates two-way data binding. To demonstrate this, we add {{formData}} into the template. This will show you all the data held by $scope.formdata and is a great way to troubleshoot your form. Obviously it's not something you'd leave in the template in production.

Connecting to the backend controller

Now that our form is connected to our controller, we need to connect our controller to our backend to store and retrieve our data from the database. Open your controllers/giftlist_controller.js file:

var Giftlist = require('../models/giftlist'); 
 
exports.index = function(req, res){ 
 
        Giftlist.find({'user_id':req.user._id}, {}, function(err,giftlists){ 
            if(err){ 
                res.send(err); 
            }else if(giftlists){ 
                res.json(giftlists); 
 
            }; 
        }); 
 
 
}; 
 
 
 
exports.create = function(req, res){ 
    var newGiftlist = new Giftlist(); 
    newGiftlist.name = req.body.name; 
    newGiftlist.user_id = req.user._id; 
 
    var gifts = []; 
    req.body.items.forEach(function(item){ 
        gifts.push({name:item}); 
    }); 
    newGiftlist.gifts = gifts; 
 
    newGiftlist.save(function(err){ 
        if(err){ 
            throw err 
        } else { 
            res.json(newGiftlist); 
        } 
    }); 
 
}; 
 
exports.show = function(req, res){ 
    Giftlist.findOne({_id:req.params.id}, function(err, list){ 
        if(req.params.format == "json" || req.isJSON){ 
            res.json(list); 
        }else{ 
            res.render('giftlist/show',{giftlist:list}); 
        } 
    }); 
 
}; 

We require in our giftlist model, and we've edited the index, show, and create routes to take advantage of the Mongoose database functions. Because we want to be able to share lists easily with people who aren't logged into our dashboard, non-JSON requests to show are going to render in a separate page.

Inside your views directory, create a new giftlist directory and create a template called show.ejs:

<!DOCTYPE html> 
<html> 
<head> 
    <title>Show Users</title> 
    <link rel='stylesheet' href='/stylesheets/style.css' /> 
</head> 
<body> 
<h1>User List: <%= appName %></h1> 
 
<table> 
    <thead> 
        <tr> 
 
            <th>First Name</th> 
            <th>Last Name</th> 
            <th>Email Address</th> 
            <th>Dashboard</th> 
        </tr> 
    </thead> 
    <tbody> 
    <% users.forEach(function(user, index){ -%> 
        <tr> 
            <td><a href="show/<%= user._id%> "><%= user.firstName %></a></td> 
            <td><%= user.lastName %></td> 
            <td><%= user.email %></td> 
            <td><a href="/dash/<%= user._id %>">View</a></td> 
        </tr> 
    <% }); %> 
    </tbody> 
</table> 
 
 
</body> 
</html> 

This is a pretty straightforward template that renders the list name and the gifts on the list.

Adding the ability to share lists on social media

Next, we want to allow users to easily share their lists. We need to make a minor adjustment to the dash-main template:

<div class="row"> 
    <div class="col-xs-12 col-md-6"> 
        <h2>My Lists</h2> 
 
        <a class="btn btn-primary" role="button" ui-sref="add" href="#/add"> 
            <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> 
            Add List</a> 
        <ul class="list-unstyled"> 
            <li ng-repeat="list in lists"><a class="btn btn-link" 
             href="/giftlist/{{list._id}}" role="button">{{ list.name }}</a></li> 
 
 
        </ul> 
    </div> 
 
    <div class="col-xs-12 col-md-6"> 
        <h2>Lists Shared With Me</h2> 
    </div> 
</div> 

The URL we've added to the link will trigger the show route in our controller, passing it the ID of the list we want to show.

Next, we'll add sharing buttons to our giftlist/show.ejs template:

<!DOCTYPE html> 
<html> 
<head > 
    <title>Giftlist: <%= giftlist.name %></title> 
 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css"> 
 
    <!-- You can use open graph tags to customize link previews.

    Learn more: https://developers.facebook.com/docs/sharing/webmasters -->

    <meta property="og:url"
           content="http://localhost:3000/giftlist/<%= giftlist._id %>" />

    <meta property="og:type"
          content="website" />

    <meta property="og:title"
          content="Giftlist App" />

    <meta property="og:description"
         content="<%= giftlist.name %>" /> 
  
 
</head> 
<body> 
<div id="fb-root"></div>
<script>(function(d, s, id)
 {

        var js, fjs = d.getElementsByTagName(s)[0];

        if (d.getElementById(id)) return;

        js = d.createElement(s); js.id = id;

        js.src =
 "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.5&appId=228887303845448";

        fjs.parentNode.insertBefore(js, fjs);

    }
(document, 'script', 'facebook-jssdk'));
</script>
<nav class="nav navbar-default">

    <div class="container-fluid">

        <div class="navbar-header">

            <a class="navbar-brand" href="#">Giftlist</a>

        </div>

    </div>

</nav> 
 
 
<div class="container"> 
    <h1><%= giftlist.name %></h1> 
 
    <div class="row"> 
        <div class="col-md-12"> 
            <ul> 
                <% giftlist.gifts.forEach(function(gift, index){ -%> 
                <li><%= gift.name %></li> 
                <% }); -%> 
            </ul> 
            <a href="https://twitter.com/share" class="twitter-share-button" data-via="JohnMooreNinja" data-size="large" data-hashtags="giftapp">Tweet</a>
            <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');
</script>

            <div class="fb-like"></div> 
 
        </div> 
    </div> 
 
 
</div> 
 
 
</body> 
</html> 

We add some open graph tags, and some code to enable Twitter and Facebook sharing.

Twitter has a neat form-based wizard to set up Twitter sharing buttons. You can find it at https://about.twitter.com/resources/buttons#tweet. You'll want to configure the Facebook button specifically for your app ID. Facebook also has a form-based configuration tool, at https://developers.facebook.com/docs/plugins/like-button.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.237.79