EJS conditional IF - ejs

newbie to EJS here,
Tried to get data from MongoDB and display in view. If there is no data, display "otherCondition", if there is data, do loop and display "repeat" and "unrelated"
<% if (typeof eventData == 'object'){ %> // check if its empty/undefined
<% eventData.forEach(function(event) { %> // loop if exists
<div class="repeat">
... // other code
</div>
<% }) %> // end of loop
<div class="unrelated">
... // other code (again)
</div>
<% } else { %> // else condition
<div class="otherCondition">
... // other code (again)
</div>
<% } %>
EJS code above, route code below:
router.route('/').get(function(req, res, next) {
mongoose.model('Event').find({}, function(err, events) {
if (err) {
return console.error(err);
} else {
res.format({
html: function() {
res.render('events', {
eventData: events,
user: req.user,
});
if (typeof eventData == 'object')
console.log('display new events: ' + eventData);
else
console.log('no events, sorry :(');
}
});
}
});
})
Probably another silly question, but can anyone help me how to fix this? In console, I get no events, sorry :( but my <div class="otherCondition"></div> doesn't get displayed.
Thanks in advance.

Here eventData is always an Array, Because in server side your using query is find() method,
So typeof eventData will always return an object hence your first condition is always true so the controller never goto otherCondition.
Better you check the lenth of eventData in first if condition
<% if(constructor.eventData === Array && eventData.length > 0){ %>
<% eventData.forEach(function(event) { %> // loop if exists
<div class="repeat">
... // other code
</div>
<% }) %> // end of loop
<div class="unrelated">
... // other code (again)
</div>
<% }else{ %>
<div class="otherCondition">
... // other code (again)
</div>
<% } %>

Related

usersWithSecrets is not defined in forEach called in ejs

Am getting this error when google auth redirects to my secrets page. And also in the secrets page I can't post secrets as they aren't posted in my database. I don't know which elements I have called wrongly.
<%- include('partials/header') %>
<div class="jumbotron text-center">
<div class="container">
<i class="fas fa-key fa-6x"></i>
<h1 class="display-3">You've Discovered My Secret!</h1>
<% usersWithSecrets.forEach(function(foundUsers){ %>
<p class="secret-text"><%=foundUsers.secret%></p>
<% }) %>
<hr>
<a class="btn btn-light btn-lg" href="/logout" role="button">Log Out</a>
<a class="btn btn-dark btn-lg" href="/submit" role="button">Submit a Secret</a>
</div>
</div>
<%- include('partials/footer') %>
app.get("/secrets", function(req,res){
if (req.isAuthenticated()) {
User.find({"secret": {$ne: null}}, function(err, foundUsers){
if(foundUsers){
res.render("secrets", {usersWithSecrets: foundUsers});
}else {
console.log(err);
}
});
app.post("/submit", function(req, res){
const submittedSecret = req.body.secret;
User.findById(req.user.id, function(err, foundUser){
if (err) {
console.log(err);
}else{
if (foundUser) {
foundUser.secret=submittedSecret;
submittedSecret.save(function(){
res.redirect("/secrets");
});
}
}
});
});
The first block of code should catch the user and redirect them to the app.get(secrets)
The second block is a user can post a secret and caught in the database but the page keeps loading infinitely.

how to return computed html in vue2?

I try to return the part of html depending on variable type I want to do like:
<template>
<div>{{getvalue(this.$store.state.output)}}</div>
</template>
<script>
export default {
methods:{
getvalue(output){
if(output.constructor == Array){
re="";
for(i in output){
re+=<p>{{i}}</p>;
}
return re;
}
else if(output.constructor == Object){
re="";
for(i in output){
re+=<p><span>i</span><span>{{output[i]}}</span></p>
}
return re;
}else if(output.constructor == String){
return <p>{{output}}</p>
}
}
},
...
}
</script>
I know in react I can use virtualDOM to do that, how should I do the similar things like above in Vue2.5.11?
#ceejayoz is right. It would be missing the whole point of Vue.
Another mistake is that computed values don't accept parameters. It is supposed to be treated as a value and not a function.
This is a more proper way of doing things in Vue. By using v-if and v-for.
<template>
<div>
<div v-if="getOutputConstructor==Array">
<p v-for="i in this.$store.state.output" :key="i">{{i}}</p>
</div>
<div v-else-if="getOutputConstructor==Object">
...
</div>
<div v-else-if="getOutputConstructor==String">
...
</div>
</div>
</template>
<script>
export default {
computed: {
getOutputConstructor() {
const output = this.$store.state.output;
return output.constructor;
}
}
};
</script>
But.. if you really really want to do it the wrong way, use v-html

Error submitting form data using knockout and mvc

#model NewDemoApp.Models.DemoViewModel
#{
ViewBag.Title = "Home Page";
}
#*<script src="#Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script>*#
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="#Url.Content("~/Scripts/knockout-3.3.0.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script type="text/javascript">
var viewModel;
var compViewModel, userViewModel;
$(document).ready(function () {
$(".wizard-step:visible").hide();
$(".wizard-step:first").show(); // show first step
$("#back-step").hide();
var result = #Html.Raw(Json.Encode(Model));
var viewModel = new DemoViewModel(result.userViewModel);
//viewModel.userViewModel.FirstName = result.userViewModel.FirstName;
//viewModel.userViewModel.LastName = result.userViewModel.LastName;
//viewModel.userViewModel.State = result.userViewModel.State;
//viewModel.userViewModel.City = result.userViewModel.City;
ko.applyBindings(viewModel);
});
var userVM = function(){
FirstName = ko.observable(),
LastName = ko.observable(),
State = ko.observable(),
City = ko.observable()
};
function DemoViewModel(data) {
var self = this;
self.userViewModel = function UserViewModel(data) {
userVM.FirstName = data.FirstName;
userVM.LastName = data.LastName;
userVM.State = data.State;
userVM.City = data.City;
}
self.Next = function () {
var $step = $(".wizard-step:visible"); // get current step
var form = $("#myFrm");
var validator = $("#myFrm").validate(); // obtain validator
var anyError = false;
$step.find("input").each(function () {
if (!validator.element(this)) { // validate every input element inside this step
anyError = true;
}
});
if (anyError)
return false; // exit if any error found
if ($step.next().hasClass("confirm")) { // is it confirmation?
$step.hide().prev(); // hide the current step
$step.next().show(); // show the next step
$("#back-step").show();
$("#next-step").hide();
//$("#myFrm").submit();
// show confirmation asynchronously
//$.post("/wizard/confirm", $("#myFrm").serialize(), function (r) {
// // inject response in confirmation step
// $(".wizard-step.confirm").html(r);
//});
}
else {
if ($step.next().hasClass("wizard-step")) { // is there any next step?
$step.hide().next().fadeIn(); // show it and hide current step
$("#back-step").show(); // recall to show backStep button
$("#next-step").show();
}
}
}
self.Back = function () {
var $step = $(".wizard-step:visible"); // get current step
if ($step.prev().hasClass("wizard-step")) { // is there any previous step?
$step.hide().prev().fadeIn(); // show it and hide current step
// disable backstep button?
if (!$step.prev().prev().hasClass("wizard-step")) {
$("#back-step").hide();
$("#next-step").show();
}
else {
$("#back-step").show();
$("#next-step").show();
}
}
}
self.SubmitForm = function (formElement) {
$.ajax({
url: '#Url.Content("~/Complaint/Save")',
type: "POST",
data: ko.toJS(self),
done: function (result) {
var newDiv = $(document.createElement("div"));
newDiv.html(result);
},
fail: function (err) {
alert(err);
},
always: function (data) {
alert(data);
}
});
}
self.loadData = function () {
$.get({
url: '#Url.Content("~/Complaint/ViewComplaint")',
done: function (data) {
debugger;
alert(data);
self.compViewModel(data);
self.userViewModel(data);
},
fail: function (err) {
debugger;
alert(err);
},
always: function (data) {
debugger;
alert(data);
}
});
}
}
</script>
<form class="form-horizontal" role="form" id="myFrm">
<div class="container">
<div class="row">
<div class="col-md-3">
</div>
<div class="col-md-6">
<div class="wizard-step">
</div>
<div class="wizard-step" >
<h3> Step 2</h3>
#Html.Partial("UserView", Model.userViewModel)
<div class="col-md-3"></div>
<div class="col-md-6">
<input type="submit" id="submitButton" class="btn btn-default btn-success" value="Submit" data-bind="click: SubmitForm" />
</div>
<div class="col-md-3"></div>
</div>
<div class="wizard-step">
<h3> Step 3</h3>
</div>
<div class="wizard-step confirm">
<h3> Final Step 4</h3>
</div>
</div>
<div class="col-md-3"></div>
</div>
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
<input type="button" id="back-step" class="btn btn-default btn-success" value="< Back" data-bind="click: Back" />
<input type="button" id="next-step" class="btn btn-default btn-success" value="Next >" data-bind="click: Next" />
</div>
<div class="col-md-3"></div>
</div>
</div>
</form>
I am able to get the data from controller and bind it using knockout. There is a partial view that loads data from controller. But when submitting the updated data, I do not get the data that was updated, instead getting error that "FirstName" property could not be accessed from null reference. I just need to get pointers where I am going wrong especially the right way to create ViewModels and use them.
When you are submitting the form in self.SubmitForm function you are passing Json object which is converted from Knockout view model.
So make sure you are providing the data-bind attributes in all input tags properly. If you are using Razor syntax then use data_bind in Html attributes of input tags.
Check 2-way binding of KO is working fine. I can't be sure as you have not shared your partial view Razor code.
Refer-
http://knockoutjs.com/documentation/value-binding.html
In Chrome you can see what data you are submitting in Network tab of javascript developer console. The Json data that you are posting and ViewModel data structure you are expecting in controller method should match.
You can also change the parameters to expect FormCollection formCollection and check what data is coming from browser when you are posting.

Handle radio button form in Marionette js

I'm trying to construct a view in my app that will pop up polling questions in a modal dialog region. Maybe something like this for example:
What is your favorite color?
>Red
>Blue
>Green
>Yellow
>Other
Submit Vote
I've read that Marionette js doesn't support forms out of the box and that you are advised to handle on your own.
That structure above, branch and leaves (question and list of options), suggests CompositeView to me. Is that correct?
How do I trigger a model.save() to record the selection? An html form wants an action. I'm unclear on how to connect the form action to model.save().
My rough draft ItemView and CompositeView code is below. Am I in the ballpark? How should it be adjusted?
var PollOptionItemView = Marionette.ItemView.extend({
template: Handlebars.compile(
'<input type="radio" name="group{{pollNum}}" value="{{option}}">{{option}}<br>'
)
});
var PollOptionsListView = Marionette.CompositeView.extend({
template: Handlebars.compile(
//The question part
'<div id="poll">' +
'<div>{{question}}</div>' +
'</div>' +
//The list of options part
'<form name="pollQuestion" action="? what goes here ?">' +
'<div id="poll-options">' +
'</div>' +
'<input type="submit" value="Submit your vote">' +
'</form>'
),
itemView: PollOptionItemView,
appendHtml: function (compositeView, itemView, index) {
var childrenContainer = $(compositeView.$("#poll-options") || compositeView.el);
var children = childrenContainer.children();
if (children.size() === index) {
childrenContainer.append(itemView.el);
} else {
childrenContainer.children().eq(index).before(itemView.el);
}
}
});
MORE DETAILS:
My goal really is to build poll questions dynamically, meaning the questions and options are not known at runtime but rather are queried from a SQL database thereafter. If you were looking at my app I'd launch a poll on your screen via SignalR. In essence I'm telling your browser "hey, go get the contents of poll question #1 from the database and display them". My thought was that CompositeViews are best suited for this because they are data driven. The questions and corresponding options could be stored models and collections the CompositeView template could render them dynamically on demand. I have most of this wired and it looks good. My only issue seems to be the notion of what kind of template to render. A form? Or should my template just plop some radio buttons on the screen with a submit button below it and I write some javascript to try to determine what selection the user made? I'd like not to use a form at all and just use the backbone framework to handle the submission. That seems clean to me but perhaps not possible or wise? Not sure yet.
I'd use the following approach:
Create a collection of your survey questions
Create special itemviews for each type of question
In your CompositeView, choose the model itemView based on its type
Use a simple validation to see if all questions have been answered
Output an array of all questions and their results.
For an example implementation, see this fiddle: http://jsfiddle.net/Cardiff/QRdhT/
Fullscreen: http://jsfiddle.net/Cardiff/QRdhT/embedded/result/
Note:
Try it without answering all questions to see the validation at work
Check your console on success to view the results
The code
// Define data
var surveyData = [{
id: 1,
type: 'multiplechoice',
question: 'What color do you like?',
options: ["Red", "Green", "Insanely blue", "Yellow?"],
result: null,
validationmsg: "Please choose a color."
}, {
id: 2,
type: 'openquestion',
question: 'What food do you like?',
options: null,
result: null,
validationmsg: "Please explain what food you like."
}, {
id: 3,
type: 'checkbox',
question: 'What movie genres do you prefer?',
options: ["Comedy", "Action", "Awesome", "Adventure", "1D"],
result: null,
validationmsg: "Please choose at least one movie genre."
}];
// Setup models
var questionModel = Backbone.Model.extend({
defaults: {
type: null,
question: "",
options: null,
result: null,
validationmsg: "Please fill in this question."
},
validate: function () {
// Check if a result has been set, if not, invalidate
if (!this.get('result')) {
return false;
}
return true;
}
});
// Setup collection
var surveyCollection = Backbone.Collection.extend({
model: questionModel
});
var surveyCollectionInstance = new surveyCollection(surveyData);
console.log(surveyCollectionInstance);
// Define the ItemViews
/// Base itemView
var baseSurveyItemView = Marionette.ItemView.extend({
ui: {
warningmsg: '.warningmsg',
panel: '.panel'
},
events: {
'change': 'storeResult'
},
modelEvents: {
'showInvalidMessage': 'showInvalidMessage',
'hideInvalidMessage': 'hideInvalidMessage'
},
showInvalidMessage: function() {
// Show message
this.ui.warningmsg.show();
// Add warning class
this.ui.panel.addClass('panel-warningborder');
},
hideInvalidMessage: function() {
// Hide message
this.ui.warningmsg.hide();
// Remove warning class
this.ui.panel.removeClass('panel-warningborder');
}
});
/// Specific views
var multipleChoiceItemView = baseSurveyItemView.extend({
template: "#view-multiplechoice",
storeResult: function() {
var value = this.$el.find("input[type='radio']:checked").val();
this.model.set('result', value);
}
});
var openQuestionItemView = baseSurveyItemView.extend({
template: "#view-openquestion",
storeResult: function() {
var value = this.$el.find("textarea").val();
this.model.set('result', value);
}
});
var checkBoxItemView = baseSurveyItemView.extend({
template: "#view-checkbox",
storeResult: function() {
var value = $("input[type='checkbox']:checked").map(function(){
return $(this).val();
}).get();
this.model.set('result', (_.isEmpty(value)) ? null : value);
}
});
// Define a CompositeView
var surveyCompositeView = Marionette.CompositeView.extend({
template: "#survey",
ui: {
submitbutton: '.btn-primary'
},
events: {
'click #ui.submitbutton': 'submitSurvey'
},
itemViewContainer: ".questions",
itemViews: {
multiplechoice: multipleChoiceItemView,
openquestion: openQuestionItemView,
checkbox: checkBoxItemView
},
getItemView: function (item) {
// Get the view key for this item
var viewId = item.get('type');
// Get all defined views for this CompositeView
var itemViewObject = Marionette.getOption(this, "itemViews");
// Get correct view using given key
var itemView = itemViewObject[viewId];
if (!itemView) {
throwError("An `itemView` must be specified", "NoItemViewError");
}
return itemView;
},
submitSurvey: function() {
// Check if there are errors
var hasErrors = false;
_.each(this.collection.models, function(m) {
// Validate model
var modelValid = m.validate();
// If it's invalid, trigger event on model
if (!modelValid) {
m.trigger('showInvalidMessage');
hasErrors = true;
}
else {
m.trigger('hideInvalidMessage');
}
});
// Check to see if it has errors, if so, raise message, otherwise output.
if (hasErrors) {
alert('You haven\'t answered all questions yet, please check.');
}
else {
// No errors, parse results and log to console
var surveyResult = _.map(this.collection.models, function(m) {
return {
id: m.get('id'),
result: m.get('result')
}
});
// Log to console
alert('Success! Check your console for the results');
console.log(surveyResult);
// Close the survey view
rm.get('container').close();
}
}
});
// Create a region
var rm = new Marionette.RegionManager();
rm.addRegion("container", "#container");
// Create instance of composite view
var movieCompViewInstance = new surveyCompositeView({
collection: surveyCollectionInstance
});
// Show the survey
rm.get('container').show(movieCompViewInstance);
Templates
<script type="text/html" id="survey">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title" > A cool survey regarding your life </h3>
</div>
<div class="panel-body">
<div class="questions"></div>
<div class="submitbutton">
<button type="button" class="btn btn-primary">Submit survey!</button>
</div>
</div>
</div >
</script>
<script type="text/template" id="view-multiplechoice">
<div class="panel panel-success">
<div class="panel-heading">
<h4 class="panel-title" > <%= question %> </h4>
</div>
<div class="panel-body">
<div class="warningmsg"><%= validationmsg %></div>
<% _.each( options, function( option, index ){ %>
<div class="radio">
<label>
<input type="radio" name="optionsRadios" id="<%= index %>" value="<%= option %>"> <%= option %>
</label>
</div>
<% }); %>
</div>
</div>
</script>
<script type="text/template" id="view-openquestion">
<div class="panel panel-success">
<div class="panel-heading">
<h4 class="panel-title" > <%= question %> </h4>
</div>
<div class="panel-body">
<div class="warningmsg"><%= validationmsg %></div>
<textarea class="form-control" rows="3"></textarea>
</div>
</div >
</script>
<script type="text/template" id="view-checkbox">
<div class="panel panel-success">
<div class="panel-heading">
<h4 class="panel-title" > <%= question %> </h4>
</div>
<div class="panel-body">
<div class="warningmsg"><%= validationmsg %></div>
<% _.each( options, function( option, index ){ %>
<div class="checkbox">
<label>
<input type="checkbox" value="<%= option %>"> <%= option %>
</label>
</div>
<% }); %>
</div>
</div>
</script>
<div id="container"></div>
Update: Added handlebars example
Jsfiddle using handlebars: http://jsfiddle.net/Cardiff/YrEP8/

JQuery, updating form and exception if update it more then once

I've got index of my elements:
<h2>Index</h2>
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('button').click(function () {
$.post("Home/Swap", $("#log").serialize(), function (data) {
$("#log").load("Home/Index #log");
});
});
});
</script>
<form name="list" action="<%=Url.Action("Post", "Home")%>" method="post" id="log">
<% foreach (var k in Model) { %>
<input type="checkbox" id="ids" name="ids" value="<%=k.pos%>" /><%= k.pos %>. To jest numer: <%=k.name%><br />
<% } %>
</form>
<button>Swap</button>
and Swap method:
public ActionResult Swap(int[] ids)
{
int pos1=ids[0];
int pos2=ids[1];
Element element1 = (from t in db.Elements
where t.pos == pos1
select t).Single();
Element element2 = (from t in db.Elements
where t.pos == pos2
select t).Single();
element1.pos = pos2;
element2.pos = pos1;
db.SaveChanges();
return Index();
}
Everything works fine at the first swap of elements. But when I swap it once, and then try to swap another two I get an exception:
System.NullReferenceException was
unhandled by user code
Message=Object reference not set to an
instance of an object.
(exception from Swap Method)
It's JQuery problem, Im sure. I susspect this $("#log").load("Home/Index #log"); line - it shows me right result, but doesn't work fine if I try to do it more then once. how to fix it?
edit: when I refresh page it works the same -> first works well, after getting an exception (the first touched elements are swaped after refreshing)
When you do .load("url selector") it gets that element, not the contents of that element, so you end up with a nested <form>, so instead create a wrapper and load that wrapper, so you replace the <form>, like this:
<div id="wrapper">
<form name="list" action="<%=Url.Action("Post", "Home")%>" method="post" id="log">
<% foreach (var k in Model) { %>
<input type="checkbox" id="ids" name="ids" value="<%=k.pos%>" /><%= k.pos %>. To jest numer: <%=k.name%><br />
<% } %>
</form>
</div>
<button>Swap</button>
Then load that element instead, so you're replacing the <form>, like this:
$(function () {
$('button').click(function () {
$.post("Home/Swap", $("#log").serialize(), function (data) {
$("#wrapper").load("Home/Index #log");
});
});
});