How do I update blog post in Mongoose? - javascript

I am building a blog site using CRUD operations. I can create, read and delete but I am having hard time with updating a created post.
Basically I have it where I can click on the "edit" button on an individual entry in home.ejs and into the edit.ejs route where that input fields are populated with current title and content. But what I can not figure out is clicking "Publish" in edit.ejs to update its own post.ejs title and content. When it is done, I want to redirect it back to the main page. Down on the "app.post("/edit/:id")" route, am I supposed to use "PUT" request to update the post?
I am sorry if this is tough to follow. I am not very good at explaining things.
Here's the relevant code:
app.js
const express = require("express");
const bodyParser = require("body-parser");
const ejs = require("ejs");
const mongoose = require("mongoose");
const _ = require("lodash");
const aboutContent = "Hac habitasse platea dictumst vestibulum rhoncus est pellentesque. Dictumst vestibulum rhoncus est pellentesque elit ullamcorper. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit. Egestas sed sed risus pretium quam vulputate dignissim suspendisse. Mauris in aliquam sem fringilla. Semper risus in hendrerit gravida rutrum quisque non tellus orci. Amet massa vitae tortor condimentum lacinia quis vel eros. Enim ut tellus elementum sagittis vitae. Mauris ultrices eros in cursus turpis massa tincidunt dui.";
const contactContent = "Scelerisque eleifend donec pretium vulputate sapien. Rhoncus urna neque viverra justo nec ultrices. Arcu dui vivamus arcu felis bibendum. Consectetur adipiscing elit duis tristique. Risus viverra adipiscing at in tellus integer feugiat. Sapien nec sagittis aliquam malesuada bibendum arcu vitae. Consequat interdum varius sit amet mattis. Iaculis nunc sed augue lacus. Interdum posuere lorem ipsum dolor sit amet consectetur adipiscing elit. Pulvinar elementum integer enim neque. Ultrices gravida dictum fusce ut placerat orci nulla. Mauris in aliquam sem fringilla ut morbi tincidunt. Tortor posuere ac ut consequat semper viverra nam libero.";
let app = express();
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(express.static("public"));
mongoose.connect("mongodb://localhost:27017/blogDB", {
useNewUrlParser: true
});
const postSchema = {
date: String,
title: String,
content: String
}
const Post = mongoose.model("Post", postSchema);
app.get("/", (req, res) => {
Post.find({}, (err, posts) => {
res.render("home", {
posts: posts
});
});
});
app.get("/about", (req, res) => {
res.render("about", {
aboutContent: aboutContent
});
});
app.get("/contact", (req, res) => {
res.render("contact", {
contactContent: contactContent
});
});
app.get("/compose", (req, res) => {
res.render("compose");
});
app.post("/compose", (req, res) => {
const postTitle = req.body.postTitle;
const postBody = req.body.postBody;
let date = new Date();
let postDate = date.toLocaleString('en-US');
const post = new Post({
date: postDate,
title: postTitle,
content: postBody
});
post.save(err => {
if (!err) {
res.redirect("/");
}
});
});
app.get("/edit/:id", (req, res) => {
const requestedId = req.params.id;
console.log(req.body);
Post.findOne({
_id: requestedId
}, (err, post) => {
if (!err) {
res.render("edit", {
title: post.title,
content: post.content
});
}
});
});
app.post("/edit/:id", (req, res) => {
const requestedId = req.params.id;
console.log(req.body);
Post.findOne({
_id: requestedId
}, (err, post) => {
if (!err) {
res.render("edit", {
title: post.title,
content: post.content
});
}
});
});
app.get("/posts/:id", (req, res) => {
const requestedId = req.params.id;
Post.findOne({
_id: requestedId
}, (err, post) => {
if (!err) {
res.render("post", {
title: post.title,
content: post.content
});
}
});
});
app.post("/delete", (req, res) => {
const deletePost = req.body.delete;
Post.findByIdAndDelete(deletePost, (err) => {
if (!err) {
res.redirect("/");
}
});
});
app.listen(3000, function () {
console.log("Server started on port 3000");
});
home.ejs
<%- include("partials/header") -%>
<h1>Home</h1>
<button type="button" class="new-entry btn btn-dark">New Entry</button>
<div class="entries-container">
<% posts.forEach(post => { %>
<div class="blog-entry">
<p class="post-date">Posted on
<%= post.date %>
</p>
<h2>
<%= post.title %>
</h2>
<div class="entry-footer">
<button type="button" class="btn btn-outline-primary">VIEW</button>
<form action="/edit" method="POST">
EDIT</button>
</form>
<form action="/delete" method="POST">
<button type="submit" name="delete" value="<%= post._id %>" class="btn btn-outline-danger">DELETE</button>
</form>
</div>
</div>
<% }) %>
</div>
<%- include("partials/footer") -%>
edit.ejs
<%- include("partials/header") -%>
<h1>Compose</h1>
<form action="/edit" method="PUT">
<div class="form-group">
<label for="postTitle">Title</label>
<input type="text" name="postTitle" class="form-control" id="postTitle" autocomplete="off" value="<%= title %>">
<label for="postBody">Post</label>
<textarea name="postBody" class="form-control" autocomplete="off" rows="8"><%= content %></textarea>
</div>
<button type="submit" name="button" class="btn btn-primary">Publish</button>
</form>
<%- include("partials/footer") -%>
post.ejs
<%- include("partials/header") -%>
<h2 class="post-title"><%= title %></h2>
<p class="post-content"><%= content %></p>
<%- include("partials/footer") -%>

If you want to simply update your existing blog post title and content with sending POST request then you can do it with findOneAndUpdate() as below:
app.post("/edit/:id", (req, res) => {
const requestedId = req.params.id;
console.log(req.body);
Post.findOneAndUpdate({
_id: requestedId // Query Part
},
{
$set: {
title: req.body.title, // Fields which we need to update
content: req.body.content
}
},
{
new: true // option part ( new: true will provide you updated data in response )
},(err, post) => {
if (!err) {
res.render("edit", {
title: post.title,
content: post.content
});
}
});
});

Related

TINYMCE Editor External Templates: Uncaught TypeError: Cannot read property 'indexOf' of undefined

I am using TINYMCE Editor Version 4.8.2 (2018-08-09) and trying to add few prebuilt templates in .html in the list. I installed the template plugin in the plugin directory and initiated it in init-tinymce.js as below.
tinymce.init({
/* replace textarea having class .tinymce with tinymce editor */
selector: "textarea",
/* plugin */
plugins: [
"template"
],
/* toolbar */
toolbar: "template",
/* templates */
templates: [
{
title : "Default Template 1",
src : "tinymce-templates/template1.html",
description : "Default Template"
},
{
title : "Default Template 2",
src : "tinymce-templates/template2.html",
description : "Default Template"
}
]
});
Now when I try to open and insert my added templates then I am able to see my both template in the dropdown list but when I cant see the preview. On inspecting, I found this error shared below.
Uncaught TypeError: Cannot read property 'indexOf' of undefined plugin.min.js:1
at S (plugin.min.js:1)
at i.onselect (plugin.min.js:1)
at Vg.c.fire (tinymce.min.js:2)
at i.fire (theme.min.js:1)
at w (plugin.min.js:1)
at plugin.min.js:1
at i.<anonymous> (plugin.min.js:1)
at Vg.c.fire (tinymce.min.js:2)
at i.fire (theme.min.js:1)
at HTMLDivElement.o (theme.min.js:1)
S # plugin.min.js:1
onselect # plugin.min.js:1
Vg.c.fire # tinymce.min.js:2
fire # theme.min.js:1
w # plugin.min.js:1
(anonymous) # plugin.min.js:1
(anonymous) # plugin.min.js:1
Vg.c.fire # tinymce.min.js:2
fire # theme.min.js:1
o # theme.min.js:1
C # tinymce.min.js:2
d
And my template is like something below...
<div class="mceTmpl">
<div class="row">
<div class="box">
<div class="col-lg-12">
<hr>
<h2 class="intro-text text-center">Build a website
<strong>worth visiting</strong>
</h2>
<hr>
<hr class="visible-xs">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc placerat diam quis nisl vestibulum dignissim. In hac habitasse platea dictumst. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
</div>
</div>
</div>
</div>
I believe where you have a src attribute the correct attribute is url. Per the documentation I see this:
This option lets you specify a predefined list of templates to be
inserted by the user into the editable area. It is structured as an
array with each item having a title, description and content/url.

Accordion animation is really slow in iOS Framework7-vue app

I am building both android and iOS app with Framework7-vue.
The Android app works perfectly, but the iOS app gives me troubles.
The user will be given the option to view a popup, this popup will have an accordion inside.
The animation in iOS is terribly slow and it looks awful.
I've been trying some stuff and googled a bit but no success.
Bellow you will find my implementation of the app.js and a snippet containing the popup.
I will be happy to hear suggestions, thank you.
app.js
const questions = {
questionOne:'Does the subject have a high CCA?',
options: {
yes:'Yes',
no:'No'
},
selected:{
yes: {
questionTwo: 'High PTH and yes to high CCA',
options: [
'PTH > 80pmol/L',
'PTH < 80pmol but > 50pmol/L and increasing',
'PTH < 50 but higher than base line'
]
},
no: {
questionTwo: 'PTH > 80pmol/L',
options: [
'Refer for surgical parathyroidectomy',
'Start Cinacalcet if parathyroidectomy is'
]
}
}
}
// this.$$ - this.Dom7;
Vue.use(Framework7Vue);
// Init Page Components
Vue.component('page-education', {
template: '#page-education'
});
Vue.component('page-mbddef', {
template: '#page-mbddef'
});
Vue.component('page-ckdmbdeffects', {
template: '#page-ckdmbdeffects'
});
Vue.component('page-algorithms', {
template: '#page-algorithms'
});
Vue.component('page-highpth', {
template: '#page-highpth'
});
Vue.component('page-dietary', {
template: '#page-dietary'
});
// Handle device ready event
// Note: You may want to check out the vue-cordova package on npm for cordova specific handling with vue - https://www.npmjs.com/package/vue-cordova
document.addEventListener('deviceready', () => {
console.log("DEVICE IS READY!");
}, false)
// Init App
// var $$ = dom7;
// console.log($$);
// var $$ = this.Dom7;
// console.log($$);
const mainApp = new Vue({
el: '#app',
data: {
popupOpened: false,
navBarShowing: true,
showingGreenCard: false,
currQuestion: questions.questionOne,
currOptCount: 2,
currOptions: questions.options,
showing: false,
isShowing: true,
icon: "<i class='far fa-file-alt'></i>",
homeIcon: "<i id='sidemenu-icon' class='fas fa-home'></i>",
pencilIcon: "<i id='sidemenu-icon' class='fas fa-pencil-alt'></i>",
healthIcon: "<i id='sidemenu-icon' class='fas fa-plus'></i>",
algIcon: "<i id='sidemenu-icon' class='fas fa-align-justify'></i>",
dietIcon: "<i id='sidemenu-icon' class='fas fa-utensils'></i>",
clipIcon: "<i id='sidemenu-icon' class='far fa-clipboard'></i>",
linkIcon: "<i id='sidemenu-icon' class='fas fa-link'></i>"
},
progress: {
value: 10
},
on: {
pageInit: function(){
console.log('page');
}
},
methods: {
setInlineProgress: function(value) {
this.value = value;
const self = this;
const app = self.$f7;
app.setProgressbar('#demo-inline-progressbar', this.value);
},
toHomeScreen: function() {
this.$f7.getCurrentView().router.back({ pageName: 'home-page', force: true, refreshPrevious: true});
this.$f7.closePanel();
},
shouldShow: function() {
this.showing = true;
},
generateQuestion: function(answer){
this.currQuestion = questions.selected.yes.questionTwo;
this.optionsOne = 'Yes';
this.optionTwo = 'No';
this.shouldShow();
},
showPopUp: function(e) {
this.showingGreenCard = true;
},
closePopUp: function(){
this.showingGreenCard = false;
},
},
on:{
},
// Init Framework7 by passing parameters here
framework7: {
root: '#app',
/* Uncomment to enable Material theme: */
material: true,
routes: [
{
path:'/',
name: 'home'
}
,
{
path: '/education/',
component: 'page-education'
},
{
path: '/ckdmbddef/',
component: 'page-mbddef'
},
{
path: '/ckdmbdeffects/',
component: 'page-ckdmbdeffects'
},
{
path: '/algorithms/',
component: 'page-algorithms',
},
{
path: '/highpth/',
component: 'page-highpth'
},
{
path: '/dietary/',
component: 'page-dietary'
},
{
path: '/form/',
component: 'page-form'
},
{
path: '/dynamic-route/blog/:blogId/post/:postId/',
component: 'page-dynamic-routing'
}
],
}
});
Here is the popup snippet:
<div class="popup-card-four" v-if="mainApp.showingGreenCard">
<p class="popup-text">Suitable low phosphate foods</p>
<i id="close-icon" #click="mainApp.closePopUp()" class="far fa-times-circle"></i>
<f7-list class="main-acc-list" accordion-list>
<f7-list-item id="acc-one-title" accordion-item title="Lower phosphate diary">
<f7-accordion-content class="table-wrapper">
<f7-block >
<table class="main-table">
<tr class="table-underline">
<td class="popup-inner">Milk</td>
<td class="popup-inner">Rice or milk</td>
</tr>
<tr>
<td class="popup-inner">Cheese</td>
<td class="popup-inner">Cottage Cheese</td>
</tr>
<tr>
<td></td>
<td class="popup-inner">Cream cheese</td>
</tr>
<tr>
<td></td>
<td class="popup-inner">Quark</td>
</tr>
<tr class="table-underline">
<td></td>
<td class="popup-inner">Ricotta</td>
</tr>
<tr>
<td class="popup-inner">Cream</td>
<td class="popup-inner">Single, double, whipping,</td>
</tr>
<tr>
<td></td>
<td class="popup-inner">pouring, clotted,</td>
</tr>
<tr>
<td></td>
<td class="popup-inner">Marscapone</td>
</tr>
</table>
</f7-block>
</f7-accordion-content>
</f7-list-item>
<f7-list-item id="acc-one-title" accordion-item title="Lower phosphate diary">
<f7-accordion-content>
<f7-list>
<f7-list-item title="Item 1"></f7-list-item>
<f7-list-item title="Item 2"></f7-list-item>
<f7-list-item title="Item 3"></f7-list-item>
<f7-list-item title="Item 4"></f7-list-item>
</f7-list>
</f7-accordion-content>
</f7-list-item>
<f7-list-item id="acc-one-title" accordion-item title="Lower phosphate diary">
<f7-accordion-content>
<f7-block>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean elementum id neque nec commodo. Sed vel justo at turpis laoreet pellentesque quis sed lorem. Integer semper arcu nibh, non mollis arcu tempor vel. Sed pharetra tortor vitae est rhoncus, vel congue dui sollicitudin. Donec eu arcu dignissim felis viverra blandit suscipit eget ipsum.
</p>
</f7-block>
</f7-accordion-content>
</f7-list-item>
</f7-list>
</div>
Resolved the issue by removing the framework7-vue component for accordion and replacing it with the standart framework7 html accordion component.
It seems that iOS does not like framework7-vue components.
Hope this will be helpful to someone.
Cheers.

Angularjs - UI Boostrap: Accordion Open and close all

i have written some code to open and close all tabs of an accordion respectively use a separate 'open' and 'close' button. How ever it requires me to dynamically add a key value pair(a Boolean value) to my json data.
What is the best practice in this situation? should i add the Boolean value as a static json element or is it OK to dynamically add values when their sole purpose is for visual structure and not relevant to actual object data.
HTML/Angular directives
<div id="app" ng-app="demoApp">
<div id="controller" ng-controller="demoAppCtrl">
<uib-accordion close-others="false">
<div class="btn-group form-group">
<button type="button" class="btn btn-warning" ng-click="toggle(true)">Open</button>
<button type="button" class="btn btn-warning" ng-click="toggle(false)">Close</button>
</div>
<uib-accordion-group is-open="hero.state" ng-click="setOpened(false)" ng-repeat="hero in heroes">
<uib-accordion-heading>
{{hero.name}}
</uib-accordion-heading>
{{hero.bio}}
</uib-accordion-group>
</uib-accordion>
</div>
</div>
Javascript/Angular
var app = angular.module('demoApp', ['ngAnimate','ui.bootstrap']);
app.controller('demoAppCtrl', function($scope) {
// This json object contain only one entry as an example
$scope.heroes = [
{'name': 'Captain America', 'team': 'Avengers', 'bio': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum vitae metus placerat, condimentum nisl et, accumsan sapien. Quisque molestie magna nulla, id malesuada sem interdum a.'}
];
$scope.addDefaultState = function(val) {
for (var i=0;i<$scope.heroes.length;i++) {
$scope.heroes[i].state = val;
}
}
$scope.addDefaultState(false);
$scope.toggle = function(status) {
$scope.heroes.forEach(function(e) {
e.state = status;
});
}
});
codepen.io - Working example (with corrections)
In my opinion the static json should not contain Boolean state value. It is okay to dynamically add values for visual presentation.
In your code the function addDefaultState is not needed. The is-open="hero.state" will take care of default state cause initially it will not find state and will consider it as false. So you change your code like below it should work too:
var app = angular.module('demoApp', ['ngAnimate','ui.bootstrap']);
app.controller('demoAppCtrl', function($scope) {
// This json object contain only one entry as an example
$scope.heroes = [
{'name': 'Captain America', 'team': 'Avengers', 'bio': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum vitae metus placerat, condimentum nisl et, accumsan sapien.'}
];
$scope.toggle = function(status) {
$scope.heroes.forEach(function(e) {
e.state = status;
});
}
});

Ionic app - controller $http

I am building a app in ionic, i am trying to get external restful api data into the view via a controller, but there seems to be something wrong with my controller because nothing is being pulled in?
my code is:
angular.module('starter', ['ionic'])
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
if(window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
}
if(window.StatusBar) {
StatusBar.styleDefault();
}
});
})
.controller('myCtrl', function($scope, $http) {
$http.get('http://jsonplaceholder.typicode.com/posts').then(function(results){
$scope.posts = results.data.posts;
}, function(err) {
console.error('ERR', err);
// err.status will contain the status code
})
});
<ion-content class="has-subheader" ng-controller="myCtrl">
<ion-list>
<ion-item ng-repeat='item in posts' class="item-thumbnail-left item-text-wrap">
<img src="http://placehold.it/100x100" alt="photo">
<h2>{{post.title}}</h2>
<h4>Place</h4>
<p style="font-size:12px; line-height:16px;">Quisque quis sem a velit placerat vehicula quis nec felis. Mauris posuere, nisl vitae condimentum luctus, tellus enim blandit orci, quis efficitur nibh libero eget dui. Aliquam fermentum velit quis sem molestie.</p>
</ion-item>
</ion-list>
</ion-content>
Any help would be appreciated.
Solution to this:
$http.get('http://jsonplaceholder.typicode.com/posts')
.success(function(results){
$scope.posts = results.posts;
console.log($scope.posts);
});
You were using data but there isn't data in the results. Look at the console, and you should see objects now.
Working codepen
you have made 2 mistakes in your code:
First: once you define 'item' in ng-repeat you should use it to bind your object key "title":
ng-repeat='item in posts'
'item' is now holding your JSON keys.
<h2>{{item.title}}</h2>
Second: reviewing jsonplaceholder JSON you are calling through this url, http://jsonplaceholder.typicode.com/posts, title key is under results.data
, so you should define posts like this:
$scope.posts = results.data
For more help: I've taken the code you provided and made it working on a plunk, check it on the following url:
http://embed.plnkr.co/Gp1U3y/preview
Please contact me for more help. Thanks

ng-repeat shows only the last item from json

How can I display a specific number of items from JSON using ng-repeat. Right now its displaying only the last item.
JSON
{
"list":{
"item": {
"id":1,
"img": "1.jpg",
"user": "David Belle",
"text": "Cum sociis natoque penatibus et magnis dis parturient montes"
},
"item": {
"id":2,
"img": "2.jpg",
"user": "Jonathan Morris",
"text": "Nunc quis diam diamurabitur at dolor elementum, dictum turpis vel"
},
"item": {
"id":3,
"img": "3.jpg",
"user": "Fredric Mitchell Jr",
"text": "Phasellus a ante et est ornare accumsan at vel magnauis blandit turpis at augue ultricies"
}
}
}
ANGULARJS
Controller
app.controller('mainCtrl', ['$scope', '$resource', function($scope, $resource) {
$scope.msAPI = $resource("data/messages-notifications.json");
$scope.msResult = $scope.msAPI.get();
}]);
HTML
<div ng-controller="mainCtrl">
<a href="" ng-repeat="w in msResult.list">
<div class="lv-title">{{ w.user }}</div>
<small class="lv-small">{{ w.text }}</small>
</a>
</div>
The easiest way would be to change your JSON structure to include an array (instead of an object), as follows:
{
"list": [{
"id":1,
"img": "1.jpg",
"user": "David Belle",
"text": "Cum sociis natoque penatibus et magnis dis parturient montes"
}, {
"id":2,
"img": "2.jpg",
"user": "Jonathan Morris",
"text": "Nunc quis diam diamurabitur at dolor elementum, dictum turpis vel"
}, {
"id":3,
"img": "3.jpg",
"user": "Fredric Mitchell Jr",
"text": "Phasellus a ante et est ornare accumsan at vel magnauis blandit turpis at augue ultricies"
}]
}

Categories

Resources