Meteor helper function exception when retrieving the count of all users - javascript

On my website I'm trying to display the number of users registered. The problem is that I get a long exception on render. My question is how do you do this dynamically instead of statically?
The code works on start up but if I switch pages and come back it defaults to a blank. The code follows:
Template.Home.helpers({
UserAmount: function() {
var Count = Meteor.users.find().count();
document.getElementById("UsersSignedUp").innerHTML = Count;
console.log("Users-helper: "+Count);
}
});
the following is now the HTML section on the home page
<section class="home-main">
<div class="container">
<div class="stats">
<div class="row">
<div class="col-md-4">
<label id="UsersSignedUp">{{UserAmount}}</label>
<span>Users</span>
</div>
<div class="col-md-4">
0
<span>Listings</span>
</div>
<div class="col-md-4">
0
<span>Matches</span>
</div>
</div>
</div>
</div>
</section>
I was able to make an onRender function to supplement the error but I'd rather not have to rely on it. I know that listings and Matches are both set to 0 but if I can fix this error the others will follow the same format.
This is the exception:
[Log] Exception in template helper: UserAmount#http://localhost:3000/Nomad.js?1c729a36a6d466ebe12126c301d77cc72299c832:56:47 (meteor.js, line 888)
http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:2880:21
http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:1651:21
http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:2928:71
_withTemplateInstanceFunc#http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:3476:16
http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:2927:52
call#http://localhost:3000/packages/spacebars.js?7bafbe05ec09b6bbb6a3b276537e4995ab298a2f:172:23
mustacheImpl#http://localhost:3000/packages/spacebars.js?7bafbe05ec09b6bbb6a3b276537e4995ab298a2f:109:30
mustache#http://localhost:3000/packages/spacebars.js?7bafbe05ec09b6bbb6a3b276537e4995ab298a2f:113:44
http://localhost:3000/template.Nomad.js?68e12845c8ef55042e39b4867426aab290c3711d:95:30
doRender#http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:2011:32
http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:1865:22
_withTemplateInstanceFunc#http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:3476:16
http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:1864:54
_withCurrentView#http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:2197:16
lookup:UserAmount:materialize#http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:1863:34
_compute#http://localhost:3000/packages/tracker.js?6d0890939291d9780f7e2607ee3af3e7f98a3d9c:327:36
Computation#http://localhost:3000/packages/tracker.js?6d0890939291d9780f7e2607ee3af3e7f98a3d9c:243:18
autorun#http://localhost:3000/packages/tracker.js?6d0890939291d9780f7e2607ee3af3e7f98a3d9c:566:34
autorun#http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:1875:29
http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:2005:17
nonreactive#http://localhost:3000/packages/tracker.js?6d0890939291d9780f7e2607ee3af3e7f98a3d9c:593:13
_materializeView#http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:2004:22
materializeDOMInner#http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:1476:31
_materializeDOM#http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:1428:26
http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:2040:46
nonreactive#http://localhost:3000/packages/tracker.js?6d0890939291d9780f7e2607ee3af3e7f98a3d9c:593:13
_materializeView#http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:2004:22
render#http://localhost:3000/packages/blaze.js?a5c324925e5f6e800a4c618d71caf2848b53bf51:2296:25
insert#http://localhost:3000/packages/iron_dynamic-template.js?d425554c9847e4a80567f8ca55719cd6ae3f2722:522:15
insert#http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:1632:22
maybeAutoInsertRouter#http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:1622:20
If there is any other information I should provide please let me know.

Try using this as the helper instead:
Template.Home.helpers({
UserAmount: function() {
return Meteor.users.find().count();
}
});
The idea is this helper is called UserAmount so the value it returns on the Home template should replace itself into the handlebars expression {{UserAmount}}
You don't have to do the heavy lifting in changing the DOM yourself. Meteor probably gets confused as to why the dom is changing when it tries to change it and it gives up that error.

Related

Is there a method to implement through data attributes in JavaScript?

I want implement the card content in the card when expanded, I have used the help of JavaScript to implement the title, image, description ,I have used 'data-title' and 'data-type' for displaying title and image respectively which are by the working fine and when I try to implement card description by using data-desc attribute , it is displaying 'undefined'.
Anyone please help me with my code.
My code link: https://codepen.io/Avatar_73/pen/ExyNdMK
const getCardContent = (title, type, desc) => {
return `
<div class="card-content">
<h2>${title}</h2>
<img src="./assets/${type}.png" alt="${title}">
<p>${desc}</p>
</div>
`;
}
<div class="cards">
<div class="card" data-type="html" data-desc="I am HTML">
<h2>HTML5</h2>
</div>
</div>
On line 115, you forgot to pass the desc to your getCardContent function
Change to:
const content = getCardContent(card.textContent, card.dataset.type, card.dataset.desc)
Change your calling method signature from
getCardContent(card.textContent, card.dataset.type)
to
getCardContent(card.textContent, card.dataset.type, card.dataset.desc)
you are not passing the third parameter(card.dataset.desc) in the method thus it is rendering the value as undefined.

How to make ko.sortable work with ko.computed

I am using knockout and Ryan Niemeyers ko.sortable.
My aim is to move items from one container to another. This works nice.
However, due to other circumstances I want the observableArrays object to be drag´n´dropped to be ko.computed.
I have a basic array containing all my elements
self.Textbatches = ko.observableArray();
then I have a filter version
self.TextsTypeOne = ko.computed(function(){
return ko.utils.arrayFilter(self.Textbatches(),function(t){
return t.type() === '1';
});
});
I have another filtered list with the texts dragging:
self.allocationableTextbatches = ko.computed(function(){
return ko.utils.arrayFilter(self.Textbatches(),function(t){
return t.type() === null;
});
});
Then I create my two containers with ´self.TextsTypeOne´ and ´self.allocationableTextbatches´:
<div id="allocationable-texts" class="menupanel">
<h4 class="select">Text(s) to allocate:</h4>
<div class="tb-table">
<div class="tb-list" data-bind="sortable:{template:'textbatchTmpl',data:$root.allocationableTextbatches,allowDrop:true}"></div>
</div>
</div>
<div id="TypeOne-texts" class="menupanel">
<h4 class="select">Text(s) on scene:</h4>
<div class="tb-table">
<div class="tb-list" data-bind="sortable:{template:'textbatchTmpl',data:$root.TextsTypeOne,allowDrop:true}"></div>
</div>
</div>
where
<script id="textbatchTmpl" type="text/html"><div class="textbatch" data-bind="click: $root.selectedText">
<span class="tb-title" data-bind="text: title"></span>
I can easily add buttons to my template setting type to 'null' and '1' respectively, and make it work that way. However I want this to be a drag and drop feature (as well) and the ko.sortable works on the jquery-ui objects. Thus it needs ´ordinary´ ´ko.observableArrays´ and not ´ko.computed´s as it is 'splice´-ing the observableArray, and dragging and dropping results in an error "TypeError: k.splice is not a function".
I have tried to make ´self.allocationableTextbatches´ and ´self.TextsTypeOne´ writable computed, but to no avail.
Any help and suggestions is highly appreciable!
Thanks in advance!

changing the order that angular and the browser choose to do things

I am having two problems with the order that things are happening in angular and the browser. I am using laravel as the backend and the following is happening.
User clicks a link and laravel routes the user to /trainers/all. trainers/all includes html which includes html which includes trainersController and ratingsCtrl. trainerscontroller makes a get request to the server for the trainers json object. ng-repeat loops through this object and displays information for each trainer including an image and their rating.
ratingsCtrl then takes their rating and turns it into a number of stars using angular.ui rating functionality.
The problem is two fold, the browser is requesting the images before angular has put the url slug from the json object into the img object. The browser seems to try multiple times failing until angular has done its work.
The second is that angular tries to take the value of the rating before trainersController has put it in the dom element as well. this second error stops the ng repeat so only one trainer is displayed, if I remove the ratingctrl then the ngrepeat completes with no issues besides the img issue. If I hardcode the rating value it works also.
here is the error
TypeError: undefined is not a function
at Ia.% (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js:145:85)
at Ia.% (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js:145:85)
at t.constant (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js:157:136)
at Ia.% (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js:145:85)
at t.constant (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js:157:136)
at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js:48:165
at q (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js:7:380)
at E (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js:47:289)
at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js:56:32
at f (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js:42:399)
here is the relevant code
app.js
var trainercompareapp = angular.module("trainercompare", ['ui.bootstrap']);
trainercompareapp.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('%%');
$interpolateProvider.endSymbol('%%');
});
function trainersController($scope, $http) {
$http.get('/trainers').success(function(trainers) {
$scope.trainers = trainers;
});
}
var RatingCtrl = function ($scope) {
$scope.rate = 7;
$scope.max = 10;
$scope.isReadonly = false;
$scope.hoveringOver = function(value) {
$scope.overStar = value;
$scope.percent = 100 * (value / $scope.max);
};
$scope.ratingStates = [
{stateOn: 'glyphicon-ok-sign', stateOff: 'glyphicon-ok-circle'},
{stateOn: 'glyphicon-star', stateOff: 'glyphicon-star-empty'},
{stateOn: 'glyphicon-heart', stateOff: 'glyphicon-ban-circle'},
{stateOn: 'glyphicon-heart'},
{stateOff: 'glyphicon-off'}
];
};
html
<div class="col-xs-12 container" ng-controller="trainersController">
<div ng-repeat="trainer in trainers">
<div class="col-xs-6">
<div class="row">
<img class="col-xs-6"src="%% trainer.user.images[0]['s3Url'] %%">
</div>
<div class="row">
<div class="col-xs-7" ng-controller="RatingCtrl">
<rating
class="rating"
value="%% trainer.rating %%"
max="max"
readonly="true"
on-hover="hoverOver(value)"
on-leave="overStar = null">
</rating>
</div>
<div class="col-xs-4">
View Profile
</div>
</div>
<hr>
</div>
</div>
</div>
Ugh, changing value="%% trainer.rating %% to just value="trainer.rating" made it work like a charm

I have two divs with the same ng-controller in AngularJS, how can I make them share information?

I have two different div tags in my html code referencing the same controller in AngularJS. What I suspect is that since these divs aren't nested they each have their own instance of the controller, thus the data is different in both.
<div ng-controller="AlertCtrl">
<ul>
<li ng-repeat="alert in alerts">
<div class="span4">{{alert.msg}}</div>
</li>
</ul>
</div>
<div ng-controller="AlertCtrl">
<form ng-submit="addAlert()">
<button type="submit" class="btn">Add Alert</button>
</form>
</div>
I know this could easily be fixed by including the button in the first div but I feel this is a really clean and simple example to convey what I am trying to achieve. If we were to push the button and add another object to our alerts array the change will not be reflected in the first div.
function AlertCtrl($scope) {
$scope.alerts = [{
type: 'error',
msg: 'Oh snap! Change a few things up and try submitting again.'
}, {
type: 'success',
msg: 'Well done! You successfully read this important alert message.'
}];
$scope.addAlert = function() {
$scope.alerts.push({
type: 'sucess',
msg: "Another alert!"
});
};
}
This is a very common question. Seems that the best way is to create a service/value and share between then.
mod.service('yourService', function() {
this.sharedInfo= 'default value';
});
function AlertCtrl($scope, yourService) {
$scope.changeSomething = function() {
yourService.sharedInfo = 'another value from one of the controllers';
}
$scope.getValue = function() {
return yourService.sharedInfo;
}
}
<div ng-controller="AlertCtrl">{{getValue()}}</div>
<div ng-controller="AlertCtrl">{{getValue()}}</div>
If I understand the question correctly, you want to sync two html areas with the same controller, keeping data synced.
since these divs aren't nested they each have their own instance of the controller, thus the data is different in both
This isn't true, if you declare the controllers with the same alias (I'm using more recente angular version):
<div ng-controller="AlertCtrl as instance">
{{instance.someVar}}
</div>
<div ng-controller="AlertCtrl as instance">
{{instance.someVar}} (this will be the same as above)
</div>
However, if you WANT them to be different and comunicate each other, you will have to declare different aliases:
<div ng-controller="AlertCtrl as instance1">
{{instance1.someVar}}
</div>
<div ng-controller="AlertCtrl as instance2">
{{instance2.someVar}} (this will not necessarily be the same as above)
</div>
Then you can use services or broadcasts to comunicate between them (the second should be avoided, tough).

IE9 ignores angular objects (within ngRepeat)

I've got an ng-include and ng-controller within an ng-repeat, and IE randomly craps itself when it sees the child instance object of the repeat:
inside main.html
<section ng-repeat="panel in sidepanels">
<h2 class="twelve columns">
<span class="twelve">
<i class="icon {{panel.icon}}"></i> <!-- resolves properly -->
{{panel.controller.name}} <!-- empty -->
</span>
</h2>
<div
ng-include src="'views/'+panel.controller.name.toLowerCase()+'.html'"
ng-controller="panel.controller"
></div>
</section>
inside controllers.js
function Main($scope) {
…
$scope.sidepanels = [
{
"controller": Alerts,
"icon": "icon-warning-sign"
}
];
…
}
function Alerts($scope,WebSocket) {
$scope.alerts = [];
WebSocket.on('…', function(data) { … });
WebSocket… //WebSocket is a Service
}
Except instead of throwing an error in console, it just silently ignores the fact that it sometimes can't resolve panel. I only noticed this was the case because I saw a failed GET on views/.html.
I checked MSDN, and supposedly IE supports the name property.
The property name doesn't work very well for functions on IE.
You can use the following snippet to retrieve a function's name (as described here):
func.toString().match(/^function ([^(\s]+)/)[1]
Add a function to your Main controller that creates the template path, like this:
$scope.getTemplatePath= function(controller) {
return 'views/' + angular.lowercase(controller.toString().match(/^function ([^(\s]+)/)[1] + '.html');
};
And on the HTML:
<div ng-include="getTemplatePath(panel.controller)" ng-controller="panel.controller"></div>
jsFiddle: http://jsfiddle.net/bmleite/faGRk/

Categories