Firebase on('child_added' is adding more unwanted childs / .once returns only the last one in the array - json

Working on a node script to automatically call Google's pagespeed api upon giving the sites listed in a json file.
{
"1" : "https://bitbucket.org",
"2" : "https://www.catswhocode.com/blog/"
}
The aim is to add the results for the api call to firebase json database along with a top-level url node to store the firebase key for the sitestats entered. Here is example firebase data structure I need.
sites
-KrehhWxld7XlKCuFSHRY
stats { ... }
url: "https://bitbucket.org"
-KrehhXAWlYdjAOA9sd95
stats { ... }
url: "https://stackoverflow.com"
urls :
393957be871e209a76e0dc5df1f526ec : -KrehhWxld7XlKCuFSHRY
7f4c919540be6ec81cd37d9e61da6c37 : -KrehhXAWlYdjAOA9sd95
Within a promise I am adding a reference for the nodes in firebase json database.
Promise.all(allRes).then( function( values ) {
var ref = db.ref();
var sitesRef = ref.child("sites");
var urlsRef = ref.child("urls");
var psiresults = {};
SitesArray.map(function (sites, index) {
psiresults[sites] = values[index]
desktopStats = values[index].desktopStats;
mobileStats = values[index].mobileStats;
sitesRef.push({
url: sites,
stats: metricsResponse
});
sitesRef.once('child_added', function(snapshot) {
console.log(snapshot.key) //returns the last key only
});
});
When using once('child_added', ... it adds to the url top level node only for the very last item. However, if on('child_added', ... multiple copies of the same data and other childs are added. Not sure how to add the exact child key to the urls top level node when a child is added to sites node each time.

If you want to process all URLs once, use once('value' with snapshot.forEach():
sitesRef.once('value', function(snapshot) {
snapshot.forEach(function(child) {
console.log(child.key);
});
});

Finally, I had to do it using..
sitesRef.on('value', function(snapshot) {
...
});
This returns all the children and then I am able to filter the result based on my SitesArray values. I don't really find this an appropriate solution, however, I was able to make it work.

Related

Can I use i18n resource bundle within json data?

I have an SAPUI5 (V1.24) master-detail application wherein I have to display a list of about 25 static items and each item displays a static image when clicked.
I have the list titles stored in an i18n file which is instantiated as a ResourceBundle within the Component.js file.
Now instead of adding 25 rows of StandardListItem objects in my Master.xml.view file I was wondering if I could store all titles in a JSON file under mockdata folder and bind a JSONModel to my sap.m.List. But since the values in my JSON "key":"value"are nothing but the list titles I was looking for a way to bind the i18n texts with the JSON. Something like this:
{
"List": [
{
"Key": "'{i18n>value1}'"
},
{
"Key": "'{i18n>value2}'"
},
...
]
}
But it didn't work at runtime. Instead it displayed the value as-is, as shown below:
Adding as many list items in the view doesn't feel right. What if tomorrow the list increases from 25 to 50? Please help.
Thanks.
After our chat discussion I came up with the following solution
var aAllKeys = [],
aMasterKeys = [],
oProperties = {},
oJSON = {
items: []
};
// Get the current locale (for example "de-DE")
var sCurrentLocale = sap.ui.getCore().getConfiguration().getLanguage();
// This creates an array of locale fallback solutions.
// For example ["de-DE", "de", "en", ""]
var aFallbacks = jQuery.sap.resources._getFallbackLocales(sCurrentLocale);
// iterate all locales
for (var i = 0; i < aFallbacks.length; ++i) {
var sLocale = aFallbacks[i];
// try to load i18n file for each locale
oProperties = jQuery.sap.properties({
url: "i18n/i18n" + (sLocale ? "_" + sLocale : "") + ".properties"
});
// if the i18n file exists (i. e. contains keys)
if (oProperties.getKeys().length > 0) {
aAllKeys = oProperties.getKeys();
break;
}
}
// find all keys of items to display in master (the prefixed ones)
for (i = 0; i < aAllKeys.length; ++i) {
if (aAllKeys[i].indexOf("MyPrefix.") > -1) {
aMasterKeys.push(aAllKeys[i]);
}
}
// find all values of items to display in master
for (i = 0; i < aMasterKeys.length; ++i) {
oJSON.items.push({
key: aMasterKeys[i],
value: oProperties.getProperty(aMasterKeys[i])
});
}
You can then use oJSON to create a new JSON model which can be bound to your masterlist
Edit: I modified the beginning of the snippet. This adds a fallback solution if there is no i18n file for the current locale. This is tested against SAPUI5 v1.30.
See the solution I have provided in a similar topic: How is localized data binding set up with JSON files and XML views?
The best part, it requires only a single-line helper method :-)

Meteor: Return unique records for a list filter

Hi I have a simple filter that allows a user to filter a list of posts based on a single dropdown of each list that has an associated post.
In other words, it allows you to filter all posts with a list of "Favorite drink places in williamsburg"
However, I only want that list to show in the dropdown once (right now it will show up 3 times if for example it has 3 posts associated with that list). Here's the helper function...
Template.userListsFilter.helpers({
userlistsfilter: function() {
var currentUser = Meteor.userId();
return Posts.find({userId: currentUser, postlistid: { $exists : true } },{sort:{postlistname:1} },{reactive:true});
}
});
If you are on the client, this can be done with function uniq from the underscore package. On the server this could be done using the node mongo driver directly.
I assume the unique field is postListName. For the client the code could be like this.
var query = Posts.find({userId: currentUser, postListId: { $exists : true }},{sort:{postListName:1} });
var postListNames = query.map( function( post ){ return post.postListName });
postListNames = _.uniq( postListNames );

BackboneJS - Multiple Collections in one view

I have a leftMenu section where I want to display 4 menus which are categorized by each subject. Every menu is a list and I want them to be displayed in the same View. I created 4 JSON files and to each one of them a Collection and a Model.
Usually i do it like this, first i define in router
this.mainMenuCollection = new MainMenuCollection();
this.mainMenuView = new MainMenuView({el:'#nav', collection:this.mainMenuCollection});
So, now I have these 4 collections defined in the router which I want in one view:
this.allcategoryMenuCollection = new AllCategoryMenuCollection();
this.natcategoryMenuCollection = new NatCategoryMenuCollection();
this.intcategoryMenuCollection = new IntCategoryMenuCollection();
this.topcategoryMenuCollection = new TopCategoryMenuCollection();
usually i render the collection in the View like this:
$(this.el).html(this.template({mainmenu:this.collection.toJSON()}));
Please help
Send in the collections as an object when you create your view:
this.mainView = new MainView({
el:'#nav'
});
this.mainView.collections = {
allcategoryMenuCollection: this.allcategoryMenuCollection
natcategoryMenuCollection: this.natcategoryMenuCollection
intcategoryMenuCollection: this.intcategoryMenuCollection
topcategoryMenuCollection: this.topcategoryMenuCollection
}
Access your collections inside the view by calling this.collections.collectionName
This may not seem like a semantically accurate way to do it, but to take advantage of backbone automatically assigning the model and collection properties in options, I do this:
var view = new MyView({
collection: {
comments: new CommentsCollection(),
images: new ImagesCollection()
}
});
Works for me. Otherwise you have to put a line in the initialize function to extend this, or use a base class. Personally I find this more elegant, even if it's missing a couple of pluralised properties.
Then, later on, you can use this.collection.comments.toJSON() for your template properties.
There's nothing wrong with housing a number of sub-views within an outer view. Your outer view could be something as simple as this. If you need to wait for all of your collections to be fetched before you render you could try:
var MyView = Backbone.View.extend({
initialize : function (options) {
var done, collections = options.collections;
done = _.invoke(collections, 'fetch');
$.when.apply($, done).done(this.onDataReady);
},
onDataReady : function () {
// Make a second pass at render after setting the flag.
this.ready = true;
this.render();
},
render : function () {
if(this.ready === true) {
// Now render the whole lot.
} else {
// Any non-data depending rendering.
}
}
});
Then you can either initialize the outer view and pass an array of collections in:
var view = new MyView({
collections: [...]
});
If you don't need all the collections to have fetched their data before hand it's even simpler. Just pass in the collections array and set them up as you need:
var MyView = Backbone.View.extend({
initialize : function (options) {
this.collectionA = options.collections[0];
},
render : function () {
}
});

Getting Current Data from KendoUI TreeView

I'm using a kendo UI tree with a remote data source from a JSON file.
I have a button on the tree page which gets the current data of the tree,sends it through a POST to a server and the server saves the current data to the JSON file so as the next time I reload the page,the changes I made will be kept.That's what I want to happen.
So I know the current data of the tree is in:
$("#treeview").data("kendoTreeView").dataSource.data()
Which means the data changes real time in there for example when someone drag and drops a node of the tree.
My problem starts when this data doesn't seem to change when I drag and drop nodes inside the tree,and only changes when I drag and drop a node on the root level of the tree and even then it doesn't do it correcly as the node should be moved in there as well but instead the node gets copied,leaving the past node in the tree as well...
For Example I have this tree:
If I make a drag and drop change like this:
And I send the data,save it and reload the change isn't made at all!
PS:Even when I view the current data after the change before sending it,I see that there is no change on the data at all even though I did a change visualy with a drag and drop.So it doesn't have to do with the sending,saving and the server.
On the other hand,if I make a change like this:
I can see in the current data that the moved node is added in the end of the data indeed but it is not deleted from it's initial position within the data!So if i send the current data to the server,save it and then refresh I get the result:
The code for viewing and sending the data is:
function sendData() {
var req = createRequest();
var putUrl = "rest/hello/treeData";
req.open("post", putUrl, true);
req.setRequestHeader("Content-type","application/json");
var dsdata = $("#treeview").data("kendoTreeView").dataSource.data();
alert(JSON.stringify(dsdata));
req.send(JSON.stringify(dsdata));
req.onreadystatechange = function() {
if (req.readyState != 4) {
return;
}
if (req.status != 200) {
alert("Error: " + req.status);
return;
}
alert("Sent Data Status: " + req.responseText);
}
}
Is this a Bug or am I doing something wrong?Has anyone been able to see the current data changing correctly on every drag and drop?
First and most important you have to use the latest version of KendoUI (Kendo UI Beta v2012.3.1024) still in beta but is where they have solved many problems.
Then, when you create the kendoTreeView you have to say something like:
tree = $("#treeview").kendoTreeView({
dataSource :kendo.observableHierarchy(data),
dragAndDrop:true
}).data("kendoTreeView");
Here the important is not using directly data array but wrapping it with kendo.observableHierarchy.
Then you will have the data updated with the result of drag & drops.
For me in addition to OnaBai answer I had to use the sync function on the save method. I am using Type Script.
this.treeData = new kendo.data.HierarchicalDataSource({
data: kendo.observableHierarchy([]),//Thanks OnaBai
schema: {
model: {
id: "MenuItemId",
children: "MenuItemChildren",
hasChildren: (e) => {
//this removes arrow next to items that do not have children.
return e.MenuItemChildren && e.MenuItemChildren.length > 0;
}
}
}
});
public save() {
this.treeData.sync().done(() => {
console.log("sync data");
var myType = this.treeData.view();
this.$http.post("/api/TreeViewPicker", myType)
.then((response) => {
});
});
}

using multiple json request with backbone.js

I have recently started working with backbone.js and i am finally started to get my head around after many tutorials.
One thing i am stuck on is how to use the routing to allow a list to pull different rest request.
Say i have the following in my collection
var NewsCollection = Backbone.Collection.extend({
model : News,
url: 'http://api.example.com/index.php/news/all/format/json',
});
From my understanding correct me if i am wrong backbone stores all the data pulled from the above feed into my model that extends this collection, this will all work i will pull in the feed and then display it in the view
This is where i get confused within my routing i have the following.
var NewsRouter = Backbone.Router.extend({
routes: {
"": "defaultRoute",
"news/:country_code":"updatedRoute"
},
defaultRoute: function () {
console.log("defaultRoute");
var movies = new NewsCollection()
new NewsView({ collection: movies });
movies.fetch();
//setInterval(function(){movies.fetch({success: function(){}});}, 60000);
},
updatedRoute:function (country_code) {
//confused
this.movie = this.movies.get(country_code);
}
})
I need to run the updatedRoute function when that will display a list of news based on cat of country code see below.
http://api.example.com/index.php/news/country/gb/format/json
How do i update the whole feed when a list item is click so the browser url would be.
http://localhost:8888/backbonetut/#news/gb
my list item is.
<li><a href='#news/gb'>GB</a></li>
I can get that in the updateRoute function with
this.movie = this.movies.get(country_code);
Can someone please help
You can either override the fetch function on your collection or temporarily change the url of the collection in your router action.