Related
I have Vue displaying a few copies of a component according to the data source in the root instance. The data is a nested JS object with children. Pointers to parent nodes are added once the data is loaded. In the view I can drill down and up, displaying one level of the hierarchy at a time. No recursion is used in the view.
My question is: Why don't the child components disappear when their parent node is deleted?
Please ignore the lengthy CSS: it's part of larger project and not relevant to the question at hand.
Since SO snippets don't display this well, here's a CodePen: https://codepen.io/MSCAU/pen/RmWOWE. The difficulty is that it still doesn't allow you to fire up Vue DevTools and peek under the hood so I'll try and simplify further or get a standalone page up.
var source = {
"name": "root",
"value": 9,
"id": 0,
"children": [{
"name": "Nodes",
"value": 32,
"id": 100,
"children": [{
"name": "Fish",
"value": 20,
"id": 1,
"children": [{
"name": "Cod",
"value": 5,
"id": 10,
},{
"name": "Salmon",
"value": 15,
"id": 110,
}]
}, {
"name": "Drinks",
"value": 12,
"id": 3,
"children": [{
"name": "Juice",
"value": 8,
"id": 11,
},
{
"name": "Wine",
"value": 4,
"id": 12,
}]
}]
}]
};
function clone(obj) {
/* Standard clone function */
if(obj === null || typeof(obj) !== 'object' || 'isActiveClone' in obj)
return obj;
var temp = obj.constructor(); // changed
for(var key in obj) {
if(Object.prototype.hasOwnProperty.call(obj, key)) {
obj['isActiveClone'] = null;
temp[key] = clone(obj[key]);
delete obj['isActiveClone'];
}
}
return temp;
}
Vue.component('bar-wrapper', {
props: ['node', 'index', 'side'],
template: '#bar-wrapper',
methods: {
amount(node) {
// bar.value = bar.old_value / 100 * this.root;
return Math.abs(node.value / node.parent.value) * 100;
},
drillDown(side, node, event) {
this.$emit('drill-to', side, node);
}
}
});
var app = new Vue({
el: '#main',
data: {
db: {
tree: clone(source)
},
expenses: {
children: []
}
},
computed: {
expenses_view_nodes() {
return this.expenses.children;
}
},
methods: {
amount(node) {
return Math.abs(node.value / node.parent.value) * 100;
},
compare(a, b) {
if (Math.abs(a.value) < Math.abs(b.value))
return 1;
if (Math.abs(a.value) > Math.abs(b.value))
return -1;
return 0;
},
makeParents(node) {
var _this = this;
if (!node.hasOwnProperty("children"))
return false;
else {
var parent_node = node;
$.each(node.children, function() {
this.parent = parent_node;
_this.makeParents(this);
});
}
return node;
},
sortMe() {
if (this.expenses && this.expenses.children) {
this.expenses.children.sort(this.compare);
}
return false;
},
drillDown(side, node) {
this.expenses = node;
},
drillUp(side, node, event) {
if (node.parent && node.parent.id != 0) {
this.expenses = node.parent;
}
},
insertCake() {
this.db.tree.children[0].children.push({id: Math.floor(Math.random() * 10000), name: "Cake", value: 14, parent: this.db.tree.children[0]});
this.db.tree.children[0].value += 14;
},
deleteFish() {
var this_amount = this.db.tree.children[0].children[0].value;
this.db.tree.children[0].children.splice(0,1);
this.db.tree.children[0].value -= this_amount;
},
deleteChild() {
// Vue.delete(this.db.tree.children[0].children[0].children, 0);
var this_amount = this.db.tree.children[0].children[0].children[0].value;
this.db.tree.children[0].children[0].children.splice(0,1);
this.db.tree.children[0].children[0].value -= this_amount;
this.db.tree.children[0].value -= this_amount;
},
init() {
this.db.tree = clone(source);
this.expenses = this.db.tree.children[0];
this.makeParents(this.db.tree);
this.sortMe();
}
},
watch: {
db: {
handler(newVal, oldVal) {
console.log("Watched the db");
},
deep: true
}
},
beforeCreate() {
console.log("%cBefore create hook: Component is not created and data variables are not available yet", "color:green");
},
created() {
console.log("%cCreated hook: Component is created and data variables are available", "color:green");
this.init();
},
beforeMount() {
console.log("%cBefore mount hook: Component is not mounted on DOM yet", "color:green");
},
mounted() {
console.log("%cMounted hook: Component is mounted on DOM", "color:green");
},
beforeUpdate() {
console.log("%cBefore update hook: Component is not updated yet", "color:green");
},
updated() {
console.log("%cUpdated hook: Component is updated", "color:green");
},
beforeDestroy() {
console.log("%cBefore destroy hook: Component is about to be destroyed", "color:green");
},
destroyed() {
console.log("%cDestroyed hook: Component is destroyed", "color:green");
}
});
a {
color: #659B5E;
}
a:hover, a:focus {
color: #3c5c37;
}
.btn {
font-size: 12px;
letter-spacing: 0.5px;
padding: 6px 18px;
text-transform: uppercase; }
.wrapper {
position: relative;
width: 100%;
}
.charts {
position: relative;
top: 0%;
text-align: center;
vertical-align: middle;
cursor: move;
transition: all 1s;
padding: 0px 10px 0px;
margin-bottom: 20px; }
.chart {
position: relative;
}
.chart-left {
float: left;
margin-right: 0.5%; }
.chart-right {
float: right;
margin-left: 0.5%; }
.bar-wrapper {
position: relative; }
.bar {
position: relative;
padding: 5px 0px;
margin-bottom: 30px;
height: 34px;
font-size: 1.2em;
opacity: 1;
-webkit-box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.2);
box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.2); }
.bar.parent {
cursor: pointer; }
.bar.parent .bar-label > a:after {
content: '\02026'; }
.bar:not(.parent) {
cursor: default; }
.chart-left .bar-wrapper {
transform-origin: right center;
height: 64px;
}
.chart-right .bar-wrapper {
transform-origin: left center;
height: 64px;
}
.bar-label {
position: absolute;
white-space: nowrap;
overflow: visible;
width: 100%;
pointer-events: none; }
.bar-label > a {
color: white;
display: inline-block;
max-width: 100%;
transition: transform 0.2s, color 0.2s, text-shadow 0.2s;
pointer-events: auto;
text-decoration: none; }
.bar-label > a:hover {
text-decoration: underline; }
.bar-label > a.no-description {
text-decoration: none;
cursor: default; }
.bar-label .popover-footer {
padding: 9px 14px 9px;
font-size: 12px;
background-color: #f8f8f8;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-areas: "type edit"; }
.bar-label .popover-footer .node-type {
grid-area: type;
color: #bbb;
text-transform: uppercase; }
.bar-label .popover-footer .node-type .fa, .bar-label .popover-footer .node-type .fas, .bar-label .popover-footer .node-type .far {
margin-right: 10px; }
.bar-label .popover-footer .node-edit {
grid-area: edit;
text-align: right; }
.bar-label .popover-footer .node-edit a {
pointer-events: auto;
display: none; }
.chart-left .bar-fluid .bar-label {
left: 0%;
padding-left: 22px; }
.chart-left .bar-fixed .bar-label {
left: 0%;
padding-left: 12px; }
.chart-left .compare {
right: 0%;
background-color: #e0e0e0;
padding-left: 12px;
text-align: left;
border-right: 1px solid #f8f8f8;
margin-right: 0;
margin-left: auto; }
.chart-left .bar-label .popover {
left: -40px !important;
right: auto !important; }
.chart-left .bar-label > a {
text-align: left;
text-shadow: -3px 0px 4px #f46d43, 3px 0px 4px #f46d43, 0px 3px 4px #f46d43, 0px -3px 4px #f46d43; }
.chart-left .bar-label > a.outside {
text-align: right; }
.charts .bar-label > a.outside {
text-shadow: none;
color: #999; }
.chart-left .bar {
background: linear-gradient(#f14813 4%, #f46d43 5%, #f14813 95%, #f79273 96%);
background-position: left top;
border-right: none;
text-align: left;
margin-left: auto;
margin-right: 0px;
overflow: visible !important; }
.chart-left .bar:hover .sub-bar {
background-color: rgba(0, 0, 0, 0.15);
box-shadow: inset 0 0 2px #ffffff; }
.chart-left bar.bar-fluid {
background: linear-gradient(#f14813 4%, #f46d43 5%, #f14813 95%, #f79273 96%); }
.chart-left .bar-fixed {
background: repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.2) 10px, transparent 10px, transparent 20px), linear-gradient(#f14813 4%, #f46d43 5%, #f14813 95%, #f79273 96%); }
.bar-fixed .bar-handle {
display: none; }
.chart-left .bar.hiding {
-webkit-animation: remove-left 0.5s ease forwards;
/* Chrome, Safari, Opera */
animation: remove-left 0.5s ease forwards; }
.chart-left .bar.showing {
-webkit-animation: add-left 0.5s ease forwards;
/* Chrome, Safari, Opera */
animation: add-left 0.5s ease forwards; }
.amount, .charts .percentage {
position: absolute;
white-space: nowrap; }
.chart-left .bar-fixed .amount, .chart-left .bar-fixed .percentage {
text-align: right;
right: 100%;
padding-right: 10px; }
.chart-left .bar-fluid .amount, .chart-left .bar-fluid .percentage, .chart-left .compare .amount, .chart-left .compare .percentage {
text-align: right;
right: 100%;
padding-right: 22px; }
.chart-right .bar-fixed .amount, .chart-right .bar-fixed .percentage {
text-align: left;
left: 100%;
padding-left: 10px; }
.chart-right .bar-fluid .amount, .chart-right .bar-fluid .percentage, .chart-right .compare .amount, .chart-right .compare .percentage {
text-align: left;
left: 100%;
padding-left: 22px; }
.shadowed {
box-shadow: 1px 2px 5px 1px rgba(0, 0, 0, 0.1); }
.chart-left .line, .chart-left .sub-bar {
right: 0px;
border-left: 1px solid rgba(255, 255, 255, 0.2);
border-right: 1px solid rgba(255, 255, 255, 0.2); }
.chart-right .line, .chart-right .sub-bar {
left: 0px;
border-left: 1px solid rgba(255, 255, 255, 0.2);
border-right: 1px solid rgba(255, 255, 255, 0.2); }
.sub-bar {
height: 34px;
position: absolute;
top: 0px;
opacity: 1;
text-align: center; }
.sub-bar span {
display: none; }
.chart-left .sub-bar {
right: 0px;
}
.chart-right .sub-bar {
left: 0px;
}
body {
margin: 50px;
}
.chart {
float: none;
cursor: pointer;
border: 1px solid lightgrey;
background-color: #eee;
}
.bars {
height: 300px;
}
.bar {
height: 34px;
margin-bottom: 26px;
}
.sub-bar {
display: inline-block;
}
.list-move {
transition: transform 1s;
}
.tools {
margin: 30px auto;
text-align: center;
}
.tools button {
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/x-template" id="bar-wrapper">
<div>
<div class="bar bar-fluid" v-bind:style="{display: 'flex', 'flex-direction': 'row-reverse', 'padding': 0, transition: 'none', width: amount(node) + '%'}">
<div class="sub-bar" v-for="(no, index) in node.children" v-bind:key="no.id" v-bind:style="{position: 'static', width: amount(no) + '%'}"></div>
<div class="bar-label" style="top: 5px">
<a v-if="node.children" #click.stop.prevent="drillDown(side, node, $event)">{{node.name}}...</a>
<span v-else>{{node.name}}</span>
</div>
</div>
</div>
</script>
<div id="main">
<center>
<p>
<i>Click on a bar label to drill down. Click on the chart background to come back up.</i>
</p>
<b>
<p>
<u>How to reproduce:</u> Click on <kbd>Fish...</kbd>, then click on <kbd>Delete Fish</kbd> button.
</p>
<p>
<u>Question:</u> Why does the view not update when I am looking at Cod and Salmon (inside Fish), and I delete the Fish node?
</p>
</b>
</center>
<div class="tools">
<button #click="deleteFish">Delete Fish</button>
<button #click="deleteChild">Delete first child</button>
<button #click="insertCake">Insert Cake</button>
<button #click="sortMe">Sort</button>
<button #click="init">Reset</button>
</div>
<div class="charts">
<div class="chart chart-left" #click="drillUp(0, expenses, $event)">
<transition-group name="list" tag="div" class="bars">
<bar-wrapper v-on:drill-to="drillDown" v-for="(node, index) in expenses_view_nodes" :key="node.id" class="wrapper" :node="node" :index="index" :side="0" :style="{position: 'absolute', top: index * 60 + 'px'}"></bar-wrapper>
</transition-group>
</div>
</div>
</div>
In JS, splicing an element from an array doesn't remove it from memory, so my expenses_view_nodes is still valid, eventhough the node's been removed from the parent's children array.
The way I resolve this is - after deleting the Fish node - to check whether the view pointer (expenses_view_nodes) points to a node that is still in the tree. If not, change it to point to Fish's parent node (id = 100). Vue then redraws as expected.
When removing items from an array in Vue, try wrapping the actual .splice operation in setTimeout.
Instead of something like:
del: function(item) {
this.list.splice(this.list.indexOf(item), 1);
}
do this:
del: function(item) {
var list = this.list, index = list.indexOf(item);
setTimeout(function() { list.splice(index, 1); }, 0);
}
I'm using Ajax to fetch records from API , when i click on search items , page get redirected to the book and author page. i want to store the record of that search.But it fails to store the data in mongoose .
Data is not saving.
My task is to list last 15 search which user search. and show on history page .Please help me guys
<!DOCTYPE html>
<html lang="en">
<head>
<title>Book Bank API</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script
src="http://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/corejs-typeahead/1.2.1/typeahead.bundle.min.js"></script>
</head>
<style>
/* scaffolding */
/* ----------- */
.tt-menu,
.gist {
text-align: left;
}
/* base styles */
/* ----------- */
html {
font: normal normal normal 18px/1.2 "Helvetica Neue", Roboto, "Segoe UI", Calibri, sans-serif;
color: #292f33;
}
a {
color: #03739c;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.table-of-contents li {
display: inline-block;
*display: inline;
zoom: 1;
}
.table-of-contents li a {
font-size: 16px;
color: #999;
}
p + p {
margin: 30px 0 0 0;
}
/* site theme */
/* ---------- */
.title {
margin: 20px 0 0 0;
font-size: 64px;
}
.example {
padding: 30px 0;
}
.example-name {
margin: 20px 0;
font-size: 32px;
}
.demo {
position: relative;
*z-index: 1;
margin: 50px 0;
}
.typeahead,
.tt-query,
.tt-hint {
width: 396px;
height: 30px;
padding: 8px 12px;
font-size: 24px;
line-height: 30px;
border: 2px solid #ccc;
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
outline: none;
}
.typeahead {
background-color: #fff;
}
.typeahead:focus {
border: 2px solid #0097cf;
}
.tt-query {
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
.tt-hint {
color: #999
}
.tt-menu {
width: 600px;
margin: 12px 400px;
padding: 8px 0;
background-color: #fff;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
box-shadow: 0 5px 10px rgba(0,0,0,.2);
}
.tt-suggestion {
padding: 3px 20px;
font-size: 18px;
line-height: 24px;
}
.tt-suggestion:hover {
cursor: pointer;
color: #fff;
background-color: #0097cf;
}
.tt-suggestion.tt-cursor {
color: #fff;
background-color: #0097cf;
}
.tt-suggestion p {
margin: 0;
}
.gist {
font-size: 14px;
}
/* example specific styles */
/* ----------------------- */
#custom-templates .empty-message {
padding: 5px 10px;
text-align: center;
}
#multiple-datasets .league-name {
margin: 0 20px 5px 20px;
padding: 3px 0;
border-bottom: 1px solid #ccc;
}
#scrollable-dropdown-menu .tt-menu {
max-height: 150px;
overflow-y: auto;
}
#rtl-support .tt-menu {
text-align: right;
}
#multiple-datasets .search {
margin: 0 20px 5px 20px;
padding: 3px 0;
border-bottom: 1px solid #ccc;
}
</style>
<style>
.topnav-right {
float: right;
}
body {
font-family: Arial;
}
* {
box-sizing: border-box;
}
form.example input[type=text] {
padding: 10px;
font-size: 17px;
border: 1px solid grey;
float: left;
height: 50px;
width: 1000px;
text-align: center;
background: #f1f1f1;
margin-left: 80px;
}
</style>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand m-auto" href="#">Book Bank</a>
</div>
<div class="topnav-right">
<ul class="nav navbar-nav -align-left">
<li class="active">Home</li>
<li>Last 15 Search</li>
<li>Logout</li>
</ul>
</div>
</div>
</nav>
<div class="container">
<!--
<div class="panel panel-default">
<div class="panel-body">
<b> Authors selected: </b>
<ul id="authorlist"></ul>
</div>
</div>
<div class="panel panel-default">
<div class="panel-body">
<b> Books selected: </b>
<ul id="booklist"></ul>
</div>
-->
<br>
<br>
<div id="multiple-datasets">
<form class="example" action="#">
<input class="typeahead" type="text" placeholder="Enter The Book Name to Search" name="search">
</form>
</div>
<table class="table table-bordered">
<h1 align="center">List of Science Book Records</h1>
<thead>
<tr>
<th>Book Name</th>
<th>Author Name</th>
<th>Subject</th>
</tr>
</thead>
<tbody class="mypanel">
<script>
$.getJSON('https://openlibrary.org/subjects/science.json', function(data) {
var show_per_page = 2;
var page= data.works.length;
var number_page = Math.ceil(page/show_per_page);
console.log(data)
for (var i=1;i<data.works.length;i++)
{
var akey = `${data.works[i].authors[0].key}`
var bkey =`${data.works[i].key}`
var text = `<tr><td>${data.works[i].title}</td>
<td>${data.works[i].authors[0].name}</td>
<td>${data.works[i].subject[i]}</td></tr>`
$(".mypanel").append(text);
}
});
</script>
</tbody>
</table>
</div>
</body>
<script>
var books = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace,
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: 'http://openlibrary.org/search.json?title=%QUERY',
wildcard: '%QUERY',
filter: function (searchResults) {
return $.map(searchResults.docs, function (searchResults) {
// console.log(searchResults.author_name);
// console.log("key is "+searchResults.key)
if (JSON.parse(sessionStorage.getItem("selectedBooks") == undefined || JSON.parse(sessionStorage.getItem("selectedBooks").indexOf(searchResults.title)) == -1)){
return {
title: searchResults.title,
key: searchResults.key,
};
}
});
}
}
});
var authorsList = [];
var authors = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace,
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: 'http://openlibrary.org/search.json?author=%QUERY',
wildcard: '%QUERY',
filter: function (searchResults) {
return $.map(searchResults.docs, function (searchResults) {
if (searchResults.author_name !== undefined){
var author = searchResults.author_name.toString();
}
if (authorsList.indexOf(author) == -1) {
authorsList.push(author);
return {
author_key: searchResults.author_key,
author: author,
};
}
});
},
}
});
$('#multiple-datasets .typeahead').typeahead({
highlight: true
},
{
display: 'title',
source: books,
templates: {
header: '<h3 class="search">Books List</h3>'
}
},
{
name: 'authors',
display: 'author',
source: authors,
templates: {
header: '<h3 class="search">Authors List</h3>'
}
});
$('#multiple-datasets').bind('typeahead:selected', function(obj, datum, name) {
console.log(datum);
if (name === 'authors'){
var request = $.ajax({
type: 'POST',
data: {author: datum.author}
});
`${data.works[i].authors[0].key}`
request.done(function (msg) {
window.location.href="/author/authors/" +datum.author_key[0];
});
request.fail(function (jqXHR,textStatus) {
alert("Request Failed"+textStatus);
});
}
else
{
var request = $.ajax({
type: 'POST',
url: `http://localhost:9090/history`,
data: {book: datum.title}
});
request.done(function (msg) {
window.location.href="/book" +datum.key;
});
request.fail(function (jqXHR,textStatus) {
alert("Request Failed"+textStatus);
});
}
});
</script>
</html>
Route Page
app.post('/history',(req,res)=>{
console.log(req.body);
res.sendStatus(201);
const newHistory = new History({
books_searched: req.body
});
newHistory.save().then(savedPost=>{
console.log("data saved");
});
});
Error
UnhandledPromiseRejectionWarning: ValidationError: History validation
failed: books_searched: Cast to String failed for value "{ book:
'Aldous Huxley' }" at path "books_searched"
at new ValidationError (C:\Users\shankar\first-app\book api\node_modules\mongoose\lib\error\validation.js:30:11)
at model.Document.invalidate (C:\Users\shankar\first-app\book api\node_modules\mongoose\lib\document.js:1898:32)
at model.$set (C:\Users\shankar\first-app\book api\node_modules\mongoose\lib\document.js:878:10)
at model._handleIndex (C:\Users\shankar\first-app\book api\node_modules\mongoose\lib\document.js:688:14)
at model.$set (C:\Users\shankar\first-app\book api\node_modules\mongoose\lib\document.js:645:22)
at model.Document (C:\Users\shankar\first-app\book api\node_modules\mongoose\lib\document.js:112:12)
at model.Model (C:\Users\shankar\first-app\book api\node_modules\mongoose\lib\model.js:69:12)
at new model (C:\Users\shankar\first-app\book api\node_modules\mongoose\lib\model.js:4344:13)
at app.post (C:\Users\shankar\first-app\book api\app\routes.js:73:33)
at Layer.handle [as handle_request] (C:\Users\shankar\first-app\book
api\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\shankar\first-app\book api\node_modules\express\lib\router\route.js:137:13)
at Route.dispatch (C:\Users\shankar\first-app\book api\node_modules\express\lib\router\route.js:112:3)
at Layer.handle [as handle_request] (C:\Users\shankar\first-app\book
api\node_modules\express\lib\router\layer.js:95:5)
at C:\Users\shankar\first-app\book api\node_modules\express\lib\router\index.js:281:22
at Function.process_params (C:\Users\shankar\first-app\book api\node_modules\express\lib\router\index.js:335:12)
at next (C:\Users\shankar\first-app\book api\node_modules\express\lib\router\index.js:275:10)
(node:2832) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async
function without a catch block, or by rejecting a promise which was
not handled with .catch(). (rejection id: 1)
Put this route code and make sure you are using object type in schema.
app.post('/history',(req,res)=>{
res.sendStatus(201);
const newHistory = new History({
searched: req.body
});
newHistory.save().then(savedPost=>{
console.log("data saved");
}).catch((error)=>{
console.log(error);
})
});
I'm using the angular-breadcrumb module to build a navigation panel.
I have a problem displaying the parent state's dynamic label: when activated the child's label is displayed and the parent's one is empty. Both states use different controllers.
What's wrong with my setup?
var parentState = {
name: 'parentState',
parent: 'parentParentState',
url: '/:department_id/object/:object_id?department_name&object_name',
ncyBreadcrumb: {
label: '<% resNavCtrl.stateLabel %>'
},
views: {
'main': {
controller: 'resourcesNavController',
controllerAs: 'resNavCtrl',
templateUrl: templateUrl
}
},
params: {
department_name: null,
object_name: null
},
};
var childState = {
name: 'childState',
parent: 'parentState',
url: '/:resourceType?resourceTypeName',
ncyBreadcrumb: {
label: '<% resCtrl.stateLabel %>'
},
views: {
'main#': {
controller: 'resourcesController',
controllerAs: 'resCtrl',
templateUrl: templateUrl
}
},
params: {
resourceType: '',
resourceTypeName: ''
}
};
You can use this breadcrumb instead
Use this directive as <div md-breadcrumb></div> or <md-breadcrumb></md-breadcrumb>
Note: This works only with Angular UI Router
md-breadcrumb.tmpl.html
<article>
<nav class="breadcrumb-block" role="navigation">
<span class="icon-breadcrumb-mobile"></span>
<div class="nav-wrapper breadcrumb-content">
<a ng-repeat="breadcrumb in breadcrumbs track by $index" ng-if="!$first" href="" class="breadcrumb" title="{{breadcrumb.url}}" ng-click="onClickingLink($event, breadcrumb)">{{breadcrumb.url | firstLetterCaps}}</a>
</div>
<div ui-view class="breadcrumb-child"></div>
</nav>
</article>
md-breadcrumb.ctrl.js
function breadcrumbController($scope, $state) {
$scope.breadcrumbs = [];
stateChanged(); // call to get initial breadcrumb
// change breadcrumb on each state change success
$scope.$on('$stateChangeSuccess', stateChanged);
// executes on $stateChangeSuccess
function stateChanged(){
$scope.breadcrumbs = getParentList($state.$current); // holds all active states
$scope.onClickingLink = onClickingLink; // holds link clicking function
$scope.breadcrumbs.reverse(); // reverse breadcrumbs child to root states
}
// executes on link click
function onClickingLink(event, breadcrumb) {
event.preventDefault(); // prevent default action
$state.go(breadcrumb.stateName); // move to state
}
// below function used to get parent states
function getParentList(state) {
var parentList = []; // holds parent states list
while(state) { // loop until root state occurs
// push into parentList array
parentList.push({'state': state, 'url': state.self.url.slice(1, state.self.url.length), 'stateName': state.toString()});
state = state.parent; // make parent as current state for loop
}
return parentList; // return parentList
}
}
md-breadcrumb.directive.js
function breadcrumbDirective() {
return {
restrict: 'EA',
templateUrl: 'md-breadcrumb.tmpl.html',
controller: breadcrumbController
}
}
md-breadcrumb.css
.breadcrumb:before {
display: none;
}
.breadcrumb-block {
box-shadow: none;
background: inherit;
height: auto;
line-height: 1.5;
margin-top: 0px;
padding-right: 12px;
}
.breadcrumb-block .breadcrumb {
color: #ffffff;
font-size: 14px;
padding: 8px 18px 8px 28px;
background: #015798;
position: relative;
display: block;
float: left;
}
.breadcrumb-block .breadcrumb:hover, .breadcrumb-block .breadcrumb:focus {
background-color: #0288D1;
outline: none;
}
.breadcrumb-block .breadcrumb:hover:after, .breadcrumb-block .breadcrumb:focus:after {
border-left: 10px solid #0288D1;
}
.breadcrumb-block .breadcrumb:first-child {
padding-left: 18px;
}
.breadcrumb-block .breadcrumb:last-child{
color: #ffffff;
/*font-size: 13px;
font-weight: 500;*/
background-color: #0288D1;
}
.breadcrumb-block .breadcrumb:last-child:after {
border-left: 10px solid #0288D1;
}
.breadcrumb-content:after {
display: block;
content: " ";
clear: both;
}
.breadcrumb-block .breadcrumb-icon:after,
.breadcrumb-block .breadcrumb:after {
content: " ";
display: block;
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-left: 10px solid #015798;
position: absolute;
top: 50%;
margin-top: -20px;
left: 100%;
z-index: 1;
}
.breadcrumb-block .breadcrumb-icon:before,
.breadcrumb-block .breadcrumb:before {
content: " ";
display: block;
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-left: 10px solid #DDDAD5;
position: absolute;
top: 50%;
margin-top: -20px;
margin-left: 1px;
left: 100%;
z-index: 1;
}
.breadcrumb-block .breadcrumb-icon:after {
border-left: 10px solid #FFF;
}
/*.breadcrumb-block .breadcrumb:last-child:after {
content: " ";
display: block;
border-left-color: #fff;
}*/
.breadcrumb-block .breadcrumb:last-child:before {
content: " ";
display: block;
}
.breadcrumb-block .breadcrumb-icon {
font-size: 16px;
color: #B8B8B8;
display: none;
padding: 10px 12px 12px 12px;
background: #fff;
width: 40px;
height: 40px;
position: relative;
box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12);
}
.breadcrumb-content {
box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12);
display: inline-block;
}
.breadcrumb-block a:focus {
border-bottom: 0px none;
}
#media only screen and (max-width: 799px) {
.breadcrumb-block .breadcrumb-icon {
display: block;
margin-bottom: 5px;
}
.breadcrumb-block .breadcrumb-content .breadcrumb {
display: block;
width: 100%;
margin-bottom: 5px;
}
.breadcrumb-block .breadcrumb:before,
.breadcrumb-block .breadcrumb:after {
/*content: "";*/
/*display: none;*/
}
.breadcrumb-block .breadcrumb {
padding-left: 18px;
box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12);
}
.breadcrumb-content {
display: block;
box-shadow: 0 0px 0px 0 rgba(0,0,0,0.0);
width: 100%;
}
}
.subcrumb {
color: #015798;
cursor: pointer;
}
.breadcrumb-child {
padding: 20px;
font-size: 15px;
}
This is my cshtml file and I'm using typeahead in script to load and autocomplete my text-box while entering the customer name.
I can see that while typing the name in customer textbox there is a call to my API but nothing is getting displayed as a dropdown.
<form>
<div class="form-group">
<label>Customer</label>
<input id="customer" type="text" value="" class="form-control" />
</div>
<div class="form-group">
<label>Movies</label>
<input type="text" value="" class="form-control" />
</div>
<button class="btn btn-primary">Submit</button>
</form>
#section scripts
{
<script>
$(document).ready(function () {
var customers = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/api/customers?query=%QUERY',
wildcard: '%QUERY'
}
});
customers.initialize();
$('#customer').typeahead({
minLength: 3,
highlight: true
},
{
name: 'customers',
display: 'name',
source: customers
});
});
</script>
}
Here is my typeahead.css file:
.typeahead {
background-color: #fff;
}
.typeahead:focus {
border: 2px solid #0097cf;
}
.tt-query {
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
.tt-hint {
color: #999
}
.tt-menu {
width: 422px;
margin: 12px 0;
padding: 8px 0;
background-color: #fff;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
box-shadow: 0 5px 10px rgba(0,0,0,.2);
}
.tt-suggestion {
padding: 3px 20px;
font-size: 18px;
line-height: 24px;
}
.tt-suggestion:hover {
cursor: pointer;
color: #fff;
background-color: #0097cf;
}
.tt-suggestion.tt-cursor {
color: #fff;
background-color: #0097cf;
}
.tt-suggestion p {
margin: 0;
}
.tt-container {
position: relative;
}
and this is the typeahead js I'm using:
https://twitter.github.io/typeahead.js/releases/latest/typeahead.bundle.js
My API return values something like this:
[
{
"id": 1016,
"name": "John",
"isSubscribeToNewsLetter": true,
"membershipType": {
"id": 4,
"signUpFee": 300,
"durationInMonths": 12,
"discountRate": 20,
"subscriptionType": "Yearly"
},
"membershipTypeId": 4,
"birthDate": "1990-10-18T00:00:00"
}
]
Try replacing you typeahead function to below:
$('#customer').typeahead({
minLength: 3,
highlight: true
},
{
name: 'customers',
displayKey: 'name',
source: customers.ttAdapter()
});
});
I have changed "display" to "displayKey" and customers to customers.ttAdapter().
Hope it helps :)
The above code I posted is correct, but there was an issue in my API which was the root cause of this whole problem:
Previous API:
public IEnumerable<Customer> GetCustomers()
{
return _context.Customers.Include(c => c.MembershipType).ToList();
}
Current API(working fine):
public IEnumerable<Customer> GetCustomers(string query = null)
{
var customersQuery = _context.Customers
.Include(c => c.MembershipType);
if (!String.IsNullOrWhiteSpace(query))
customersQuery = customersQuery.Where(c => c.Name.Contains(query));
return customersQuery;
}
I'm creating a Local Weather app and I created an dropdown menu in which you click on a location and the information such as City, Country, and temperature appears. However, I was unable to make the information appear every time I click on a location.
Here's my code.
As you can see, I was able to load the current location and temperature. What I did was to list global variables and call them in function getLocation() and run the info in function getWeather(). For the dropdown Menu, I made an array called mainCities and append the cities to dropdown menu in function testMenu(). In this function, I added onclick=testWeather('place'). For this one, I created another function called function testWeather(cityLocation) in which I again listed info for global variables and tried to run it again in function getWeather() but it's not working. What did I miss?
You can see my code in action at: http://codepen.io/kikibres/pen/EZMJZw
$(document).ready(function() {
var currentLat;
var currentLong;
var currentCity;
var currentRegion;
var currentCountry;
var mainCities = {
'San_Francisco': {
'region': 'California',
'country': "United States",
'lat': 37.7749300,
'lon': -122.4194200
},
'St._Louis': {
'region': 'Missouri',
'country': "United States",
'lat': 38.6272700,
'lon': -90.1978900
},
'Miami': {
'region': 'Florida',
'country': "United States",
'lat': 25.7742700,
'lon': -80.1936600
},
'Tokyo': {
'region': 'Tokyo',
'country': "Japan",
'lat': 35.689500,
'lon': 139.6917100
}
};
function testMenu() {
for (var place in mainCities) {
var city = place.replace(/_/g, ' ');
$('#testMenu').append("<li onclick=testWeather('" + place + "');><a href='#'>" + city + "</a></li>");
}
};
function testWeather(cityLocation) {
currentLat = testLocation[cityLocation].lat;
currentLong = testLocation[cityLocation].lon;
currentRegion = testLocation[cityLocation].region;
currentCity = testLocation[cityLocation];
currentCountry = testLocation[cityLocation].country;
getWeather();
};
function getLocation() {
$.getJSON('http://ip-api.com/json/?callback=?', function(data) {
currentRegion = data.regionName;
currentCity = data.city;
currentCountry = data.country;
currentLat = data.lat;
currentLong = data.lon;
//$("#cityname").text(currentCity);
getWeather();
});
};
function getWeather() {
$("#cityname").text(currentCity);
$("#state").text(currentRegion);
$("#country").text(currentCountry);
$.getJSON('http://api.openweathermap.org/data/2.5/weather?lat=' + currentLat + '&lon=' + currentLong + '&units=imperial&APPID=e656b9ee098cf2341fcfdb365b96b4a8', function(json) {
var showfahrenheit = true;
var tempfahrenheit = Math.round(json.main.temp);
var temcelcius = Math.round((tempfahrenheit - 32) * 5/9);
$("#temp").html(tempfahrenheit);
$('#unit-switch').on('click', function() {
if (showfahrenheit === false) {
$("#temp").html(tempfahrenheit);
showfahrenheit = true;
} else {
$("#temp").html(temcelcius);
showfahrenheit = false;
}
$("#unit-toggle").toggleClass("toggle");
//$('#temp').toggleClass('toggle');
});
});
};
$(".cityarea").html(getLocation);
testMenu();
});
#import url('https://fonts.googleapis.com/css?family=Roboto:300,400');
body {
position: relative;
}
html,body{
height:100%;
}
.wrapper {
width: 100%;
height: 100%;
position: relative;
}
.container {
position: relative;
display: block;
margin: 0 auto;
width: 60%;
}
.header h1 {
text-align: center;
font-family: 'Roboto', sans-serif;
font-weight: normal;
margin: 0 0 10px 0;
}
.weatherbox {
text-align: center;
}
.cityarea h2 {
color: #000000;
font-family: 'Roboto', sans-serif;
font-weight: normal;
font-size: 2em;
margin: 0;
}
.countryarea {
position: relative;
display: -webkit-flex;
display: flex;
-webkit-justify-content: center;
justify-content: center;
margin: 0 auto;
}
.countryarea h3 {
margin: 0 0 10px 0;
font-family: 'Roboto', sans-serif;
font-weight: normal;
}
.countryarea h3:first-child {
margin-right: 8px;
}
.dropdown {
position: relative;
display: inline-block;
font-size: 16px;
color: #FFF;
}
.dropdown-header {
display: block;
padding: 3px 20px;
font-size: 12px;
line-height: 1.42857143;
color: #777;
white-space: nowrap;
text-transform: uppercase;
}
input[type=checkbox]{
display: none;
}
label{
box-sizing: border-box;
display: inline-block;
width: 100%;
background-color: #57A0D4;
padding: 10px 20px;
cursor: pointer;
text-align: center;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
border-radius: 5px;
font-size: 20px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
label .fa-globe {
margin-right: 10px;
}
/* The ul will have display:none by default */
ul{
position: absolute;
list-style: none;
text-align: left;
width: 100%;
min-width: 160px;
z-index: 1;
padding: 5px 0;
margin: 2px 0 0;
font-size: 14px;
text-align: left;
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.2);
display: none;
background-color: #fff;
border: 1px solid #ccc;
border: 1px solid rgba(0,0,0,.15);
border-radius: 4px;
-webkit-box-shadow: 0 6px 12px rgba(0,0,0,.175);
box-shadow: 0 6px 12px rgba(0,0,0,.175);
}
ul li{
/*padding: 15px;*/
background-color: #fff;
color: #4FB9A7;
margin-bottom: 1px;
cursor: pointer;
}
ul li a {
padding: 8px 20px;
color: inherit;
text-decoration: none;
display: block;
}
ul li a:hover{
background-color: #4FB9A7;
color: #FFF;
}
input[type=checkbox]:checked ~ label {
background-color: #3D88BD;
}
input[type=checkbox]:checked ~ ul {
display: block;
}
ul .divider {
height: 1px;
margin: 9px 0;
overflow: hidden;
background-color: #e5e5e5;
}
.temperaturearea span#temp {
position: relative;
color: #000000;
font-size: 80px;
}
.temperaturearea #temp:after {
content: '';
position: absolute;
height: 10px;
width: 10px;
top: 16px;
right: -17px;
border: 3px solid #000000;
border-radius: 50%;
}
.weather > span {
position: relative;
font-size: 1.2rem;
}
.weather > span:before {
content: '';
position: absolute;
left: -10px;
top: 0px;
height: 3px;
width: 3px;
border: 2px solid #000;
border-radius: 50%;
}
.main-toggle span {
margin: 0 0 0 16px;
}
.main-toggle span:last-child {
margin-left: 11px;
}
.weather button {
background: #6bbf6b;
border: none;
border-radius: 30px;
outline: none;
width: 45px;
height: 20px;
margin: 5px 5px 0;
cursor: pointer;
position: relative;
transition: background .2s;
}
.weather button:active {
background: #67b567;
}
.weather #unit-toggle {
position: absolute;
display: inline-block;
left: -8px;
top: 2px;
width: 15px;
height: 15px;
background: #fff;
border-radius: 50%;
transition: left .2s;
}
#unit-toggle.toggle {
left: 16px;
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="container">
<div class="header"><h1>Local Weather</h1></div>
<div class="weatherbox">
<div class="cityarea">
<h2 id="cityname"></h2>
</div>
<div class="countryarea">
<h3 id="state"></h3>
<h3 id="country"></h3>
</div>
<div class="dropdownlocation">
<div class="dropdown">
<input type="checkbox" id="checkbox-toggle">
<label for="checkbox-toggle"><i class="fa fa-globe" aria-hidden="true"></i><i class="fa fa-caret-down" aria-hidden="true"></i></label>
<ul id="testMenu">
<li>Current Location</li>
<li class="divider"></li>
<li class="dropdown-header">Main Cities</li>
</ul>
</div>
</div>
<div class="temperaturearea">
<div>
<span id="wicon"></span>
</div>
<div id="wdescription"></div>
<span id="temp"></span>
<div class="weather main-toggle"> <!-- Begin Unit Switch -->
<span>F</span>
<button id="unit-switch"><span id="unit-toggle"></span></button>
<span>C</span>
</div>
</div>
</div>
</div>
</div>
There are couple of issues with the code, since you have defined all your functions and variable inside document.ready you can't access any of those functions and variable outside document.ready. That's why when you call 'testWeather' on onclick li, it throws an 'testWeather' is not defined. To solve this(you should make your variables and function global) you should define all you variable outside the document.ready. Just keep only below two lines of code inside document.ready.
$(".cityarea").html(getLocation);
testMenu();
But this will just solve 'testWeather' is not defined issue, and there is onre more issue in your code, inside testWeatherfunction there, you are using one property called testLocation, which is not defined, so you will get a JavaScript error here. You have saved your test locations in mainCities variable so you should use mainCities instead of testLocation. These two changes will make your app works without any error. Here is corrected JavaScript code. There is one more minor error your code, the way you are reading current city is not right.
$(document).ready(function () {
$(".cityarea").html(getLocation);
testMenu();
});
var currentLat;
var currentLong;
var currentCity;
var currentRegion;
var currentCountry;
var mainCities = {
'San_Francisco': {
'region': 'California',
'country': "United States",
'lat': 37.7749300,
'lon': -122.4194200
},
'St._Louis': {
'region': 'Missouri',
'country': "United States",
'lat': 38.6272700,
'lon': -90.1978900
},
'Miami': {
'region': 'Florida',
'country': "United States",
'lat': 25.7742700,
'lon': -80.1936600
},
'Tokyo': {
'region': 'Tokyo',
'country': "Japan",
'lat': 35.689500,
'lon': 139.6917100
}
};
function testMenu() {
for (var place in mainCities) {
var city = place.replace(/_/g, ' ');
$('#testMenu').append("<li onclick=testWeather('" + place + "');><a href='#'>" + city + "</a></li>");
}
};
function testWeather(cityLocation) {
currentLat = mainCities[cityLocation].lat;
currentLong = mainCities[cityLocation].lon;
currentRegion = mainCities[cityLocation].region;
currentCity = mainCities[cityLocation];
currentCountry = mainCities[cityLocation].country;
getWeather();
};
function getLocation() {
$.getJSON('http://ip-api.com/json/?callback=?', function (data) {
currentRegion = data.regionName;
currentCity = data.city;
currentCountry = data.country;
currentLat = data.lat;
currentLong = data.lon;
//$("#cityname").text(currentCity);
getWeather();
});
};
function getWeather() {
$("#cityname").text(currentCity);
$("#state").text(currentRegion);
$("#country").text(currentCountry);
$.getJSON('http://api.openweathermap.org/data/2.5/weather?lat=' + currentLat + '&lon=' + currentLong + '&units=imperial&APPID=e656b9ee098cf2341fcfdb365b96b4a8', function (json) {
var showfahrenheit = true;
var tempfahrenheit = Math.round(json.main.temp);
var temcelcius = Math.round((tempfahrenheit - 32) * 5 / 9);
$("#temp").html(tempfahrenheit);
$('#unit-switch').on('click', function () {
if (showfahrenheit === false) {
$("#temp").html(tempfahrenheit);
showfahrenheit = true;
} else {
$("#temp").html(temcelcius);
showfahrenheit = false;
}
$("#unit-toggle").toggleClass("toggle");
//$('#temp').toggleClass('toggle');
});
});
};