I combined two of my publications into a single one, as they were very similar. Both of them returned a set of cursor. I've rewritten them in low-level API, in order to make some control on data removing.
My problem is that, for a reason I totally ignore, the subscription onReady callback is never triggered. I've got other low-level pubsub in my app which publish data in a similar way and that works perfectly.
On the server, all my logs are correctly displayed, and no error is shown. The publication is runned correctly and the data are effectively sent to the client.
On the client, none of the onReady or onError callback is triggered.
Publication :
Meteor.publish("getTalkthread", function ( thread_id ) {
unblock(this);
console.log("start");
let uids = tags = corrections = [],
self = this;
let posts_cursor = Modules.both.queryGet({
type : 'posts',
method : 'find',
query : { $or: [
{ _id: thread_id },
{ parent: thread_id }
]
},
projection : { sort : { _id: 1 },
limit : 50
}
});
let posts_array = posts_cursor.fetch(),
posts_ids = posts_array.map( e => ( e._id ) );
//console.log("fetched posts", posts_array);
let corrs_cursor = Modules.both.queryGet({
type : 'corrections',
method : 'find',
query : { talkId: { $in: posts_ids } },
projection : { sort : { _id: 1 },
limit : 50
}
});
let corrs_array = corrs_cursor.fetch();
//console.log("fetched corrs", corrs_array);
let posts_authors = posts_array.map( e => ( e.owner ) ),
corrs_authors = corrs_array.map( e => ( e.owner ) );
let users_ids = _.union( posts_authors, corrs_authors );
let users_cursor = Modules.both.queryGet({
type : 'users',
method : 'find',
query : { _id: { $in: users_ids } },
projection : { sort : { date: -1 },
limit : 100,
fields : USER_PROFILE_FIELDS_
}
});
//console.log("fetched users", users_cursor.fetch());
let observers = {};
observers.posts = posts_cursor.observeChanges({
added : ( id, fields ) => { console.log("post added " + id); self.added ("posts", id, fields); },
changed : ( id, fields ) => { console.log("post changed " + id); self.changed("posts", id, fields); },
removed : ( id ) => {
console.log("test");
if ( id != thread_id ) {
console.log(true);
self.removed("posts removed " + id);
}
}
});
observers.users = users_cursor.observeChanges({
added : ( id, fields ) => { console.log("user added " + id); self.added ("users", id, fields); },
changed : ( id, fields ) => { console.log("user changed " + id); },
removed : ( id ) => { console.log("user removed " + id); }
});
observers.corrs = corrs_cursor.observeChanges({
added : ( id, fields ) => { console.log("corr added " + id); self.added ("corrections", id, fields); },
changed : ( id, fields ) => { console.log("corr changed " + id); self.changed("corrections", id, fields); },
removed : ( id ) => { console.log("corr removed " + id); self.removed("corrections", id); }
});
console.log("observers created");
this.ready();
self.onStop( () => {
observers.posts.stop();
observers.users.stop();
observers.corrs.stop();
});
console.log("end");
});
Subscription :
console.log("sub");
self.subscribe("getTalkthread", id, self.rerun.get()), {
onReady: function () {
console.log("READY");
if ( !Posts.findOne({ _id: id } )) {
$("#404NotFound").show();
$("#isoContent").hide();
} else if ( Posts.findOne( id ) ) {
$("#isoContent").show();
}
$("#spin").hide();
},
onError: function () {
console.log("ERROR");
$("#spin").hide();
$("#404NotFound").show();
$("#isoContent").hide();
}
};
The client page is correctly rendered, but the DOM manipulation are not triggered, forbidding the display of the content. Does anyone see what I'm doing wrong here ?
Thanks you
EDIT:
I tried to return an array of the three cursors, and, for whatever reason, the problem is the same. However, I don't see why.
Though I don't understand why, using Arunoda's SubsManager instead of the native Meteor.subscribe resolved the problem.
Related
This is my Indexing.vue component.
<div>
<div v-for="data in indexingList" :key="data.indexing_id">
<p>{{ data.indexing_name }}</p>
<p>{{ data.indexing_url }}</p>
</div>
<base-button type="primary" size="sm" #click="deleteIndexing(data.indexing_id)">Delete
</base-button>
</div>
export default {
data() {
return {
indexingList: [],
}
},
methods: {
getIndexingList: function() {
this.api.getDataModule("indexing/" + store.state.journalId, "journals/v1/")
.then((res) => {
console.log(res.data);
this.indexingList = res.data.indexingList;
},
(err) => {
console.log(err);
}
);
},
deleteIndexing: function(dVal) {
let sVal = {};
sVal.indexing_id = dVal;
this.api.putData("indexing/" + store.state.journalId + "/deleteindexing", sVal)
.then((res) => {
console.log(res.data);
},
(error) => {
console.log(error);
}
);
},
},
mounted: function() {
this.getIndexingList();
},
}
I'm getting data from the server using getIndexingList function in the form of the API get method. And removing the data once the user clicks the delete button by using the deleteIndexing function in the form of the put API method.
We wrote APIs in a separate file. Here in indexing.vue we are just passing the APIs.
Now I want to fix the delete(remove) function. The data was removed from the database once the user clicks the delete button but it is not removed from the webpage. Every time I need to refresh the page to see the changes.
You can use Vue.delete (this.$delete) which also ensure that the deletion triggers the view updates.
Try this-
deleteIndexing: function(dVal) {
let sVal = {};
sVal.indexing_id = dVal;
this.api
.putData("indexing/" + store.state.journalId + "/deleteindexing", sVal)
.then(
(res) => {
// find the item from indexingList and remove it
// Also make sure your response has the id of the deleted item
let index = this.indexingList.findIndex(item => item.id == res.id);
if (index != -1) {
this.$delete(this.indexingList, index);
}
},
(error) => {
console.log(error);
},
);
},
I have a custom component which receives a list of filters in order to display just the doctors that the user has selected:
<DoctorsSidebarFilter #update-view='showFilteredDoctors'></DoctorsSidebarFilter>
Next, in my main component, I'm using this to display the doctors:
<v-flex
v-for="doctor in allDoctors"
:key="doctor.first_name"
xs12
sm6
md4
>
And here's my data:
export default {
data: () => ({
allDoctors:[],
}),
methods: {
fetchDoctors(){
//Retrieve doctors
this.$store.dispatch(RETRIEVE_DOCTORS)
.then(
response => {
this.allDoctors = response;
}
)//TODO-me: Handle the error properly!
.catch(error => {
console.log(error);
});
},
showFilteredDoctors(filters){
let result = [];
this.fetchDoctors();
console.log('1:' + " " + JSON.stringify(this.allDoctors));
if (filters.length > 0) { // If Array is not empty then apply the filters
console.log('2');
this.allDoctors.forEach(function(e) {
if(filters.some(s => s.specialty === e.specialty || s.city === e.city)) {
result.push(e);
}
});
console.log('3:' + " " + JSON.stringify(result));
this.allDoctors = [...result];
console.log('4:' + " " + JSON.stringify(this.allDoctors));
}
}
},
mounted() {
this.fetchDoctors();
}
}
The problem is that eventhough my filtering works correctly and I can see from console.log('4:' + " " + JSON.stringify(this.allDoctors)); that this.allDoctors contains the new, filtered list; this is never displayed on screen.
Instead I see the default list of doctors that I've fetched from my API. Using vue devtools I can see that the this.allDoctors is momentarily updated with the correct values but then it goes back to the default ones.
As #user1521685 has already explained, the call to fetchDoctors is asynchronous so it'll complete after you've performed the filtering.
Typically you'd do something like this using a computed property instead and only make the server call once.
export default {
data: () => ({
allDoctors: [],
filters: []
}),
computed: {
filteredDoctors() {
const allDoctors = this.allDoctors;
const filters = this.filters;
if (filters.length === 0) {
return allDoctors;
}
return allDoctors.filter(doctor => {
return filters.some(filter => filter.specialty === doctor.specialty || filter.city === doctor.city);
});
}
},
methods: {
fetchDoctors(){
//Retrieve doctors
this.$store.dispatch(RETRIEVE_DOCTORS)
.then(
response => {
this.allDoctors = response;
}
)//TODO-me: Handle the error properly!
.catch(error => {
console.log(error);
});
},
showFilteredDoctors(filters){
this.filters = filters;
}
},
mounted() {
this.fetchDoctors();
}
}
In your template you'd then use:
v-for="doctor in filteredDoctors"
fetchDoctors is async, so in showFilteredDoctors you fetch the doctors, then set the filtered array and then the thenable in fetchDoctors kicks in and overrides the doctors again: this.allDoctors = response.
You'd have to return the Promise in fetchDoctors and use it in showFilteredDoctors like so:
this.fetchDoctors().then(() => /* do the filtering */)
EDIT: Return the Promise like this:
return this.$store.dispatch(RETRIEVE_DOCTORS).then().catch()
Im trying to use .find({}) with mongogb and unfortunately its not giving me the response I was expecting, I'm unsure how to check if the document exists or not? What I'm trying to do is say:
If a document does exist then do something.. IE send a response back
but If a document doesn't exist then create the document,
unfortunately I know that a document doesnt exist yet it must be picking up the wrong thing with 'if (docs)' but then when I change it to something else then it always creates records!?
The code:
addRefund : (refundCalc, callback) => {
order_number = refundCalc.refundDetails.customer_details.order.order_number;
dbconnect.createConnection()
refund.find({order_number: order_number}, (err, docs) => {
if (docs) {
console.log('docss!!!!!!!!!!!!!' + JSON.stringify(docs));
console.log('calling within error!!!!!!')
let notStored = {"refundDocStored" : "False"}
callback(notStored)
dbconnect.closeConnection();
}
else {
refund.create(refundCalc).then((refunddoc) => {
let filestored = {"refundDocStored" : "True"}
dbconnect.closeConnection();
callback(filestored)
}).catch((err)=> {
console.log(err);
dbconnect.closeConnection();
})
}
})
},
the schema:
const refundSchema = new Schema({
domain : { type: String},
refundDetails : {
customer_details : [],
refund : {
shipping : {
amount : { type: Number},
tax : {type : Number},
maximum_refundable : {type : Number}
},
refund_line_items: [],
transactions: []
}
}
});
The orders are stored within the refundDetails like this:
"refundDetails":{"customer_details":{"order":{"order_number":1021
It simply doesnt seem to work for me! if a document exists i cant seem to actually prove that it does?
Any help would be great, thanks!
You are using the wrong search query. You are searching for order_number which is a property of an object inside another object. You have to reference the order_number full path in your query i.e {"refundCalc.refundDetails.customer_details.order.order_number" : order_number}
addRefund : (refundCalc, callback) => {
order_number = refundCalc.refundDetails.customer_details.order.order_number;
dbconnect.createConnection()
refund.find({"refundCalc.refundDetails.customer_details.order.order_number": order_number}, (err, docs) => {
if (docs) {
console.log('docss!!!!!!!!!!!!!' + JSON.stringify(docs));
console.log('calling within error!!!!!!')
let notStored = {"refundDocStored" : "False"}
callback(notStored)
dbconnect.closeConnection();
}
else {
refund.create(refundCalc).then((refunddoc) => {
let filestored = {"refundDocStored" : "True"}
dbconnect.closeConnection();
callback(filestored)
}).catch((err)=> {
console.log(err);
dbconnect.closeConnection();
})
}
})
},
I have a onWrite cloud function set up to listen for when a user updates something. I'm trying to delete the oldest child if there are more than 3, this is there I'm at:
exports.removeOld = functions.database.ref('/users/{uid}/media').onWrite(event => {
const uid = event.params.uid
if(event.data.numChildren() > 3) {
//Remove Oldest child...
}
})
Each of these children has a "timestamp" key.
{
"users" : {
"jKAWX7v9dSOsJtatyHHXPQ3MO193" : {
"media" : {
"-Kq2_NvqCXCg_ogVRvA" : {
"date" : 1.501151203274347E9,
"title" : "Something..."
},
"-Kq2_V3t_kws3vlAt6B" : {
"date" : 1.501151232526373E9,
"title" : "Hello World.."
}
"-Kq2_V3t_kws3B6B" : {
"date" : 1.501151232526373E9,
"title" : "Hello World.."
}
}
}
}
}
So in the above example, when the text value is added to "media", the oldest would be delete.
This sample should help you.
You need something like that :
const MAX_LOG_COUNT = 3;
exports.removeOld = functions.database.ref('/users/{uid}/media/{mediaId}').onCreate(event => {
const parentRef = event.data.ref.parent;
return parentRef.once('value').then(snapshot => {
if (snapshot.numChildren() >= MAX_LOG_COUNT) {
let childCount = 0;
const updates = {};
snapshot.forEach(function(child) {
if (++childCount <= snapshot.numChildren() - MAX_LOG_COUNT) {
updates[child.key] = null;
}
});
// Update the parent. This effectively removes the extra children.
return parentRef.update(updates);
}
});
});
You can find all Cloud Functions for Firebase samples here.
I am working on like and unlike section of an image. When a user likes an image, it pushes the userID to an array in mongodb. When a user unlikes an image, it removes the userID from the array. I am trying to do it using $addToSet and $pull.
Question: How can I do it in a single block instead of writing two separate bolcks for these two? Currently I am using a variable opr but it is not working. How can I make it work?
if(likeAction == "like"){
var opr = '$addToSet'
}
else if(likeAction == "unlike"){
var opr = '$pull'
}
Like.update(
{ imageID: imageID },
{ opr : { userIDs: userID } },
function(err){
if(!err){
Like.findOne({imageID : imageID}, function(err,like){
image.likeCount = like.userIDs.length
image.save(function(err){
if(!err){
return res.send({
status: {
error: 0,
message: "Successful"
}
})
}
})
})
}
}
);
I think this should work to combine both queries and solve the opr issue:
var update = {};
if (likeAction === 'like'){
update.$addToSet = { userIDs: userID };
update.$inc = { likeCount : 1 };
} else if (likeAction === 'unlike'){
update.$pull = { userIDs: userID };
update.$inc = { likeCount : -1 };
}
Like.update({ imageID: imageID }, update, ...);
You can do something like that :
var opr, updateCommande = {};
if(likeAction == "like"){
opr = '$addToSet'
}
else if(likeAction == "unlike"){
opr = '$pull'
}
updateCommande[opr]= { userIDs: userID }
Like.update(
{ imageID: imageID },
updateCommande,
function(err){
if(!err){
Like.findOne({imageID : imageID}, function(err,like){
image.likeCount = like.userIDs.length
image.save(function(err){
if(!err){
return res.send({
status: {
error: 0,
message: "Successful"
}
})
}
})
})
}
}
);