ExpressJS Fetching value from nested JSON Object - json

I am using ExpressJS..When trying to fetch value from the req.body, in the console.log(req.body[ndx])(it prints { " c o..... like this as single character)
for (var ndx in req.body){
console.log(req.body[ndx]);
}
My req.body has the below nested JSON Object: How to extract count?
req.body["count"] outputs undefined
{"count":1,"totalCount":38,"node":[{"categories":[],"foreignSource":null,"foreignId":null,"label":"172.20.96.20","assetRecord":{"description":null,"operatingSystem":null,"category":"Unspecified","password":null,"id":2655,"username":null,"vmwareManagedEntityType":null,"vmwareManagementServer":null,"numpowersupplies":null,"hdd6":null,"hdd5":null,"hdd4":null,"hdd3":null,"hdd2":null,"hdd1":null,"storagectrl":null,"thresholdCategory":null,"enable":null,"connection":null,"autoenable":null,"cpu":null,"ram":null,"snmpcommunity":null,"rackunitheight":null,"admin":null,"additionalhardware":null,"inputpower":null,"vmwareManagedObjectId":null,"vmwareState":null,"vmwareTopologyInfo":null,"circuitId":null,"assetNumber":null,"rack":null,"slot":null,"region":null,"division":null,"department":null,"building":null,"floor":null,"room":null,"vendorPhone":null,"manufacturer":null,"vendor":null,"modelNumber":null,"supportPhone":null,"maintcontract":null,"maintContractNumber":null,"maintContractExpiration":null,"displayCategory":null,"notifyCategory":null,"pollerCategory":null,"vendorFax":null,"vendorAssetNumber":null,"lastModifiedBy":"","lastModifiedDate":1433277477504,"dateInstalled":null,"lease":null,"leaseExpires":null,"managedObjectInstance":null,"managedObjectType":null,"serialNumber":null,"port":null,"comment":null},"lastCapsdPoll":1433277477793,"createTime":1433277477504,"labelSource":"A","type":"A","id":"10"}]}
My Code:
var bodyParser = require('body-parser');
var urlencodedParser = bodyParser.urlencoded({ extended: true });
app.use('/index', function(req, res, next){
var getReq = http.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
jsnArry += chunk;
});
res.on('end', function (chunk) {
req.body = jsnArry;
for (var ndx in req.body){
console.log(req.body[ndx]);
}
next();
});
}).end();
});
app.use(bodyParser.json({ type: 'application/*+json' }));
app.use('/index', urlencodedParser, function(req, res, next){
res.send(req.body);
next();
});
console.log(JSON.parse(req.body)) o/p below ones
{ count: 1,
totalCount: 38,
node:
[ { categories: [],
foreignSource: null,
foreignId: null,
label: '172.20.96.20',
assetRecord: [Object],
lastCapsdPoll: 1433277477793,
createTime: 1433277477504,
labelSource: 'A',
type: 'A',
id: '10' } ] }
var options = {
host: host,
method: 'GET',
headers: {
'Accept' : 'application/json'
}
};
res.json(info["totalCount"]);
res.sendFile(path.join(_dirname, '/html', 'index.html'));
//Only shows the res.json value not the html page
Below is my html file:
<!DOCTYPE html>
<html data-ng-app="shopStore">
<head>
<meta charset="ISO-8859-1">
<title>Simple Angular Testing</title>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"></link>
<link rel="stylesheet" type="text/css" href="css/main.css"></link>
</head>
<body>
<script type="text/javascript" src="js/lib/angular.min.js"></script>
<script type="text/javascript" src="js/lib/d3.min.js"></script>
<script type="text/javascript" src="js/product.js"></script>
<script type="text/javascript" src="js/app.js"></script>
<div data-ng-controller="scopeCtrl">
<div data-ng-repeat="x in newRec">
<p>I am product seller {{x.Name}} in {{x.City}} # {{x.Country}}</p>
</div>
{{newRec[0].Name}}
</div>
<div data-ng-controller="nmsCtrl">
<p>Data to display</p>
<!-- <div data-ng-repeat="jsn in jsnData">
<p>I am product seller {{jsn.count}} displayed out of {{jsn.totalCount}}</p>
</div> -->
</div>
<div data-ng-controller="Store-Controller as store">
<div data-ng-repeat="product in store.products">
<div data-ng-hide='product.cantPur'>
<h6>Product:::{{product.item}}</h6>
<h6>Dollar Price:::{{product.dollar | currency}}</h6>
<h6>Description::::{{product.desc}}</h6>
<h6>{{review.stars}}</h6>
<button data-ng-show='product.canAdd'>Add to Cart</button>
<product-panel></product-panel>
</div>
</div>
</div>
</body>
</html>

Do you include body-parsing middleware? body-parser support JSON parsing.
var app = require('express')();
var bodyParser = require('body-parser');
app.use(bodyParser.json());
bodyParser.json also expect to receive payload in application/json content type. Example using jQuery ajax:
.ajax({
url:url,
type:"POST",
data:data,
contentType:"application/json; charset=utf-8",
dataType:"json",
success: function(){
...
}
})

You probably need to convert the req.body into json using
bar myRes= JSON.parse(req.body);
console.log(myRes.count);
This should be done for you automatically. How is your bodyParser middleware set uup?
It should be set up as in the next post.

Related

Images Save Even When Form Submit Fails In Nodejs Project Using Multer and HTML

I am working on a Nodejs project and currently attempting to figure out how to prevent images uploaded through a form from being saved when the form submit fails (i.e. due to empty fields).
I have looked other several other posts, Google, and multer documentation but have not been able to figure out how to stop the upload from occurring. Here is the code to my repo: https://github.com/halsheik/RecipeWarehouse.git. Below, I have posted any relevant code. Thanks for any help.
// Modules required to run the application
const express = require('express');
const multer = require('multer');
const crypto = require('crypto');
const path = require('path');
const { ensureAuthenticated } = require('../config/auth');
// Creates 'mini app'
const router = express.Router();
// Models
const Recipe = require('../models/Recipe'); // Recipe Model
// Set up storage engine
const storage = multer.diskStorage({
destination: function(req, file, callback){
callback(null, 'public/uploads');
},
filename: function(req, file, callback){
crypto.pseudoRandomBytes(16, function(err, raw) {
if (err) return callback(err);
callback(null, raw.toString('hex') + path.extname(file.originalname));
});
}
});
const upload = multer({
storage: storage
});
// My Recipes
router.get('/myRecipes', ensureAuthenticated, function(req, res){
Recipe.find({}, function(err, recipes){
if(err){
console.log(err);
} else {
res.render('./home/myRecipes', {
recipes: recipes
});
}
});
});
// My Recipes
router.get('/createRecipe', ensureAuthenticated, function(req, res){
res.render('./home/createRecipe');
});
// Create Recipe
router.post('/createRecipe', ensureAuthenticated, upload.single('recipeImage'), function(req, res){
const { recipeName, recipeDescription, ingredients, directions } = req.body;
let errors = [];
// Checks that all fields are not empty
if(!recipeName || !recipeDescription || !ingredients || !directions){
errors.push({ msg: 'Please fill in all fields.' });
}
// Checks that an image is uploaded
if(!req.file){
errors.push({ msg: 'Please add an image of your recipe' });
}
// Checks for any errors and prevents recipe creation if any
if(errors.length > 0){
// Displays create Recipe form along with errors
res.render('./home/createRecipe', {
errors
});
} else {
// Create a new 'Recipe' using our model
const newRecipe = new Recipe({
recipeName: recipeName,
author: req.user._id,
recipeImageFileName: req.file.filename,
recipeDescription: recipeDescription,
ingredients: ingredients,
directions: directions,
});
// Saves recipe to mongoDB database
newRecipe.save().then(function(){
res.redirect('/recipes/myRecipes');
}).catch(function(err){
console.log(err);
});
}
});
// Get Single Recipe
router.get('/:id', function(req, res){
// Searches for a 'Recipe' with a unique 'id'
Recipe.findById(req.params.id, function(err, recipe){
if(err){
throw err;
}
// Renders the Recipe in its own page with full information
res.render('./home/recipe.ejs', {
recipe: recipe
});
});
});
// Delete recipe
router.delete('/:id', function(req, res){
const query = {_id: req.params.id}
Recipe.deleteOne(query, function(err){
if(err){
console.log(err);
throw err;
}
res.send('Success');
});
});
module.exports = router;
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Homemade</title>
<!-- Required program scripts -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>
<!-- Style Sheets-->
<link rel="stylesheet" href="/styles/navBarStyle.css">
<link rel="stylesheet" href="/styles/myRecipesStyle.css">
<link rel="stylesheet" href="/styles/createRecipeStyle.css">
</head>
<body>
<!-- Background image -->
<img id="background" src="/images/foodBackground.jpg" alt="">
<div id="newRecipeContainer">
<div id="closeButtonContainer">
<div id="backButton"><a id="back" href="/recipes/myRecipes">← My Recipes</a></div>
</div>
<form id="createRecipeForm" action="/recipes/createRecipe" method="POST" enctype="multipart/form-data">
<label id="formSubHeading">Create Your Homemade Recipe</label>
<div id="recipeNameContainer">
<label id="recipeNameLabel">Title</label>
<input id="recipeNameInput" type="text" name="recipeName">
</div>
<div id="recipeImage">
<label id="recipeImageLabel">Add An Image of Your Meal</label>
<input id="recipeImageInput" type="file" accept="image/*" name="recipeImage"/>
<label id="recipeImageInputLabel" for="recipeImageInput" name="recipeImage">Choose A File</label>
</div>
<div id="recipeDescription">
<label id="recipeDescriptionLabel">Description</label>
<textarea id="recipeDescriptionInput" name="recipeDescription" cols="30" rows="10" maxlength="2000"></textarea>
</div>
<div class="ingredientsContainer">
<label id="ingredientsLabel">Ingredients</label>
<button id="addIngredientButton" type="button" #click="addIngredientForm">Add Another Ingredient</button>
<div class="allIngredients" v-for="(ingredient, ingredientIndex) in ingredients">
<label class="ingredientLabel">{{ ingredientIndex + 1 }}.)</label>
<input class="ingredientInput" type="text" name="ingredients" v-model="ingredient.ingredient">
<button class="deleteIngredientButton" type="button" v-if="ingredientIndex > 0" #click="deleteIngredientForm(ingredientIndex)">X</button>
</div>
</div>
<div class="directionsContainer">
<label id="directionsLabel">Directions</label>
<button id="addDirectionButton" type="button" #click="addDirectionForm">Add Another Direction</button>
<div class="allDirections" v-for="(direction, directionIndex) in directions">
<label class="directionLabel">{{ directionIndex + 1 }}.)</label>
<input class="directionInput"type="text" name="directions" v-model="direction.direction">
<button class="deleteDirectionButton" type="button" v-if="directionIndex > 0" #click="deleteDirectionForm(directionIndex)">X</button>
</div>
</div>
<div id="createRecipeButtonContainer">
<button id="createRecipeButton" type="submit">Create Recipe</button>
</div>
</form>
</div>
<script src="/controls/newRecipeControl.js"></script>
</body>
</html>
Thanks again for any help.
There are 2 ways to I know of which can stop the upload
You can use fileFilter. It is one of the options passed to multer. But it can only be used to check for filetype, etc because req.body may only contain fields that appear before the file in the form.
const upload = multer({
storage: storage,
fileFilter: (req, file, cb) => {
// req.body is NOT reliable here
// it may only contain the some of the fields
// To reject this file pass `false`, like so:
cb(null, false)
// To accept the file pass `true`, like so:
cb(null, true)
// You can always pass an error if something goes wrong:
cb(new Error('I don\'t have a clue!'))
}
});
router.post('/createRecipe', ensureAuthenticated, upload.single('recipeImage'), function(req, res){
if(!req.file){
//the file was not uploaded
}
...
}
Also I looked into your repo. You are passing fileFilter as an option to multer.diskStorage. This doesn't do anything. multer.diskStorage accepts only 2 options destination and filename. Pass it to multer instead
Use MemoryStorage (the default one or an alternative) and only save to disk if the conditions are satisfied

How to fix ReferenceError: X is not defined

I am getting an error that shows my function "loaddata" is not defined. However, I have placed the script at the top of the page with no success. Any ideas why this is happening?
Thank you!
<script type="text/javascript">
function loaddata(){
var sid = document.getElementById("student_id");
if(sid) {
$.ajax({
type: 'post',
url: 'search.php',
data: {
student_id:sid,
},
success: function (response) {
// We get the element having id of display_info and put the response inside it
$( '#display_info' ).html(response);
}
});
}
else
{
$( '#display_info' ).html("Please Enter Some Words");
}
}
</script>
</head>
<body>
<div class="content">
<H2>Lookup Member</H2>
<input type="text" name="student_id" id="student_id" onkeyup="loaddata();">
</div>
</body>
Work for me in a single html file.
I comment your jquery code just for test.
<head>
<script type="text/javascript">
function loaddata() {
var sid = document.getElementById("student_id");
if (sid) {
// $.ajax({
// type: 'post',
// url: 'search.php',
// data: {
// student_id: sid,
// },
// success: function(response) {
// // We get the element having id of display_info and put the response inside it
// $('#display_info').html(response);
// }
// });
} else {
$('#display_info').html("Please Enter Some Words");
}
}
</script>
</head>
<body>
<div class="content">
<H2>Lookup Member</H2>
<input type="text" name="student_id" id="student_id" onkeyup="loaddata();">
</div>
</body>

create a search bar jsvue from json object

I want to create a search bar that pulls searches through the data of a json object and displays data to the user. I currently have code that looks like this and it works fine.
<html>
<head>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> </head> <body> <div id='app'> <input type='text' v-model='keyword' placeholder='search title'> <button v-on:click="">automotive</button> <div v-for="post in filteredList"> <iframe width="420" height="315" v-bind:src="post.link"> </iframe> <a v-bind:href="post.link">{{post.title}}</a> </div> </div>
<script> "use strict";
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Post = function Post(title, link, author, img) {
_classCallCheck(this, Post);
this.title = title; this.link = link; this.author = author; this.img = img; }; var app = new Vue({ el: '#app', data: {
keyword:'',
postList: [
new Post(
'Vue.js',
'https://www.youtube.com/embed/tgbNymZ7vqY',
'Chris',
'https://vuejs.org//images/logo.png'
),
new Post(
'React.js',
'https://www.youtube.com/embed/k3frK9-OiQ0',
'Tim',
'http://daynin.github.io/clojurescript-presentation/img/react-logo.png'
),
new Post(
'Angular.js',
'https://angularjs.org/',
'Sam',
'https://angularjs.org/img/ng-logo.png',
),
new Post(
'Ember.js',
'http://emberjs.com/',
'Rachel',
'http://www.gravatar.com/avatar/0cf15665a9146ba852bf042b0652780a?s=200'
),
new Post(
'Meteor.js',
'https://www.meteor.com/',
'Chris',
'http://hacktivist.in/introduction-to-nodejs-mongodb-meteor/img/meteor.png'
),
new Post(
'Aurelia',
'http://aurelia.io/',
'Tim',
'https://cdn.auth0.com/blog/aurelia-logo.png'
),
new Post(
'Node.js',
'https://nodejs.org/en/',
'A. A. Ron',
'https://code-maven.com/img/node.png'
),
new Post(
'Pusher',
'https://pusher.com/',
'Alex',
'https://avatars1.githubusercontent.com/u/739550?v=3&s=400'
),
new Post(
'Feathers.js',
'http://feathersjs.com/',
'Chuck',
'https://cdn.worldvectorlogo.com/logos/feathersjs.svg'
), ] }, methods: {
}, computed:{
filteredList(){
return this.postList.filter((post) => {
return post.title.toLowerCase().includes(this.keyword.toLowerCase());
});
} } })
</script> </body> <html>
Ignore what the links are going to it doesnt matter. The problem Im having however is getting this to work from an external source via an axios request. I've done axios request before and got json data back but im struggling to make this search feature work with it. The following is an example of the broken code (ignore the v-on:click its not set up yet. ignore the fact there are no videos I can deal with that later I just need the search feature to work with an json data from an axios request) but I keep getting errors like 'type error : this.item is not defined' and 'object error' anyway heres the code:
<html>
<head>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div id='app'>
<input type='text' v-model='keyword' placeholder='search item'>
<button v-on:click="">automotive</button>
<div v-for="item in filteredList">
<p>{{item.name}}</p>
</div>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
keyword:'',
itemList: '',
},
created: function() {
this.loaddata();
},
methods: {
loaddata: function(){
var vueapp = this;
axios.get('https://jsonplaceholder.typicode.com/users').then(function (response){
vueapp.itemList = response.data;
})
},
},
computed:{
filteredList(){
return this.item.filter((item) => {
return item.name.toLowerCase().includes(this.keyword.toLowerCase());
});
}
}
})
</script>
</body>
<html>
You don't have item declared in your data. you can't call this.item.filter in your filteredList() method when you don't have it declared.
You can change your filteredList with this.itemList.filter() which is the list being loaded in loaddata()

Ng-view or ui-view not displaying html page

I am relatively new to Angularjs, and am building a website. When I try to inject todo.html into the body tags of index.html nothing happens. I am not getting any errors in the console. I have read many of the similar posts to mine, and have already tried
Remove the ng-include from the body of index.html
Moved the links for angualrjs and bootstrap from the body of index.html to the head
Originally I used Ng-route but it did not work, so I implemented ui-router
I have tried both ng-route and ui-router,and both run without any errors. I don't think it has anything to do with either.
index.html
<html ng-app="todoApp">
<head>
<!-- META -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"><!-- Optimize mobile viewport -->
<title>Todo App</title>
<!-- Angular ans JS links-->
<script src="vendor/angular/angular.min.js"></script>
<script src="vendor/angular-ui-router/release/angular-ui-router.min.js"></script>
<script src="app/app.js"></script>
<script src="app/services/todo.service.js"></script>
<script src="app/controllers/todo.controller.js"></script>
<!-- <script src="vendor/angular-route/angular-route.min.js"></script>-->
<!--Jquery and Bootstrap Links-->
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://npmcdn.com/tether#1.2.4/dist/js/tether.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.4/js/bootstrap.min.js" integrity="sha384-VjEeINv9OSwtWFLAtmc4JCtEJXXBub00gtSnszmspDLCtC0I4z4nqz7rEFbIZLLU"
crossorigin="anonymous"></script>
<!-- css links -->
<link href="vendor/bootstrap-css-only/css/bootstrap.min.css" rel="stylesheet"><!-- load bootstrap -->
<link rel="stylesheet" href="assets/css/todoApp.css">
<link rel="stylesheet" type="text/css" href="assets/css/Header-Picture.css">
</head>
<body >
<div ng-include="'app/views/header.html'"></div>
<!--<div ng-include="'app/views/footer.view.html'"></div>
-->
<ui-view></ui-view>
<!--<div ui-view></div>-->
</body>
</html>
App.js
var todoApp = angular.module('todoApp', [
'ui.router'
]);
todoApp.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('todo', {
url: "/",
templateUrl: 'views/todo.html',
controller: 'TodoController'
})});
todo.controller.js
todoApp.controller('TodoController', ['$scope', 'Todos', function TodoController($scope, Todos) {
$scope.formData = {};
console.log("in the TodoController");
// when landing on the page, get all todos and show them
Todos.get()
.success(function(data) {
$scope.todos = data;
});
// when submitting the add form, send the text to the spring API
$scope.createTodo = function() {
if(!$scope.todoForm.$valid) {
return;
}
Todos.create($scope.formData)
.success(function(data) {
$scope.formData = {}; // clear the form so our user is ready to enter another
$scope.todos.push(data);
});
};
// delete a todo after checking it
$scope.deleteTodo = function(id) {
Todos.delete(id)
.success(function(data) {
angular.forEach($scope.todos, function(todo, index){
if(todo.id == id) {
$scope.todos.splice(index, 1);
}
});
});
};
// when submitting the add form, send the text to the node API
$scope.saveTodo = function(todo) {
Todos.update(todo)
.success(function(data) {
$scope.editedTodo = {};
});
};
$scope.editedTodo = {};
$scope.editTodo = function(todo) {
$scope.editedTodo = todo;
}
$scope.revertTodo = function() {
$scope.editedTodo = {};
}
}]);
You should be using otherwise to force the first state to be loaded as below
app.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('todo', {
url: "/",
templateUrl: 'todo.html',
})
$urlRouterProvider.otherwise('/');
});
Your index.html will look like
<div ng-include="'app/views/header.html'"></div>
<ui-view>
</ui-view>
LIVE DEMO
I added the code posted by #Aravind to my project which I belive was an improvement on my own and was correct. But the issue was the file path to the todo.html. The file path in the original was views/todo.html
the correct path is app/views/todo.html
My original code:
todoApp.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('todo', {
url: "/",
templateUrl: 'views/todo.html',
controller: 'TodoController'
})});
Current Working Code
todoApp.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('todo', {
url: "/",
templateUrl: 'app/views/todo.html',
})
$urlRouterProvider.otherwise('/');
});

Flot, angularjs and getting data to plot via http get

wowee....can't use flask to return a json object to plot a flot chart in angularjs.
Totally does not work. I use the hard coded json...the chart shows. Whats the deal with a get requests in angularjs? I go to localhost:5000/datatest and I see my json object. Yet angular will not plot a valid json object?
In flask..
#app.route('/datatest')
def datatest():
test1 = [[[0, 1], [1, 5], [2, 2]]]
data = json.dumps(test1)
resp = Response(data, status=200, mimetype='application/json')
return resp
My Controller and Directive.
var App = angular.module('App', []);
App.controller('Ctrl', function ($scope,$http) {
$http.get('datatest').success(function(data) {
$scope.data = data;
});
//$scope.data = [[[0, 1], [1, 5], [2, 2]]];
});
App.directive('chart', function() {
return {
restrict: 'E',
link: function(scope, elem, attrs) {
var data = scope[attrs.ngModel];
$.plot(elem, data, {});
elem.show();
}
};
});
My HTML:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="static/js/jquery/jquery-2.1.0.js"></script>
<script type="text/javascript" src="static/js/flot/jquery.flot.js"></script>
<script type="text/javascript" src="static/js/angular/angular.min.js"></script>
<script type="text/javascript" src="static/lib/flot/controller.js"></script>
<style type='text/css'>
chart {
display:none;
width:400px;
height:200px;
}
</style>
</head>
<body>
<div ng-app='App'>
<div ng-controller='Ctrl'>
<chart ng-model='data'></chart>
</div>
</div>
</body>
</html>
Your directive is calling $plot before $http finishes getting data. Instead, you can watch the data array in your directive, and call $plot when it changes:
app.directive('chart', function() {
return {
restrict: 'E',
scope: {
data: '='
},
link: function(scope, elem, attrs) {
scope.$watch('data', function() {
if (scope.data.length > 0) {
$.plot(elem, scope.data, {});
elem.show();
}
})
}
};
});
html: <chart data='data'></chart>
Here is a demo: http://plnkr.co/7nx2Xf5i1OfLEkzMdwNm