sails.js - Locals problem (Post comments) - javascript

I have problem with locals. In post model I have comments collection, everything is fine and without problems get user and post id but I can't output in views username and user avatar from post.postComments[i].author.username/avatar ( look below in index.js ). What could be a problem ?
PostController.js
index: function(req, res){
Post.find({}).populate('author').populate('postComments').exec(function(err, results) {
res.send(200, results);
});
},
addComment: function(req, res){
var params = req.allParams();
Comment.create({author: params.author, content: params.content, post: params.post, postId: params.postId}, function(err, comment){
if (err){ res.send(500, err); console.trace(err); }
else{
res.send(200, comment);
res.redirect("/");
console.log("Testt");
}
});
},
index.ejs
<form action="/addComment" method="POST">
<div class="row">
<div class="col-10 col-sm-11 col-md-11">
<input type="hidden" name="author" value="<%= req.user.id %>">
<input type="hidden" name="postId" value="<%= post.id %>">
<input type="text" id="postcomment" name="content" placeholder="Comment here..">
</div>
<div class="col-1 col-sm-1 col-md-1">
<button type="submit"><i class="fas fa-location-arrow" style="color: #0c5460; font-size: 23px; margin-left: -10px;"></i></button>
</div>
</div>
<% if(post.postComments.length > 0) { %>
<% for(var i = 0; i < post.postComments.length; i++) { %>
<div id='<%- post.postComments[i].id%>'>
<div style="padding-top: 10px;">
<div class="container" style="background-color: #ccc; border-radius: 20px;">
<div class="row">
<div class="col-md-1" style="padding: 0;">
<img src='/images/profileimage/<%- post.postComments[i].author.profileimage_uid %>' style="width: 30px; height: 30px; border-radius: 80px; border: 1px solid #ccc;">
</div>
<div class="col-md-2" style="padding: 3px; margin: inherit;">
<%- post.postComments[i].author %>
</div>
<div class="col-md-7" style="padding: 4px; word-break: break-all;">
<p>- <%- post.postComments[i].content%></p>
</div>
</div>
</div>
</div>
</div>
<% } %>
<% } %>

I'm doing some guessing based on limited code in your question, but...
If this is the line that fetches your data to add to your view:
Post.find({}).populate('author').populate('postComments')
and if the Author of a post Comment is a linked collection, then the problem is that your author is not populated into your post comments. You populate your Post with the Comments, but never go on to populate the Comments with their Author
Apologies if I'm guessing wrong about your data storage.
EDIT
If you want to know how to implement the two-level "nested" populate you are trying to do... sails does not do this out of the box, you have to write the code yourself. Here's one way, starting by fetching a user dictionary:
User.find({}).exec(function(err,users) {
// handle any error
var userDict = {};
for (var i = 0; i < users.length; i++) {
userDict[users[i].id] = users[i];
}
// now fetch your posts
Post.find({}).populate('postComments').exec(function(err, posts) {
// handle any error
// now use your user dictionary to populate
for (var i = 0; i < posts.length; i++) {
var post = posts[i];
if (post.author && userDict[post.author]) {
post.author = userDict[post.author];
}
for (var j = 0; j < post.postComments.length; j++) {
var comment = post.postComments[j];
if (comment.author && userDict[comment.author]) {
comment.author = userDict[comment.author];
}
}
}
return res.send(200, posts);
});
});
This is pretty ugly, but something like this may be required if you want this two-level population. Some thoughts:
You can flatten the callbacks some if you're able to use a promise library.
If you have a very large number of users, you may decide to build your user dictionary after getting your posts and fetch just the ones you need.

Related

Blazor, Image from Graph API not loading without refreshing

In my Blazor app when I user signs in (through Azure AD) I am using an async Graph API call to retrieve their profile picture and display it in the top bar.
The problem is it only loads after a window refresh.
To try to load it after I re-run the async method when they click login, since the OnInitializedAsync() runs before they sign in as well then, the loginDisplay view is first rendered.
after converting the picture to a Base64String I then check if the string is not empty (result of null or a failed conversion for the method I wrote) and try o display the image:
#if (userPhoto != string.Empty)
{
<div class="col-md-auto align-self-center position-relative">
<a #onclick="() => userMenu()">
<img id="mePicture" class="rounded-circle" src="#userPhoto" style="width: 45px; height: 45px;" />
</a>
#if (renderUserMenu)
{
<div class="container-fluid position-absolute top-0 end-50 border-1 p-2 border-dark shadow bg-light rounded bg-dark" style="width: 120px;" #onblur="() => userMenu()">
<button class="btn btn-sm btn-outline-secondary m-0 p-0 shadow-sm" style="height: 20px; width: 20px;" #onclick="() => userMenu()">X</button>
<div class="text-center align-content-center">
<img class="rounded-circle" src="#userPhoto" style="width: 65px; height: 65px;"/>
<button class="btn btn-sm btn-outline-secondary shadow-sm" #onclick="BeginLogout">Log out</button>
</div>
</div>
}
</div>
}
else
{
<div class="col-md-auto align-self-center">
<div class="rounded-circle" style="width: 45px; height: 45px;"> #initials</div>
<img class="rounded-circle" src="#userPhoto" style="width: 45px; height: 45px;" />
</div>
}
I don't know how to force it to render after the async method fetches it.
My guess is that the LodingDisplay.razor partial doesn't refresh after login in.
I tried using JS to load it (didn't fail, but didn't render it)
Also tried a secondary function on the #onclick event of the login button also to no effect.
Any help appreciated.
Relevant C# Code:
protected override async Task OnInitializedAsync()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var _user = authState.User;
if (_user.Identity.IsAuthenticated)
{
claims = _user.Claims;
var request = GraphClient.Me.Request();
user = await request.GetAsync();
await GetMePhotoAsync();
authMessage = "";
}
}
private async Task GetMePhotoAsync()
{
var photo = await GraphClient.Me.Photo.Content.Request().GetAsync();
byte[] bytes;
using (var memoryStream = new MemoryStream())
{
photo.CopyTo(memoryStream);
bytes = memoryStream.ToArray();
}
var photoString = Convert.ToBase64String(bytes);
userPhoto = string.Format("data:image/jpeg;base64,{0}", photoString);
var uid = user.Id;
var displayName = user.DisplayName;
var displayNameList = displayName.Split(" ").ToList();
if (displayNameList.Count > 1)
{
initials = displayNameList[0].Substring(0) + displayNameList[1].Substring(0);
}
else
{
initials = displayName.Substring(0, 1);
}
}

How to display objects from local storage on to the website?

So I have created this website which lets users search for the weather in different cities. These searches then get saved in an object which looks like this through the localstorage.
To display this on the website I've tried to make the following
<div class="jumbotron bg-white">
<div class="container">
<h1>Latest requests</h1>
<h5 id="get-weather">We remember your five last requests for you :)</h5>
<div class="last-requests">
<img src="" class="imgs">
<p class="cityname" class="mr-3"></p>
<p class="cityweather"></p>
<p class="citytemp"></p>
<p class="citywind"></p>
</div>
</div>
</div>
And the following JS
// Displays last 5 requests/searches
function displayLastRequests() {
const lastReq = JSON.parse(localStorage.getItem('last-requests'))
console.log(lastReq)
if (displayLastRequests > 0) {
// for loop request
for (req in lastReq) {
$(".imgs").attr('src', req.imgurl);
$(".cityname").text(req.city_name);
$(".cityweather").text(req.city_weather);
$(".citytemp").text(req.city_temp + " °C");
$(".citywind").text(req.city_wind + " m/s");
}
}
};
displayLastRequests()
Not quite sure where I'm doing something wrong, any help would be much appreciated.
Your existing code will only show the last search as there's only one "cityname" to output to.
You can use HTML5 <template> to provide a ...well... template which you can copy and add as required.
Your for loop may also need to be for (.. of ..) rather than .. in .. which will give indexes rather than entries.
Updated code:
function displayLastRequests() {
//const lastReq = JSON.parse(localStorage.getItem('last-requests'))
// Sample data
const lastReq = [
{city_name:"Istanbul", weather:"Cloudy"},
{city_name:"Madrid", weather:"Stormy"},
{city_name:"London", weather:"Sunny"}
];
console.log(lastReq)
for (req of lastReq) {
var clone = $($("#last-request-template").html());
clone.appendTo(".last-requests");
clone.find(".cityname").text(req.city_name);
clone.find(".cityweather").text(req.weather);
//clone.find(".imgs").attr('src', req.imgurl);
//clone.find(".citytemp").text(req.city_temp + " °C");
//clone.find(".citywind").text(req.city_wind + " m/s");
}
};
displayLastRequests()
.last-requests { border: 1px solid #CCC; }
.last-request+.last-request { border-top: 1px solid #CCC; }
p { padding:5px; margin: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="jumbotron bg-white">
<div class="container">
<h1>Latest requests</h1>
<h5 id="get-weather">We remember your five last requests for you </h5>
<template id='last-request-template'>
<div class='last-request'>
<!--<img src="" class="imgs">-->
<p class="cityname"></p>
<p class="cityweather"></p>
<!--<p class="citytemp"></p>-->
<!--<p class="citywind"></p>-->
</div>
</template>
<div class="last-requests"> </div>
</div>
</div>

Error: [$injector:unpr]

I am working in Angular Js . I am trying to create a Login System from Sql Database based on username and password. I enter the right username and password but this do not go to page I want to redirect user home page but I can not do it . I got following error when I submit username and password into textbox from Console Application ....
angular.min.js:123 Error: [$injector:unpr] http://errors.angularjs.org/1.6.5/$injector/unpr?p0=myServiceProvider%20%3C-%20myService%20%3C-%20myCntrl
at angular.min.js:7
at angular.min.js:46
at Object.d [as get] (angular.min.js:43)
at angular.min.js:46
at d (angular.min.js:43)
at e (angular.min.js:44)
at Object.invoke (angular.min.js:44)
at O.instance (angular.min.js:94)
at q (angular.min.js:69)
at f (angular.min.js:62)
Here is my Module.Js Code...
var app = angular.module("myApp", [])
.controller("myCntrl", function ($scope, myService) {
$scope.LoginCheck = function () {
var User = {
UserName: $scope.uName,
Password: $scope.password
};
$("#divLoading").show();
var getData = myService.UserLogin(User);
getData.then(function (msg) {
if (msg.data == "0") {
$("#divLoading").hide();
$("#alertModal").modal('show');
$scope.msg = "Password Incorrect !";
}
else if (msg.data == "-1") {
$("#divLoading").hide();
$("#alertModal").modal('show');
$scope.msg = "Username Incorrect !";
}
else {
uID = msg.data;
$("#divLoading").hide();
window.location.href = "/Home/Index";
}
});
debugger;
}
function clearFields() {
$scope.uName = '';
$scope.uPwd = '';
}
//move this function inside `myCntrl` controller function.
$scope.alertmsg = function () {
$("#alertModal").modal('hide');
};
});
app.service("myService", function ($http) {
this.UserLogin = function (User) {
var response = $http({
method: "post",
url: "/Login/Login",
data: JSON.stringify(User),
dataType: "json"
});
return response;
}
});
Here is Login Controller Code ..
public class LoginController : Controller
{// GET: /Login/
public ActionResult Login()
{
return View();
}
[HttpPost]
public string Login(UserLogin data)
{
bool isPasswordCorrect = false;
string un = data.UserName;
string Password = data.Password;
using (HalifaxDatabaseEntities entity = new HalifaxDatabaseEntities())
{
var user = entity.UserLogins.Where(u => u.UserName == un).FirstOrDefault();
if (user != null)
{
if (Password == user.Password)
{
Session["LoginID"] = user.ID;
Session["Username"] = user.Firstname + ' ' + user.Lastname;
return user.ID.ToString();
}
else
{
return "0";
}
}
else
{
return "-1";
}
}
}
}
}
Here is my HTML CODE ....
#{
Layout = null;
}
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<title></title>
<script src="~/Scripts/angular.min.js"></script>
<script src="~/Scripts/LoginScript/Module.js"></script>
<script src="~/Scripts/jquery-ui-1.12.1.min.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
<link href="~/Content/bootstrap.min.css" rel="stylesheet" />
<link href="~/Content/Site.css" rel="stylesheet" />
<link href="~/Content/ui-bootstrap-csp.css" rel="stylesheet" />
</head>
<body>
<div ng-controller="myCntrl">
<h1>
<img src="~/Content/images/Loginicon.png" />
</h1>
<br />
<div id="alertModal" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<!-- dialog body -->
<div class="modal-body">
<button type="button" id="btn" value="Close" class="close" data-dismiss="modal">×</button>
{{msg}}
</div>
<!-- dialog buttons -->
<div class="modal-footer">
<button type="button" ng-click="alertmsg()" class="btn btn-primary">OK</button>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="panel panel-success" style="width: 50%;">
<div class="panel-heading">Login</div>
<div class="panel-body" style="box-shadow: -6px 2px 46px 7px #888888; padding: 20px;">
<form name="loginForm" novalidate>
<div class="form-horizontal">
<div class="form-group">
<div class="row">
<div class="col-md-3" style="text-align: right;">
Username :
</div>
<div class="col-md-6">
<div class="input-group">
<input type="text" class="form-control" id="Uname" placeholder="Username" ng-model="uName" name="Username" required autofocus />
<span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
</div>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-3" style="text-align: right;">
Password :
</div>
<div class="col-md-6">
<div class="input-group">
<input type="password" class="form-control" id="password" placeholder="Password" ng-model="password" name="Password" required autofocus />
<span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-6">
<div id="divLoading" style="margin: 0px; padding: 0px; position: fixed; right: 0px; top: 0px; width: 100%; height: 100%; background-color: #666666; z-index: 30001; opacity: .8; filter: alpha(opacity=70); display: none">
<p style="position: absolute; top: 30%; left: 45%; color: White;">
please wait...<img src="~/Content/images/load.png">
</p>
</div>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-5" style="text-align: right;">
<button id="btnLogin" type="submit" class="btn btn-success" ng-disabled="!(password && uName)" ng-click="LoginCheck()">Login</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
Here is Solution Explorer Screen Shot...
Here is the screen shot when I lunch developer tools....
You have declared alertmsg outside controller function. Move it inside a myCntrl controller factory function. So basically because of $scope.alertmsg function kept outside $scope variable isn't defined, that's why JS compiler yelling there. And further statement aren't get executed. Since myService service doesn't get register in myApp angular module and it says myService unknown provider
http://errors.angularjs.org/1.6.5/$injector/unpr?p0=myServiceProvider%20%3C-%20myService
var app = angular.module("myApp", [])
.controller("myCntrl", function ($scope, myService) {
$scope.LoginCheck = function () {
//code as is
}
function clearFields() {
$scope.uName = '';
$scope.uPwd = '';
}
//move this function inside `myCntrl` controller function.
$scope.alertmsg = function () {
$("#alertModal").modal('hide');
};
});
Apart from angularjs errors, you have fix all the error appearing in
console. Likewise load jQuery before bootstrap.js

ng-repeat is showing data that is not there

I am using data load dynamically through a function and then populate using ng-repeat.
Below is the controller code.
$scope.loadproducts = function(item_id) {
var pdata = $.param({
item_id : item_id,
});
$http({
method: 'POST',
url: baseurl + 'api/get_items_in_category_cart',
data: pdata,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function successCallback(response) {
$scope.items = response.data.products;
$scope.description = response.data.description;
console.log($scope.items);
$timeout(apply_tooltip, 500);
}, function errorCallback(response) {
});
};
View
<div class="container col-md-6" ng-repeat="item in items">
<div class="panel panel-primary">
<div class="panel-heading">
<h4 class="panel-title">{{ item.title }}</h4>
</div>
<div class="panel-body">
<div class="col-md-3 col-xs-5"><img ng-src="{{ item.thumbnail }}"></div>
<div class="col-md-9" style="">
<div class="row">
<div class="col-md-12">
<span style="font-size: 14px; font-weight: 500;">{{ item.description }}</p>
</div>
</div>
<div class="row">
<div class="col-md-4 text-right" style=" text-align: left;">
<p>{{item.price | currency:"NZ $"}}</p>
</div>
<div class="col-md-8" style=" text-align: left;">
<input type="text" value="{{item.qty}}" style="width:50px; height: 28px; line-height: 28px;" readonly id="qty_{{item.id}}"/>
</div>
</div>
</div>
</div>
</div>
</div>
Upon initial page load the the code does what it is supposed to do.
There is a proceed button where the user gets taken to another page.
If the user goes back, the items are supposed to reload again.
The issue is during the reload the text box that is supposed to show the quantity for each item ->> value="{{item.qty}}" seems to show the same qty for all qty text boxes regardless of the original qty they had.
The console.log($scope.items) shows the proper value that it is supposed to have. It's just the display that is showing the wrong qty.
Any idea guys?
If your data is loaded correctly, you can go in two ways:
1 - Try to use a ng-model in your input
<input type="text" ng-model="item.qty" style="width:50px; height: 28px; line-height: 28px;" readonly id="qty_{{item.id}}"/>
You can either remove the 'readonly' property and add a ng-disabled="true".
2 - You can try to clear your list before you get the new values. So, before you load again your values from back-end you could just clear your data:
$scope.items = [];
And put the data again when the request is completed.
Note that this second solution it isn't conventional, and should be used in case that you can't find out another solution.

How can I get specific information from an external api to my ng-repeat

main.html
<div class="row" ng-repeat="post in myBlogPosts.slice().reverse()">
<br>
<div class="col-md-9 text-center">
<a href="#/blog-post/{{post._id}}">
<div class="thumbnail mTextBg customShadow">
<br>
<img class="img-responsive" src="http://placekitten.com/700/400" alt="">
<div class="caption">
<h3>{{post.imdbId}}</h3>
<p>{{post.blogContent}}</p>
</div>
</div>
</a>
</div>
<div class="col-md-3">
// I WANT THIS PART !!
<div class="well sideBars customShadow">
<img class="img-responsive" ng-src="{{film.Poster}}" title="{{film.Title}}">
<h4 class="text-center">{{film.Title}}</h4>
<p class="text-center" style="margin-bottom: 2px;"><b>Year:</b> {{film.Year}}</p>
<p class="text-center"><span class="customMargin">Runtime: {{film.Runtime}}</span></p>
<p class="text-center"><span class="customMargin">Director: {{film.Director}}</span></p>
<p class="text-center"><span class="customMargin">Writer: {{film.Writer}}</span></p>
<p class="text-center"><span class="customMargin">Actors: {{film.Actors}}</span></p>
</div>
</div>
</div>
This is part of my main.html . In h3 and p tags, I get imdbId and blogContent from my database and put it in ng-repeat in order to traverse blog posts in list. I want to be able get other information(under // I WANT THIS PART) for every post in myBlogPost.
MainController.js
var refresh = function() {
$http.get('/myDatabase').success(function(response) {
$scope.myBlogPosts = response;
});
};
refresh();
This part work as expected when page loaded.
I need also these parts in Main Controller ;
var onGetFilmData = function (data) {
$scope.film = data;
};
var onError = function (reason) {
$scope.error = reason;
};
imdb.getImdbInfo(-- need Id --).then(onGetFilmData, onError);
But I need to put each post id somehow in order to get specific data from Imdb api.
Imdb.js
(function(){
var imdb = function($http){
var getImdbInfo = function (id) {
return $http.get('http://www.omdbapi.com/?i=' + id + '&plot=short&r=json')
.then(function(response){
return response.data;
});
};
return{
getImdbInfo: getImdbInfo
};
};
var module = angular.module('myApp');
module.factory('imdb', imdb);
})();
If I delete id part and put a specific id string in getImdbInfo function, all post in main.html fill with just one film information. I want to fetch those data for each film in my database(I am holding imdb id of each film in my database).
MainController
var jsonObj = {};
var refresh = function() {
$http.get('/myDatabase').success(function(response) {
jsonObj = response;
for(var i = 0; i < jsonObj.length ; i++){
jsonObj[i].title = '';
}
for(var i = 0; i < jsonObj.length ; i++){
(function(i) {
imdb.getImdbInfo(jsonObj[i].imdbId).then(function (data) {
jsonObj[i].title = data.Title;
});
})(i);
}
$scope.myBlogPosts = jsonObj;
});
};
refresh();
main.html
<div class="row" ng-repeat="post in myBlogPosts.slice().reverse()">
<br>
<div class="col-md-9 text-center">
<a href="#/blog-post/{{post._id}}">
<div class="thumbnail mTextBg customShadow">
<br>
<img class="img-responsive" src="http://placekitten.com/700/400" alt="">
<div class="caption">
<h3>{{post.imdbId}}</h3>
<p>{{post.blogContent}}</p>
</div>
</div>
</a>
</div>
<div class="col-md-3">
<!-- Side Widget Well -->
<div class="well sideBars customShadow">
<h4 class="text-center">{{post.title}}</h4>
</div>
</div>
</div>
I solve my problem with adding response from Imdb to my json object which is coming from database. So I can easily use them in ng-repeat.

Categories