Meteor + MongoDB: How to get nested data? - javascript

I'm new to Meteor and trying to figure out this issue I have.
I'm trying to load data from the Lessons collection based on the route being passed. e.g if /courses/level1/lesson1/1a is passed then show data
Unfortunately this doesn't work.
Am I on the right path or is there a better way of doing this?
Collection
{
"_id": "YSgr3fvjpEBn7ncRa",
"courseId": "level1",
"lesson": [
{
"lessonId": "lesson1",
"freeLesson": true,
"title": "Lesson 1",
"eachLesson": [
{
"eachLessonId": "1a",
"title": "This is (masculine)",
"video": "839843"
},
{
"eachLessonId": "1b",
"title": "That is (masculine)",
"video": "839843"
},
{
"eachLessonId": "1c",
"title": "This is (feminine)",
"video": "839843"
},
{
"eachLessonId": "1d",
"title": "That is (feminine)",
"video": "839843"
},
{
"eachLessonId": "1e",
"title": "Getting to know you",
"video": "839843"
}
]
}
]
}
Routes
Router.route("courses/:courseId/:lessonId/:eachLessonId", {
path:"/courses/:courseId/:lessonId/:eachLessonId",
layoutTemplate: "layoutLessons",
template:"lessons",
onBeforeAction:function(){
var currentUser = Meteor.userId();
if (currentUser) {
Session.set('courseId', this.params.courseId);
Session.set('lessonId', this.params.lessonId);
Session.set('eachLessonId', this.params.eachLessonId);
this.next();
} else {
Router.go('/')
}
},
});
Template helper
Template.lessons.onCreated(function(){
Meteor.subscribe('listLessons');
});
Template.lessons.helpers({
currentLesson: function() {
var currentLesson = Session.get('eachLessonId');
return Lessons.find({"lesson.eachLesson.eachLessonId" : currentLesson});
},
});
HTML
{{#each currentLesson}}
{{title}}
{{video}}
{{/each}}

Instead of storing courseId, lessonId and eachLessonId as Session values, you could use the Iron Router's waitOn and data option.
For example, you could rewrite your route as follows:
Router.route('/courses/:courseId/:lessonId/:eachLessonId', {
name: 'lessons',
layoutTemplate: 'layoutLessons',
template: 'lessons',
onBeforeAction: function() {
let currentUser = Meteor.user();
if (currentUser) this.next();
else Router.go('/');
},
data: function() {
var doc = Lessons.findOne({
"courseId": this.params.courseId,
"lesson.lessonId": this.params.lessonId,
"lesson.eachLesson.eachLessonId": this.params.eachLessonId
});
if (doc) {
var lesson = {};
var lessonId = this.params.eachLessonId;
_.each(doc.lesson, function(i) {
lesson = _.find(i.eachLesson, function(j) {
return j.eachLessonId == lessonId;
});
});
return lesson;
}
return {};
},
waitOn: function() {
return [
Meteor.subscribe('lesson', this.params.courseId, this.params.lessonId, this.params.eachLessonId)
];
}
});
This should set the data context to the requested eachLesson object. However, you may consider setting the data context to a document in the Lessons collection and then just picking certain eachLesson objects. In addition, you should create a publish function which returns just the requested Lessons document and not all of them, like you probably do now in your listLessons publication. You can pass all IDs as arguments to the corresponding publish function.

Related

I've found a Json data using findById, how do I use it in my code?

I am creating an API that gets Patients data(id and name), Physicians data(id and name) and Appointments(id, phyId, patId, app_date) and displays the Patients appointed to a particular physician. I need to create a remote method in physician.js in such a way that I get related Appointment that has phyId and print the details of the Patients using the patId obtained from appointment.
I'm using loopback 3.
Refer this link for clear idea:
https://loopback.io/doc/en/lb3/HasManyThrough-relations.html
I have related models (Physicians, Patients) that are related by "hasMany" with each other "through" Appointment(another model) and Appointment is related to each of these by belongsTo, in my loopback application and i need to print the Patients of a particular Physician.
Patient data:
[
{
"id": 1,
"name": "Anna Mull"
},
{
"id": 2,
"name": "Paige Trner"
}
]
Physician data:
[
{
"id": 1,
"name": "Cardiologist"
}
]
Appointment data:
[
{
"id": 1,
"physicianId": 1,
"patientId": 1,
"appointmentDate": "2019-01-28T10:06:33.530Z"
},
{
"id": 2,
"physicianId": 1,
"patientId": 2,
"appointmentDate": "2019-01-28T10:06:33.530Z"
}
]
I know there is a method already available to query the Patients of a Physician, but I want to code it myself to learn and also print it in the following format.
My idea is to get all the Appointments having the specific phyId in it and find the patId in those appointment and store it in an array. I then use that array to get the patients from the Patient model. I managed to get the Patient details in a function, but I can only console.log(Patients) but I am not able to display it in the API response.
The following is the format i need it in. (EXPECTED OUTPUT in API response)
Physician:
{
"id": 1,
"name": "Cardiologist"
}
Patients:
[
{
"id": 1,
"name": "Anna Mull"
},
{
"id": 2,
"name": "Paige Trner"
}
]
or any similar format.
I've tried to the same and here is my code.
common/models/physician.js
'use strict';
var app = require('../../server/server');
module.exports = function (Physician) {
Physician.getDetails = function (phyid, cb) {
var Appointments = app.models.Appointment;
var Patient = app.models.Patient;
Physician.findById(phyid, function (err, Physician) {
Appointments.find({ where: { physicianId: phyid } }, function (err, Appointment) {
if (err) {
cb(null, "Errorrrrrrrr", "Errorrrrrr");
}
else {
var patients = [], i = 0;
var patobj= [];
for (i in Appointment) {
patients[i] = Appointment[i].patientId;
//console.log(patients);
Patient.findById(patients[i], function(err, Patients){
if(err){
cb("Error in patients", "--");
}
else{
patobj[i]=Patients;//doesnt have any effect
console.log(Patients);//prints in console
}
});
}
cb(null, Physician, patobj);//only Physician is printed, patobj is empty.
}
});
});
}
Physician.remoteMethod('getDetails', {
http: {
path:
'/:phyid/getDetails',
verb: 'get'
},
accepts: {
arg: 'phyid',
type: 'number'
},
returns: [{
arg: 'Physician',
type: 'Object'
}, {
arg: 'Patient',
type: 'Object'
}]
});
};
I am actually getting this in the API response:
{
"Physician": {
"id": 1,
"name": "Cardiologist"
},
"Patient": []
}
and this in the console:
D:\Project\Project1>node .
Web server listening at: http://localhost:3000
Browse your REST API at http://localhost:3000/explorer
{ name: 'Anna Mull', id: 1 }
{ name: 'Paige Trner', id: 2 }
How am I supposed to get the patient data to be printed in the API response?
You patients are empty because, finding Patients by Id is an asynchronous operation. But the for loop is synchronous. The loop finishes and calls the following line before any of the Patients are found.
cb(null, Physician, patobj);//only Physician is printed, patobj is empty.
You need to wait for all the patients to be found by using either Promise.all or async.each.

SAPUI5 Pass data from one view to another view

I am developing of a SAPUI5 application, in my application I need to pass data from one View1 to View2. I follow some sample code from internet but it seems like does not work for me.
The following is my source code
View1.controller.js
onShoppingCartPressed: function(){
var viewCartData = {
"Customer" : SelectedCustomer,
"Salesman" : SelectedSalesman,
"TxnKey" : "TXN1000103"
};
this._oRouter.navTo("ViewCarts", viewCartData);
}
View2.controller.js
onInit: function () {
this._oRouter.getRoute("ViewCarts")
.attachMatched(this._onRouteMatched, this);
},
_onRouteMatched: function(oEvent) {
console.log(oEvent.getParameter("arguments"));
},
Manifest.json
"routing": {
"config": {
"routerClass": "sap.f.routing.Router",
"viewType": "XML",
"viewPath": "com.accenture.newspage.order.ui.order-ui.view",
"controlId": "flexibleColumnLayout",
"transition": "slide",
"controlAggregation": "beginColumnPages",
"bypassed": {
"target": [
"notFound"
]
},
"async": true
},
"routes": [
{
"pattern": "orderDetail/{order}/{layout}",
"name": "orderDetail",
"target": [
"order",
"orderDetail"
]
},
{
"pattern": "ViewCarts",
"name": "ViewCarts",
"target": "viewCarts"
}
],
"targets": {
"worklist": {
"viewName": "Worklist",
"viewId": "worklist",
"viewLevel": 1
},
"viewCarts": {
"viewName": "ViewCart",
"viewId": "ViewCartPage",
"viewLevel": 1
}
}
}
When I console.log() out the data, it shows me empty data and it does not show the data that I passed from View1.
You seem to have two options here in my option. Correct me if I'm wrong.
Hope this helps.
Option 1
Manifest
{
"pattern": "ViewCarts/customer/{Customer}/Salesman/{Salesman}/key/{TxnKey}",
"name": "ViewCarts",
"target": "ViewCarts"
}
Sending controller
onShoppingCartPressed: function(oEvent) {
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("viewCarts", {
Customer: "XXXXXXXXXXXX", // can't be an object
Salesman: "xxxxxxxxxxxx",
TxnKey : "TXN1000103"
});
}
Receiving controller
onInit: function() {
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.getRoute("viewCarts").attachMatched(this._onRouteMatched, this);
},
_onRouteMatched: function(oEvent) {
var customer = oEvent.getParameter("arguments").Customer;
var salesman = oEvent.getParameter("arguments").Salesman;
var key = oEvent.getParameter("arguments").TxnKey;
}
Option 2
Manifest
No changes from yours
Sending controller
onShoppingCartPressed: function(oEvent) {
var viewCartData = {
"Customer": "XXXXXXXXXXXX",
"Salesman": "xxxxxxxxxxxx",
"TxnKey": "TXN1000103"
};
var oModel = new sap.ui.model.json.JSONModel(viewCartData);
this.getOwnerComponent().setModel(oModel, "viewCartData");
// OR sap.ui.getCore().setModel(oModel, "viewCartData");
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("viewCarts");
}
Receiving controller
onInit: function() {
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.getRoute("viewCarts").attachMatched(this._onRouteMatched, this);
},
_onRouteMatched: function(oEvent) {
var oModel = this.getView().getModel("viewCartData");
// OR var oModel = sap.ui.getCore().getModel("viewCartData");
}
For me in most scenarios it's better to use sessionStorage.
It allows you to save almost anything (also javascript Object) and access it anywhere in webapp.
sessionStorage.setItem('myKeyString', 'myString'); //Set value
sessionStorage.getItem('myKeyString'); // Get saved string
sessionStorage.setItem('myObject', JSON.stringify({"key": "value"})); // Save object
JSON.parse(sessionStorage.getItem('myObject')); // Get saved object

Backbone - Updating models in collection with data polled from another API

I have a Backbone collection that I'm populating from an API endpoint. This return data such as:
[
{
"gameId": 1234,
"gameName": "Fun Game 1"
},
{
"gameId": 1235,
"gameName": "Fun Game 2"
},
{
"gameId": 1236,
"gameName": "Fun Game 3"
},
etc,
etc,
etc
]
The collection is very simple and is initialised in the router so it's accessible to the entire app:
var GamesCollection = Backbone.Collection.extend({
model: GameModel,
url: '/path/to/games/list',
parse:function(response){
return response;
}
});
I have another endpoint that returns a collection of data related to the original data. This data looks like:
[
{
"gameId": 1234,
"numberOfPlayers": "1,000"
},
{
"gameId": 1235,
"numberOfPlayers": "Fun Game 2"
},
{
"gameId": 9999,
"numberOfPlayers": "Some other game that's not in the original list"
}
]
Note that the number of players response may or may not contain data for every game in the original response and may or may not contain data for games that do not exist in the original games response.
I need to be poll the number of players endpoint every X minutes and update the models in the GamesCollection with the data from the response so I can show this in the view.
What is the best way to handle this?
Query your numberOfPlayers endpoint and then set the fetched data on your collection. You can customize how set works with add and remove options.
For example,
var GamesCollection = Backbone.Collection.extend({
model: GameModel,
url: '/path/to/games/list',
parse: function(response){
return response;
},
pollNumberOfPlayers: function() {
var self = this;
return Backbone.ajax('/numberOfPlayers/endpoint').then(function(resp) {
self.set(resp, {add: false, remove: false, merge: true});
});
},
startPolling: function() {
var self = this,
timer = 10000; //10 seconds
self.pollNumberOfPlayers().then(function() {
setTimeout(function() {
self.startPolling();
}, timer);
});
}
});
Note that I assumed your GameModel objects have gameId as their idAttribute.

How can I filter results in typeahead.js using a second variable?

I am trying to filter results using Typeahead.js. I can currently filter the results using a field called activity_title. This works fine.
How can I filter my results by a second value? In this case, I would like to select only the results that have a certain value for activity_level. I need to set this when the typeahead is initialised rather than hard coding it into the Bloodhound initialisation (e.g. I don't want to use url: 'api/activity/&range=1,3')
I have the following valid JSON that I access remotely:
{
"meta": [
{
"name": "activity_id",
"table": "table",
"max_length": 4
},
{
"name": "activity_title",
"table": "table",
"max_length": 91
},
{
"name": "activity_level",
"table": "table",
"max_length": 2
}
],
"detail": [
{
"activity_id": "57",
"activity_title": "Help old ladies to cross the road.",
"activity_level": "2"
},
{
"activity_id": "58",
"activity_title": "Help mum with the washing up.",
"activity_level": "3"
},
{
"activity_id": "59",
"activity_title": "Shine my shoes",
"activity_level": "1"
},
{
"activity_id": "60",
"activity_title": "Put the bins out",
"activity_level": "1"
}
]
}
I set up a Bloodhound instance like this:
var activities = new Bloodhound({
datumTokenizer: function (datum) {
return Bloodhound.tokenizers.whitespace(datum.activity_title);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: '/api/activity/',
filter: function(data) {
return $.map(data['detail'], function(detail) {
return {
activity_id: detail.activity_id,
activity_title: detail.activity_title,
objective_level: detail.objective_level
};
});
}
}
});
I use Typeahead.js to do a lookup on the data as I type.
$( document ).on( "focus", ".typeahead-init", function() {
// + '&range=' + minimum + ',' + maximum
var minimum = $('#group-level-min-1').val();
var maximum = $('#group-level-max-1').val();
$(this).typeahead({
highlight: true
},
{
name: 'activity_title',
displayKey: 'activity',
source: activities.ttAdapter(),
templates: {
header: '<div class="header-name">Activities</div>',
empty: [
'<div class="empty-message">',
'No activities match your search',
'</div>'
].join('\n'),
suggestion: Handlebars.compile('<div class="typeahead-activity" id="typeahead-activity-{{activity_id}}"><strong>{{objective_level}}</strong> - {{activity_title}}</div>')
}
})
//info on binding selection at https://github.com/twitter/typeahead.js/issues/300
.bind('typeahead:selected', function(obj, datum, name) {
var target = $(this).closest('.activity-container');
var activityId = datum['activity_id'];
var url = '/api/activity/id/'+activityId;
$(target).children('.activity-id').val(activityId);
//http://runnable.com/UllA9u8MD5wiAACj/how-to-combine-json-with-handlebars-js-for-javascript-ajax-and-jquery
var raw_template = $('#activity-output').html();
// Compile that into an handlebars template
var template = Handlebars.compile(raw_template);
// Fetch all data from server in JSON
$.get(url,function(data,status,xhr){
$.each(data,function(index,element){
// Generate the HTML for each post
var html = template(element);
// Render the posts into the page
target.append(html);
});
});
});
$(this).removeClass("typeahead-init");
$(this).focus();
});
This has been cobbled together from several answers on Stackoverflow and others. Any help greatly appreciated.

How to access second level JSON object via $.getJSON

Hi guys please take a look at this having trouble reaching second level inside a object that looks like this:
{
"results": [{
"title": "TEAM",
"subtitle": "athletes",
"offset": 0,
"icon": "info",
"relevance": 1,
"link": {
"title": "HOME",
"url": "http://www.test.com",
"id": "23458"
}]
}
and this is the code I have:
var theJson = {
init: function() {
this.url = 'http://test.com/js?jsonfeed=123123';
this.fetch();
},
fetch: function() {
$.getJSON(this.url, function( data ){
var obj = $.map(data.results, function( newObj ){
return {
title: newObj .title,
icon: newObj.icon,
topic: newObj.link.id, //??
topic: newObj["link"].id //??
topic: newObj[1].id //??
};
});
});
}
};
theJson.init();
My question is how do I reach the id var in link array inside results object? Thank guys you all rock!
You are doing it correct the first and second time:
newObj.link.id
newObj["link"].id
There are both correct ways to get the id under link.
You can easily access it by index, like that:
newObj["link"][0]

Categories