Flow Router client side publishing for single item - meteor

I just made the change to Flow Router and trying to understand it. I'm creating a portfolio. I have a list of portfolio items. You can click a single item and it will take you to the specific page. I have the path helper set up, but the client side publishing seems to be the issue.
This is the helper I have:
Template.Single.helpers({
portfolio: () => {
var itemId = FlowRouter.getParam('id');
return Portfolio.findOne({_id: id});
}
});
and the route:
FlowRouter.route('/portfolio/:_id', {
action() {
BlazeLayout.render('MasterLayout', {content: 'Single'});
}
});

Related

Ionic and SQLite wait until database info is loaded

I have a project on Ionic where I need to render some information of the database in the home page, something like a TODO program.
I already have some information on the database and I'm trying to render the list of items but I have the next problem:
First the home page is loaded without any result
Then the data from the database is loaded and printed on the screen
The problem is I want the view to wait until the database info is loaded until showing anything, I'm wondering if I can use some kind of loading icon.
I've followed the answer here: Open database before main controller is called in Ionic and SQlite
I have the database initialization working but as I've said, the data is loaded after the view is rendered.
I've tried using $ionicLoading but I didn't get any good result
This is my view:
.controller('homeCtrl', function ($scope, $state, $cordovaSQLite, DB) {
$scope.$on('$ionicView.enter', function() {
tasks = []
var query = "SELECT * FROM task;";
$cordovaSQLite.execute(DB.db, query, []).then(function(results) {
if(results.rows.length > 0) {
for (i=0; i<results.rows.length; i++){
console.log("SELECTED -> " + results.rows.item(0).title);
$scope.tasks.push(results.rows.item(i))
}
} else {
console.log("No results found");
}
}, function (err) {
$scope.tasks = [];
console.error(err);
});
$scope.tasks = tasks;
});
})
This is a video example of the issue I'm having right now:
https://youtu.be/H2fUYQuV3xg
Finally I found a solution following the advice of using resolve in my routes.
.state('home', {
url: '/',
templateUrl: 'templates/home.html',
controller: 'homeCtrl',
resolve: {
tasks: function(DB) {
return DB.getTasks();
});
}
}
})
I have a factory called DB where I have some functions to retrieve data from the database. On this example I load the tasks before entering on the URL using DB.getTasks()
To load the variable tasks resolved on the route I have to add the variable name on the function like this:
app.controller('homeCtrl', function (tasks) {
$scope.tasks = tasks;
})

Meteor subscription doesn't refresh despite WaitOn

I'm using Iron Router. I have a RouterController that looks something like this:
var loggedInUserController = RouteController.extend({
layoutTemplate: "GenericLayout",
waitOn: function () {
return Meteor.subscribe("TheDataINeed");
}
});
And I have a route defined which uses this controller to wait for the 'TheDataINeed':
Router.route("/myapp", {
name: "Landing",
controller: loggedInUserController,
data: function () {
if(this.ready()){
return {content: "page-landing"};
}
}
});
Now, the problem is the data I am subscribed to is conditional: meaning, depending on the user's role, I publish different data, like so:
if (!Roles.userIsInRole(this.userId, 'subscribed') ) {
return [
myData.getElements({}, { fields: { _id: 1, title: 1}, limit: 5 })
];
} else {
return [
myData.getElements({}, { fields: { _id: 1, title: 1} })
];
}
When the user's role is not 'subscribed', I limit the published data to 5 elements.
The problem is publishing is not reactive, so when the user changes his role for the first time to 'subscribed' and I navigate to my route ("/myapp"), the user still sees the limited number of elements instead of all of them.
Is there a way to manually re-trigger the subscription when I am loading this route? If possible, I'd like to do this without adding new packages to my app.
Not sure about that approach but can you try to set session value in route instead of subscription code. Then in a file on client side where your subscriptions are you can wrap Meteor.subscribe("TheDataINeed") in Tracker.autorun and have a session as a subscription parameter. Every time that session value is changed autorun will rerun subscription and it will return you data based on a new value.

Correct way to manage routes after sign in for different roles

I have 2 roles, admins and users. I have a home route '/' that is just a sign in page. When admin users sign in they must go to one route ('adminPortal') and when user users log in they must go to the 'userPortal' route. On signing out both roles should route back to '/'.
Before I had an admin role, I was routing on sign in like so:
Router.onBeforeAction(function() {
this.render('loading');
if (! Meteor.userId()) {
this.render('Home');
} else {
this.next();
}
});
which worked fine (actually it was breaking my waitOn: render loading template stuff which I just discovered but more on that later). I then added roles like this (from a Stack Overflow answer I can't find right now):
server/
insertUsers=function(){
var adminId=Accounts.createUser({
username:"admin",
password:"password"
});
Roles.addUsersToRoles(adminId,"admin");
var userIdd = Accounts.createUser({
username:"user",
password:"password"
});
Roles.addUsersToRoles(userIdd,"user");
};
and
Meteor.startup(function () {
// always start from scratch (you will want to comment this line at some point !)
Meteor.users.remove({});
if(Meteor.users.find().count()===0){
insertUsers();
}
})
and
Meteor.publish("user", function () {
if (this.userId) {
return Meteor.users.find({_id: this.userId},
{fields: {'roles': 1}});
} else {
this.ready();
}
});
And I tried to route to the user/admin portals like this:
router.js
Router.route('/', {
before: function() {
if (! Meteor.userId()) { // I acutally added this check that the user is not logged in after the infinite loading problem but I thought the question was getting too long so I just left it in rather
this.render('Home')
} else {
if (Roles.userIsInRole(Meteor.user(), 'admin')) {
Router.go('/admin')
} else {
Router.go('/user')
}
}
},
waitOn: function () {
return Meteor.subscribe('user');
}
});
Now this very almost works! If I log is as either user I go to the right portal. However, when I sign out, my onBeforeAction (i.e. first code block in this question) only renders the Home template and does not actually change the URL to '/' (i.e. the URL remains either '/user' or '/admin'). Now when I try log in a second time, it will always take me to the route that I was taken to on the first log in unless I manually change the browser URL to '/'. So I thought I'd just replace the this.render('Home') with a Router.go('/'); but that seems to have created some sort of infinite loop where the Home template never renders (it did incidentally now for the first time correctly render my loading template though).
So thanks for reading all that! What's the right way to do this?
Try adding Router.go('/'); in your logout button event, along with Meteor.logout();
Example:
Template.loginButtons.events({
'click #login-buttons-logout' : function (event, template) {
Meteor.logout(function(err) {
Router.go('/');
});
}
});
I had the same issue as you, and that was the simplest way I've found to return to home page after logout.

Trigger global meteor subscription without iron router

This should be an easy one.
Here's what I would like:
Page 1: Subscribe to 'List' collection
Page 2: display items from 'List' collection (user then selects an item from the list, returning the _id)
Page 3: subscribe to 'Data' collection using _id <- STAY SUBSCRIBED FROM NOW ON, ON EVERY PAGE, WITHOUT THE USE OF IRON ROUTER
I've messed around with Template.subscribe, Template.onCreated etc. but it's Friday and I'm sure this is elementary stuff so I thought I'd ask.
Cheers!
Here's how I did it:
User logs in -> render userLayout -> set currentPage to
loading
If user has already selected a project, set currentPage to userSplash (else set currentPage to userProjects where they will select a project and then go to userSplash)
Template.userLayout.created = function() {
Session.set('currentPage', 'loading');
return Meteor.subscribe('projects', user.profile.company_id, function() {
if (currentProject_id !== void 0) {
return Session.set('currentPage', 'userSplash');
} else {
return Session.set('currentPage', 'userProjects');
}
});
};
Render userSplash -> subscribe to required project data -> load dashboard when finished
Template.userSplash.rendered = function() {
return Meteor.subscribe('tasks', currentProject_id, function() {
return Session.set('currentPage', 'userDashboard');
});
};
(this answer provided some useful info)

Adding an array object to Users collection in Meteor.js

I'm very new to Meteor.js and I'm finding the documentation a bit hard to understand.
I'm starting with a very simple app where Users will simply be allowed to add existing Games to their profile by clicking a button. The Games are stored in another Meteor Collection.
In rails I would just create a has_and_belongs_to_many relationship but that isn't how Meteor works. I thought the best way would be to add an empty array when the user's account is created - then, when they click the "add game" button it would pass the game's title into the users array.
I have this in my /server/users.js file:
Accounts.onCreateUser(function(options, user){
user.games = [];
return user;
});
Meteor.methods({
addGame: function(title) {
Meteor.users.update(Meteor.userId(), { $addToSet: { games: title}});
}
});
And I'm making a call to the addGame method in my /client/views/games/games_list.js file as such:
Template.gamesList.events({
'click .add-to-chest-btn': function(e){
var title = $(e.target).attr('name');
e.preventDefault();
Meteor.call('addGame', title, function(title){ console.log(title)});
}
});
Am I on the right track or is there a better way to do this?
You're on the right track, but do declare an array instead of an object:
Accounts.onCreateUser(function(options, user){
user.games = [];
return user;
});
Push the value directly instead of an object, and use $addToSet to avoid duplicates in case you push the same gameId multiple times:
Meteor.methods({
addGame: function(gameId) {
Meteor.users.update(Meteor.userId(), { $addToSet: { games: gameId }});
}
});

Resources