I am working on meteor app. I want to reload the content of the client when the database variable has changed. I am using pub-sub. I want to load content if the status variable has changed.
Meteor.publish('activities', function(loggedInUserToken) {
var authObj = AuthenticationToken.findOne({ authToken: loggedInUserToken });
if (authObj) {
var userObj = Users.findOne({ _id: authObj.user_id });
var activities = Activities.find({}, { sort: { createdAt: -1 } });
return activities;
}
return this.ready();
});
Template.masterSku.onCreated(function() {
var instance = this;
instance.autorun(function() {
var loggedInUserToken = "xxxxxxxxxxxxxxxx"
statusSuscription = instance.subscribe("activities", loggedInUserToken);
var activitiesObj = Activities.findOne({}, { sort: { createdAt: -1 } })
if (activitiesObj && activitiesObj.status == "true") {
console.log("Status Changed load Content accordingly")
}
})
The autorun will not recompute until a reactive data has changed AND this change is requested within the computation. While a subscription is a reactive source, you also need to check for it's readyness and thus cause a new computation cycle:
instance.autorun(function() {
var loggedInUserToken = "xxxxxxxxxxxxxxxx"
var statusSuscription = instance.subscribe("activities", loggedInUserToken);
if (statusSuscription.ready()) { // causes a new compuation by the Tracker
var activitiesObj = Activities.findOne({}, { sort: { createdAt: -1 } })
if (activitiesObj && activitiesObj.status == "true") {
console.log("Status Changed load Content accordingly")
// here you can insert data into a ReactiveVar or ReactiveDict and use
// result in a Template helper, which itself will cause your Template to re-render
}
}
}
You can use Tracker in onCreated function OR you can try fill data through helpers.
import { Tracker } from 'meteor/tracker';
Tracker.autorun(() => {
var loggedInUserToken = "xxxxxxxxxxxxxxxx"
statusSuscription = instance.subscribe("activities", loggedInUserToken);
var activitiesObj = Activities.findOne({}, { sort: { createdAt: -1 } })
if (activitiesObj && activitiesObj.status == "true") {
console.log("Status Changed load Content accordingly")
}
});
Related
I'm creating an electron app using pouchDB and I want the app to be able to diferents customers and sync the data between them. As an example I'm making the tutorial: https://github.com/nolanlawson/pouchdb-getting-started-todo, I adapt the code to electron and I created a noSQL database at cloudant.
At the moment I can save data but I cannot sync with my remote db that is in cloudant. Here is the endpoint I'm using to sync data between both database.
Here is the error that I'm getting.
Here is the code of my script.js
(function() {
'use strict';
var $ = document.querySelector.bind(document);
var ENTER_KEY = 13;
var newTodoDom = document.getElementById('new_todo');
var syncDom = document.getElementById('sync-wrapper');
// EDITING STARTS HERE (you dont need to edit anything above this line)
var NodePouchDB = require('pouchdb');
var db = new NodePouchDB('todos');
var couchdb = require('felix-couchdb')
var remoteCouch = couchdb.createClient(5984, 'https://ac725f4e-29ec-4614-8e96-02ebc74a529b-bluemix.cloudant.com/')
db.info(function(err, info) {
console.log("is working", info)
db.changes({
since: info.update_seq,
live: true
}).on('change', showTodos);
});
// We have to create a new todo document and enter it in the database
function addTodo(text) {
var todo = {
_id: new Date().toISOString(),
title: text,
completed: false
};
db.put(todo).then(function (result) {
console.log("everything is A-OK");
console.log(result);
}).catch(function (err) {
console.log('everything is terrible');
console.log(err);
});
}
// Show the current list of todos by reading them from the database
function showTodos() {
db.allDocs({include_docs: true, descending: true}).then(function(doc) {
redrawTodosUI(doc.rows);
}).catch(function (err) {
console.log(err);
});
}
function checkboxChanged(todo, event) {
todo.completed = event.target.checked;
console.log(todo);
db.put(todo);
}
// User pressed the delete button for a todo, delete it
function deleteButtonPressed(todo) {
db.remove(todo);
}
// The input box when editing a todo has blurred, we should save
// the new title or delete the todo if the title is empty
function todoBlurred(todo, event) {
var trimmedText = event.target.value.trim();
if (!trimmedText) {
db.remove(todo);
} else {
todo.title = trimmedText;
db.put(todo);
}
}
// Initialise a sync with the remote server
function sync() {
syncDom.setAttribute('data-sync-state', 'syncing');
var opts = {live: true};
db.sync(remoteCouch, opts, syncError);
}
// EDITING STARTS HERE (you dont need to edit anything below this line)
// There was some form or error syncing
function syncError() {
syncDom.setAttribute('data-sync-state', 'error');
}
// User has double clicked a todo, display an input so they can edit the title
function todoDblClicked(todo) {
var div = document.getElementById('li_' + todo._id);
var inputEditTodo = document.getElementById('input_' + todo._id);
div.className = 'editing';
inputEditTodo.focus();
}
// If they press enter while editing an entry, blur it to trigger save
// (or delete)
function todoKeyPressed(todo, event) {
if (event.keyCode === ENTER_KEY) {
var inputEditTodo = document.getElementById('input_' + todo._id);
inputEditTodo.blur();
}
}
// Given an object representing a todo, this will create a list item
// to display it.
function createTodoListItem(todo) {
var checkbox = document.createElement('input');
checkbox.className = 'toggle';
checkbox.type = 'checkbox';
checkbox.addEventListener('change', checkboxChanged.bind(this, todo));
var label = document.createElement('label');
label.appendChild( document.createTextNode(todo.title));
label.addEventListener('dblclick', todoDblClicked.bind(this, todo));
var deleteLink = document.createElement('button');
deleteLink.className = 'destroy';
deleteLink.addEventListener( 'click', deleteButtonPressed.bind(this, todo));
var divDisplay = document.createElement('div');
divDisplay.className = 'view';
divDisplay.appendChild(checkbox);
divDisplay.appendChild(label);
divDisplay.appendChild(deleteLink);
var inputEditTodo = document.createElement('input');
inputEditTodo.id = 'input_' + todo._id;
inputEditTodo.className = 'edit';
inputEditTodo.value = todo.title;
inputEditTodo.addEventListener('keypress', todoKeyPressed.bind(this, todo));
inputEditTodo.addEventListener('blur', todoBlurred.bind(this, todo));
var li = document.createElement('li');
li.id = 'li_' + todo._id;
li.appendChild(divDisplay);
li.appendChild(inputEditTodo);
if (todo.completed) {
li.className += 'complete';
checkbox.checked = true;
}
return li;
}
function redrawTodosUI(todos) {
var ul = document.getElementById('todo-list');
ul.innerHTML = '';
todos.forEach(function(todo) {
ul.appendChild(createTodoListItem(todo.doc));
});
}
function newTodoKeyPressHandler( event ) {
if (event.keyCode === ENTER_KEY) {
addTodo(newTodoDom.value);
newTodoDom.value = '';
}
}
function addEventListeners() {
newTodoDom.addEventListener('keypress', newTodoKeyPressHandler, false);
}
addEventListeners();
showTodos();
if (remoteCouch) {
sync();
}
})();
To get to where the problem sits, can you verify that you can speak to the Cloudant database normally, that is using curl from the command-line? Using curl, fetch a document by its _id, perhaps a document you created manually using the Cloudant dashboard. That should shake out any problems with authentication: I note you're using IAM, which isn't always straight-forward -- and to my knowledge, not supported by PouchDB (or wasn't, last time I looked).
If that is the problem, create a new Cloudant instance with IAM+Legacy credentials.
I have a knockout script that have a defaut viewmodel with few fields values. When I have entered the data in the form and submit, it is not sending any updated value back. For example, the zipcode stays the value I defined on the defaul load. Here is the code
$(function () {
ViewModel.zipCode.subscribe(function (value) {
$.get("/api/abc/GetCounties?zipCode=" + value, null, ViewModel.county, 'json');
}.bind(this));
});
var ViewModel = function (data) {
var self = this;
self.zipCode = ko.observable(data.zipCode);
self.dateOfEvent = ko.observable(new Date());
//Enrollment reasons
self.enrollmentReasons = ko.observableArray(new Array());
$.get("/api/abc/reasons", null, self.enrollmentReasons, 'json');
//county from Zipcode subscribed
self.county = ko.observableArray(new Array());
$.get("/api/utilityapi/GetCounties?zipCode=" + data.zipCode, null, self.county, 'json');
self.selectedChoice = ko.observable();
self.applicants = ko.observableArray();
self.applicants.push(new getaquoteapplicant());
//IsValid zip subscribed
self.isValidZip = ko.observable(false);
//operations
self.addApplicant = function () { self.applicants.push(new getaquoteapplicant()) };
self.removeApplicant = function (getaquoteapplicant) { self.applicants.remove(getaquoteapplicant) };
self.save = function () {
$.ajax({
url: '/xyz/start',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(data),
success: function (result) {
window.location.replace('/getaquote/planoptions', result);
}
});
}
}
var getaquoteapplicant = function () {
var self = this;
self.coverageFor = [
{ name: "Self" },
{ name: "Spouse/Life Partner" },
{ name: "Son" }
];
self.dateofBirth = ko.observable();
self.tobaccoUser = [
{ name: "Yes", value: true },
{ name: "No", value: false }
];
};
var defaultmodel = {
dateOfEvent: new Date(),
selectedChoice: 4,
zipCode:55044
}
ViewModel = new ViewModel(defaultmodel);
ko.applyBindings(ViewModel);
`
The problem could be that you are running the knockout binding even before the page is loaded or that you trying to access the ViewModel before it is created in the code execution order. One last thing is that always better to create a new instance of the model rather than the assigning it to itself.
I have created them in the fiddle below. It seems to work
https://jsfiddle.net/ramkiFiddle/npsgq8uL/1/
$(document).ready(function() {
var viewModel = new ViewModel(defaultmodel);
ko.applyBindings(viewModel);
});
I noticed this strange behaviour, when for few users only on production, it inserts every item multiple times to collection on asynchronous Meteor call. I tried multiple things, but nothing worked. I can't test on localhost, bc it never happens to me on localhost or in production.
I spent the whole night solving this, but didn't find any solution. I suppose it's caused by new Date(), but I have to call it somewhere. The production server is in Amsterdam and it seems like it happens only for users located further outside of Europe.
I found this to be similar issue, but can't really wrap my head on how to implement - https://github.com/meteor/meteor/issues/4263
Slug is what same songs are supposed to have the same.
This is the workflow, user clicks on song that triggers addNewSong function:
addNewSong = function (track) {
Globals.current_track = track;
checkIfSongAlreadySaved();
}
I need to check if song is already in collection, if it's -> route to it, else create the new song and route to it.
checkIfSongAlreadySaved = function() {
loadPrimaryGlobalItems();
Meteor.call('checkIfSongAlreadySaved', Globals.current_song_item_slug, function(error, result) {
if( result.item ) {
Globals.current_song_item_id = result.item._id;
Globals.current_song_item_slug = result.item.slug;
routeToSongPage();
if (! (result.item.download && result.item.mp3) ) {
downloadSong();
}
}
else {
loadSecondaryGlobalItems();
var item = {
slug:Globals.current_song_item_slug,
duration:Globals.current_duration,
thumbnail:Globals.current_song_thumbnail,
title:Globals.current_cleaned_song,
album:Globals.current_track.album,
artist:Globals.current_track.artists[0],
track:Globals.current_track.name,
date:result.date,
}
Globals.current_song_item_id = Songs.insert(item);
routeToSongPage();
downloadSong();
recentSongItem(result.date);
}
});
}
Add recent song
recentSongItem = function (date) {
Recentsongs.insert({
slug:Globals.current_song_item_slug,
songId:Globals.current_song_item_id,
title:Globals.current_cleaned_song,
duration:Globals.current_duration,
date:date,
});
}
If creating new song,
downloadSong = function() {
Meteor.call('findSong', Globals.current_song, function(error, result) {
console.log(result);
if (result) {
Globals.current_song_mp3 = true;
updateSongItemDownload(result.itemDetails);
}
else {
alert('not found')
}
});
}
and update song, to add download and mp3 values.
updateSongItemDownload = function(link) {
Songs.update({
_id: Globals.current_song_item_id
},
{
$set: {
download: link,
mp3: Globals.current_song_mp3,
}
});
}
Server methods:
Meteor.methods({
checkIfSongAlreadySaved: function(slug) {
return {item: Songs.findOne({slug:slug}), date: new Date()};
},
findSong:function(song) {
ServerGlobals.current_song = song;
var result = searchSite();
return result;
},
});
EDIT:
This is my subscription, just in case it might be causing the problem:
Template.songPage.onCreated(function() {
Session.set('processing', true);
var self = this;
self.autorun(function() {
var id = Router.current().params.id;
self.subscribe('singleSong', id);
var item = Songs.findOne({_id: id});
if (item) {
if (item.download) {
createSong(item.download);
}
else if( item.download === false ) {
console.log('item not found');
}
Session.set('loader', false);
Session.set('processing', false);
}
});
});
Meteor.publish('singleSong', function(id) {
check(id, String);
return Songs.find({_id: id});
});
You can apply a unique index on the slug field to ensure the same slug can only exist once and the second operation to insert will fail and show up as an error in your callback which you can discard or alert user as you desire.
db.collection.createIndex( { slug: 1 }, { unique: true } )
You will need to clear or modify the slug name on the dups from the db before applying the index though
like i menthioned in the topic i'm looking for a plugin that save my marked text and could restore it after loose of focus. Like a JS Library called Rangy i used in the past. Is there such a plugin or does anyone has an idea how i could deal with this kind of problem?
Regardings Adrian
function gEBI(id) {
return document.getElementById(id);
}
var savedSel;
var savedSelActiveElement;
function saveSelection() {
if (savedSel) {
// rangy.removeMarkers(savedSel);
}
savedSel = rangy.saveSelection();
savedSelActiveElement = document.activeElement;
}
function restoreSelection() {
if (savedSel) {
rangy.restoreSelection(savedSel, true);
window.setTimeout(function() {
if (savedSelActiveElement && typeof savedSelActiveElement.focus != "undefined") {
savedSelActiveElement.focus();
}
}, 1);
}
}
$(document).ready(function()
{
try {
document.execCommand("MultipleSelection", null, true);
} catch(ex) {}
rangy.init();
// Enable buttons
var saveRestoreModule = rangy.modules.SaveRestore;
if (rangy.supported && saveRestoreModule && saveRestoreModule.supported) {
var saveButton = gEBI("saveButton");
//saveButton.disabled = false;
saveButton.ontouchstart = saveButton.onmousedown = function() {
saveSelection();
return false;
};
$('.EditorTab').mousedown(function(){
saveSelection();
return false;
});
});
The following solution would allow you to store and restore any number of ranges, but it assumes that you have not destroyed the nodes that the ranges are attached to when it comes time to restore those ranges.
var SelectionStore = (function() {
var savedRanges = {};
return {
store: function(saveIdentifier) {
var ranges = rangy.getSelection().getAllRanges();
savedRanges[saveIdentifier] = ranges;
return ranges;
},
restore: function(saveIdentifier) {
var i, selection;
selection = rangy.getSelection();
if (!savedRanges[saveIdentifier]) throw new Error('Invalid saved selection identifier used. Selection not found for ID: ' + saveIdentifier);
selection.removeAllRanges();
for (i in savedRanges[saveIdentifier]) selection.addRange(savedRanges[saveIdentifier][i]);
}
}
})();
Usage examples:
//store the current selection
SelectionStore.store('Tab1');
//restore a selection
SelectionStore.restore('Tab1');
I'm writing a batch process to read an RSS feed, and store the contents in MongoDB via Mongoose. I would run the script, and it would process the contents just fine... but the script wouldn't return to the console. My hypothesis was that my database connection was still open, and that's why I it wasn't returning.
I couldn't just track when my parsing was complete, because I still might have some mongoose save operations going on.
So, I wrote a function to track my open database connections, and the status of my RSS parsing. But my code ended up being heinously verbose. I'm wondering if there is a better model/pattern for doing something like this.
var FeedParser = require('feedparser')
, mongoose = require('mongoose');
var TEST_RSS_URL = "./test/data/20120303-seattle.rss";
var OPEN_DB_CONNECTIONS = 0;
var PARSING_DONE = false;
/*
* Keeps track of open database connections, and closes the connection when done
*/
function track_and_close_database(mode) {
switch(mode)
{
case 'open':
OPEN_DB_CONNECTIONS++;
break;
case 'close':
OPEN_DB_CONNECTIONS--;
if (0 == OPEN_DB_CONNECTIONS && PARSING_DONE) conn.close();
break;
case 'parseStart':
PARSING_DONE = false;
break;
case 'parseEnd':
PARSING_DONE = true;
if (0 == OPEN_DB_CONNECTIONS && PARSING_DONE) conn.close();
break;
}
}
function parse_stuff(stuff) {
// do some stuff
setTimeout(console.log("parsed some stuff",20));
}
function main() {
parser = new FeedParser();
parser.on('article', function(article) {
track_and_close_database('open');
// check to see if we already have this listing
stuff_model = conn.model('stuff');
stuff = stuff_model.findOne({'href': article.link}, function (error, doc) {
if (error) {
track_and_close_database('close');
return;
}
// this one doesn't exist yet, parse and save
if (null == doc) {
listing = parse_stuff(article);
// if listing is valid, save it!
if (null != listing) {
listing.save(function (error) { track_and_close_database('close') });
}
// parsing failed
else track_and_close_database('close');
}
// nothing to do, already in the database
else track_and_close_database('close');
});
});
// Completed parsing the RSS file
parser.on('end', function(article) {
track_and_close_database('parseEnd');
});
track_and_close_database('parseStart')
parser.parseFile(TEST_RSS_URL);
}
// run this thing
main();
I too ran into this issue. I would think the preferred way to handle this would be with events, however upon looking into the source code there didn't seem to be anything in there that kept some type of operation count. I ended up hooking it up to an the EventEmitter. What would be better is if Mongoose emitted events for before and after saves so I didn't have to plug this into all of my models.
Here is a sample of how I did it:
/* lib/render_log.js */
/* Model for managing RenderLog */
var common = require("./common");
common.initialize_locals(global);
var mongoose = require("mongoose"),
Schema = mongoose.Schema;
var RenderLogSchema = new Schema({
renderer: String,
template: String,
content: {}
});
RenderLogSchema.pre('save', function(next){
this.db.model('RenderLog').emit('open_db_op', this);
next();
});
RenderLogSchema.post('save', function(){
this.db.model('RenderLog').emit('close_db_op', this);
});
mongoose.connect('mongodb://localhost/time-jr-development');
var RenderLog = mongoose.model("RenderLog", RenderLogSchema);
exports = module.exports = RenderLog;
Followed by my test executable:
/* bin/test_mongoose.js */
var async = require('async');
var RenderLog = require("../lib/render_log");
var ConnectionManager = {
open_db_ops: 0,
new_db_op: function(){
this.open_db_ops ++;
},
close_db_op: function(){
this.open_db_ops --;
},
close: function(self){
if(!self)
self = this;
if(self.open_db_ops > 0){
console.log("Waiting...")
process.nextTick(async.apply(self.close, self));
}else{
RenderLog.db.close();
}
}
};
RenderLog.on("open_db_op", function(model){
ConnectionManager.new_db_op();
});
RenderLog.on("close_db_op", function(model){
ConnectionManager.close_db_op();
})
new RenderLog({renderer: "foo", template: "foo.html", content: {"bar": 1}}).save();
new RenderLog({renderer: "foo", template: "foo.html", content: {"bar": 2}}).save();
new RenderLog({renderer: "foo", template: "foo.html", content: {"bar": 3}}).save();
new RenderLog({renderer: "foo", template: "foo.html", content: {"bar": 4}}).save();
new RenderLog({renderer: "foo", template: "foo.html", content: {"bar": 5}}).save();
// You have to push this to the next tick otherwise it gets called before the save
// events have been emitted
async.nextTick(async.apply(ConnectionManager.close, ConnectionManager));