Text flashes and disappear - meteor

This question will be similar to this one I asked earlier. I got it working, copied solution, but there must be something Im missing here. I start with the code:
router.js:
this.route('note',{
path: '/note/:_id',
data: function() { return Notes.findOne(this.params._id); },
});
this.route('notes', {
waitOn: function() { return Meteor.subscribe('notes')}
});
template 'notes' :
<table id="notes-table">
{{#each notes}}
<tr id="table-row">
<td id="indicator"></td>
<td id="remove-note" class="icon-close notes-table-class"></td>
<td id="notes-title" class="Nbody notes-table-class">{{this.title}}</td>
<td id="notes-body-prev" class="Nbody notes-table-class">{{this.body}}</td>
</tr>
{{/each}}
</table>
helpers.js :
Template.notes.events({
'click .Nbody': function(events,template){
console.log('displaying note : ' + this._id);
Router.go('/note/'+this._id);
}
});
Template 'note' is simple {{title}} and {{body}}
The problem is, when I click on the note table body it does take me where it should be, which is single note, but its text just flashes for a second and disappear immediately and never comes back, so I see nothing..
Question: What is the problem?
I do not get any error in the console.
The Differences between this and 'memo' solutions are:
- here im using table instead of span's
- I dropped wrapping clickable body in 's tags ( I think this might be the reason )

You have to subscribe in 'note' route to be able to retrieve it:
client:
this.route('note',{
path: '/note/:_id',
waitOn: function() { return Meteor.subscribe('note',this.params._id )}
data: function() { return Notes.findOne(this.params._id); },
});
this.route('notes', {
waitOn: function() { return Meteor.subscribe('notes')}
});
server:
Meteor.publish('note', function(noteId){
return Notes.find(this.params._id);
})
In comment you wrote that it started to work when you : moved waitOn to Router.configure. Route.configure waitOn is working for all routes and because Method.publish('notes') function probably returns Notes.find() then note starts to work correct.

Related

ngTable Will Not Reload Data

I'm an experienced C# and MVC programmer, but just starting out with AngularJS. I've been using ngTable (trying to anyway) with limited success, but one issue I cannot resolve - no matter what I do, I cannot get a particular table to refresh when the data has changed.
I'm getting data from a Web API via a factory - it's a list of suppliers associated with a particular Product. The first time the screen is loaded, the data is brought back and the table displays fine - any subsequent call, the data is returned, but the table is not updating. Other areas of the page are updating as expected.
I've done some console logging, and can see that the data is coming back. I've tried the tableParams.reload() function, and setting the tableParams.total() but nothing seems to trigger the table to refresh.
This is my function in the Controller:
function getStockSupplier() {
console.log("getStockSupplier()");
$scope.stockSupplierTableParams = {};
stockSupplier.getAll({ url: "localhost:52457", sku: $scope.model.sku })
.$promise.then(function (response) {
$scope.stockSupplier = response;
$scope.stockSupplierTableParams = new NgTableParams({
}, {
dataset: response
});
console.log("Got result");
console.log("Length: " + $scope.stockSupplierTableParams.settings().dataset.length);
$scope.stockSupplierTableParams.reload();
}, function (response) {
alert('no stock item');
$scope.stockSupplier = null;
});
}
And this is my HTML code:
<div ng-controller="stockController">
<div>
<table ng-table="stockSupplierTableParams" class="table table-condensed table-bordered table-striped">
<tr ng-repeat="issue in $data">
<td data-title="'Supplier1'">
{{issue.SupplierName}}
</td>
<td data-title="'On Order'">
{{issue.OnOrder}}
</td>
</tr>
</table>
</div>
</div>
I'm at a complete loss - it may be something fundamental I'm missing, but it's driving me crazy, so any help great appreciated.
EDIT: Here's the code for the Web API Service call, in case that has any relevance. Also, I should point out that the HTML above is used in a Directive, if that makes any difference.
var myService = angular.module('myService', ['ngResource']);
myService.factory('stockSupplier', [
'$resource',
function ($resource) {
return $resource('http://:url/api/StockInfo?Sku=:sku&StockSupplier=true',
{
url: '#url',
sku: '#sku'
},
{
getAll: {
method: 'GET',
isArray: true
},
});
}
]);
I have a simple solution, you can re-render table when data loaded:
HTML
<div ng-controller="stockController">
<div data-ng-if="tableIsReady">
<table ng-table="stockSupplierTableParams" class="table table-condensed table-bordered table-striped">
<tr ng-repeat="issue in $data">
<td data-title="'Supplier1'">
{{issue.SupplierName}}
</td>
<td data-title="'On Order'">
{{issue.OnOrder}}
</td>
</tr>
</table>
</div>
<div data-ng-if="!tableIsReady">
// you can put loader here
</div>
</div>
JS
function getStockSupplier() {
console.log("getStockSupplier()");
$scope.stockSupplierTableParams = {};
$scope.tableIsReady = false; // here we hide table, and show loader
stockSupplier.getAll({ url: "localhost:52457", sku: $scope.model.sku })
.$promise.then(function (response) {
$scope.stockSupplier = response;
$scope.stockSupplierTableParams = new NgTableParams({
}, {
dataset: response
});
console.log("Got result");
console.log("Length: " + $scope.stockSupplierTableParams.settings().dataset.length);
$scope.stockSupplierTableParams.reload();
$scope.tableIsReady = true; // here we show table again
$scope.$apply() // start digest
}, function (response) {
alert('no stock item');
$scope.stockSupplier = null;
});
}
Otherwise, you code is fine, you just may be forget put $scope.$apply() after $scope.stockSupplierTableParams.reload();
I got it working! Though I'm not 100% sure how, so if any experienced Angular developers could explain it, I would be grateful. There are two parts to it.
Basically - instead of setting the dataset to the response object of the API call, I set it to the variable $scope.stockSuppler. Then, I explicitly empty this variable before the update - code below:
function getStockSupplier() {
console.log("getStockSupplier()");
$scope.tableIsReady = false;
$scope.stockSupplierTableParams = {};
$scope.stockSupplier = [];
stockSupplier.getAll({ url: "localhost:52457", sku: $scope.model.sku })
.$promise.then(function (response) {
$scope.stockSupplier = response;
$scope.stockSupplierTableParams = new NgTableParams({
}, {
dataset: $scope.stockSupplier
});
console.log("Got result");
console.log("Length: " + $scope.stockSupplierTableParams.settings().dataset.length);
$scope.stockSupplierTableParams.reload();
$scope.tableIsReady = true;
}, function (response) {
alert('no stock item');
$scope.stockSupplier = null;
});
}
Also, I removed the ngController="stockController" from the directive HTML code - the directive is called in a div which already defined the controller. Having the controller defined twice appears to have been confusing it as well. I should emphasis that only be making both of these changes, I got to to work. One or the other only, it still doesn't work correctly.
The table is now updating as expected with the other parts on the page.
I'm not sure why using the scope variable rather than the response from the API makes a difference, but it's done the trick.

With Iron Router, Find() and FindOne()

I loaded Iron:Router with my Meteor, and now I'm having difficulties loading both all of a collection, and one specific entry.
What I'm trying to do: In the Nav Bar, I want to list all of a user's previous entries. I got that working great. It lists each into a drop down, provides a proper link, and loads up only what the user has previous input. In the body it's suppose to load up specific information for each entry.
Whats not working: It's either not giving me a findOne() where the params._id equal the id in the route, or it's not loading anything. The location is correct. It's just not loading the info like it should.
UPDATE: I moved some things around and got the 'name' field to print out, still not able to get it to verify owner just yet. Console prints out: "Exception in template helper: ReferenceError: currentCharacter is not defined" Replacing with current code.
What am I doing wrong? Below is the code:
Route:
this.route('character/:_id', {
template: 'character',
subscriptions: function() {
return Meteor.subscribe("characterlist");
return Meteor.subscribe("characterlist", this.params._id);
},
data: function () {
var routeid = this.params._id;
return {
currentCharacter: CharList.findOne({_id: routeid}),
characterlist: CharList.find({'owner':Meteor.userId()})
};
}
});
Template Helper Class:
Template.character.helpers({
characterlist: function () {
return CharList.find({}, {sort: {createdAt: -1}});
},
currentCharacter: function () {
return CharList.findOne({'_id':Router.current().params._id});
},
isOwner: function () {
return currentCharacter.owner === Meteor.userId();
}
});
HTML:
<template name='character'>
<div class="container-body">
{{#if currentUser}}
<div class="well">
{{currentCharacter.name}}
{{#with currentCharacter}}
{{#if isOwner}}
<p>Character: {{name}}</p>
{{else}}
<p>You are not approved to make spends for this character.</p>
{{/if}}
{{/with}}
</div>
{{else}}
<h4>Please log in.</h4>
{{/if}}
</div>
</template>
Let me sigh metaphorically out loud online. Like usual when I ask for help I find what i did wrong.
It needs to get "this" object and pull the "owner" type out. Once it has "this.owner" it can verify the user is of course the correct owner. I was trying to be smart and didn't realize it was asking for the wrong information for four hours! Code below:
isOwner: function () {
return this.owner === Meteor.userId();
}

Publish/subscribe not working - how to debug?

I am trying to switch my app from autopublish to publish/subsribe. After trying to set publish/subscribe up or my first collection, none of my items from the collection are being displayed anymore, and I am at a loss at what might be going wrong.
Can someone spot a mistake in my code below or give any hints as to how to debug this type of problem?
client/routes.js
Router.route('/slug', {
name: 'route.name',
title: 'My Page',
template: 'TemplateName',
subscriptions: function() {
this.subscribe('myPublication');
},
action: function() {
this.render();
}
});
server/lib/collectionLib.js
...
Meteor.publish('myPublication', function() {
return MyCollection.find();
});
client/myCollection/view/mycollection-list.html
...
{{#each myCollection}}
...
{{/each}}
...
I was missing the exposure of the collection to the template. See answer provided here: https://forums.meteor.com/t/publish-subscribe-not-working-how-to-debug/13127/6?u=codejak
Oh I see now, unless you are doing MyCollection.find() in your template helper you need to do it in the route:
data: function () {
return MyCollection.find({}).fetch();
}

When the page loads scroll down (not so simple) : Meteor JS

I have chat app, and if you use Slack you know that when you enter to the room, you will automatically find yourself at the bottom of your chat room.
So I decided to do this and what I have
Template.room.onCreated(function() {
console.log($(document).height(),$(window).height());
$('html, body').scrollTop($(document).height()-$(window).height());
});
it output 437 , 437
BUT when I do this in console:
$('html, body').animate({ scrollTop: $(document).height()-$(window).height() + 64}, "fast");
it outputs 2000,437 , and that means that my messages is not loaded fully in my template. (If someone want more code, just ask.)
Any idea how to build this ?
EDIT:
This part of template.html
<div class="messages-wrap">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
{{#if haseMoreMessages}}
<div class="loadmore text-center" id="incLimit">Load More</div>
{{/if}}
{{#if Template.subscriptionsReady }}
{{#each messages}}
{{> message}}
{{/each}}
{{/if}}
</div>
</div>
</div>
</div>
And template.js (part of it)
Template.room.onRendered(function() {
Session.setDefault('messageLimit', 200);
});
Template.room.onCreated(function() {
var self = this;
self.autorun(function() {
if (self.subscriptionsReady()) {
Tracker.afterFlush(function () {
$('html, body').scrollTop($(document).height() - $(window).height());
});
}
});
});
Template.room.events({
'click #incLimit' : function(e,template){
Session.set('messageLimit',Session.get('messageLimit') + 100);
}
});
Template.room.helpers({
messages: function(){
return Messages.find({},{sort:{createdAt:1}});
},
haseMoreMessages:function(){
if (Session.get('messageLimit') > Messages.find().count()) return false;
return true;
},
});
This is one very frustrating aspect of Blaze. Try this, though:
Template.room.onRendered(function () {
var template = this;
this.autorun(function () {
if (template.subscriptionsReady()) {
Tracker.afterFlush(function () {
$('html, body').scrollTop($(document).height() - $(window).height());
});
}
});
});
This waits till all the template subscriptions are ready first, and then waits till any computations are fully complete (Tracker.afterFlush), and then executes the scroll. Tracker.afterFlush is usually necessary if your template has {{#if}} blocks that depend on other things before they get evaluated and rendered.
UPDATE:
Without seeing all your code and knowing why or when you want to scroll to the top, it's hard to say what you're aiming for. But see the Meteorpad link below for a working version of what you were trying to do with the message limits (I'm only incrementing the limit by 1 since there are 3 messages).
Meteorpad Link
A few things you should note:
Set default variables and subscribe to things in Template.x.onCreated, not Template.x.onRendered.
You forgot to actually subscribe to your collection.
Limit messages on the server side, in the Meteor.publish callback.
Different Approach via tracking of Template helpers:
I took advantage of the Template helper, as it already tracks reactively all changes (and new) messages. Hence, there you can place your scroll-down command.
Template.room.helpers({
messages: function(){
//put your scroll-up command here
//as this helper is keeping track on changes already
$('html, body').scrollTop($(document).height() - $(window).height());
return Messages.find({},{sort:{createdAt:1}});
},
haseMoreMessages:function(){
if (Session.get('messageLimit') > Messages.find().count()) return false;
return true;
},
});
Hence, you save the resources to have an additional tracker observing the collection (you would be double-tracking the same collection).

in Meteor, how do i update a property on only one instance of a template?

If I have an {{# each}} binding in Meteor, and I want to update a property on only one instance of the template inside the #each. How would I do that? I've tried setting a value on the "template" object inside the events map, but that doesn't seem to be reactive. I've also tried binding to a Session property, but that will cause every instance to update instead of just the one I want...
for example:
{{#each dates}}
{{> dateTemplate}}
{{/each}}
<template name="dateTemplate">
{{date}}
<span style="color: red;">{{errorMsg}}</span> <--- how do i update errorMsg?
</template>
Template.dateTemplate.events({
'click': function(event, template) {
template.errorMsg = 'not valid'; <--- this doesn't do anything
}
});
EDIT TO ADDRESS ANSWER BELOW:
Template.dateTemplate.events({
'click': function(event, template) {
template.errorMsg = function() { return 'not valid';} <--- this also doesn't do anything
}
});
You don't have to use handlebars for this, because its not something that needs reactivity to pass the message through, reactive variables work best with db data, or data that would be updated by another client over the air.
You could use JQuery (included by default) to update it, it can also get a bit fancier:
<template name="dateTemplate">
{{date}}
<span style="color: red;display: none" class="errorMessage"></span>
</template>
Template.dateTemplate.events({
'click': function(event, template) {
$(template.find('.errorMessage')).html('Your Error Message').slideDown();
}
});
Ive edited it so the error is hidden by default, and slides down with an animation
I'm experimenting handling this by passing a different reactive object to each instance of the template. Then the template can bind to the reactive object (which is unique per instance) and we don't have any extra boilerplate.
It ends up looking like this:
Initial render:
Template.firstTemplateWithPoll(ContextProvider.getContext())
Template.secondTemplateWithPoll(ContextProvider.getContext())
// (I actually pass getContext an identifier so I always get the same context for the same template)
JS:
Template.poll.events = {
'click .yes' : function() {
this.reactive.set('selection', 'yes');
},
'click .no' : function() {
this.reactive.set('selection', 'no');
}
};
Template.poll.selection = function(arg) {
return this.reactive.get('selection');
}
Template:
<template name="poll">
<blockquote>
<p>
Your selection on this poll is {{selection}}
</p>
</blockquote>
<button class='yes'>YES</button>
<button class='no'>NO</button>
</template>
template.errorMsg should be a function that returns your error.
Template.dateTemplate.events({
'click': function(event, template) {
template.errorMsg = function() { return 'not valid'; };
}
});

Resources