Firestore: snapshotChanges is not a function - angularjs

In reference to this post, I hit a problem where it says snapshotChanges is not a function:
TypeError: db.collection(...).snapshotChanges is not a function
at new <anonymous> (index.js:139)
at Object.instantiate (angular.js:5156)
at angular.js:11719
at Object.link (angular-route.js:1251)
at angular.js:1388
at Ba (angular.js:11266)
at p (angular.js:10585)
at g (angular.js:9832)
at angular.js:9697
at angular.js:10111 "<ng-view autoscroll="true" class="ng-scope">"
Below is my code:
(Note that with or without pipe() will also hit the same error too.)
var db = firebase.firestore();
$scope.accounts = db.collection("Accounts").snapshotChanges().pipe(map(actions => {
return actions.map(a => {
const data = a.payload.doc.data();
const id = a.payload.doc.id;
return { id, ...data };
});
}));
On my SPA index.html, I have the following references defined:
<script defer src="/__/firebase/6.4.1/firebase-app.js"></script>
<script defer src="/__/firebase/6.4.1/firebase-auth.js"></script>
<script defer src="/__/firebase/6.4.1/firebase-firestore.js"></script>
<script defer src="/__/firebase/6.4.1/firebase-messaging.js"></script>
<script defer src="/__/firebase/6.4.1/firebase-storage.js"></script> -->
<script defer src="/__/firebase/init.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.8/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.8/angular-route.min.js"></script>
<!-- AngularFire -->
<script src="https://cdn.firebase.com/libs/angularfire/2.3.0/angularfire.min.js"></script>
May I know why this error (snapshotChanges is not a function) still appear? I need it to include the doc.id in my mapping result, thank you!

If you're using the plain JavaScript SDK to query Firestore, and you want to attach a listener to a Query, you should be using onSnapshot() for that.
If you want to use the AngularFire API instead and use snapshotChanges, that's going to require a going through its own API (see the link), which is different than the plain JavaScript SDK.

Related

Google Maps API dynamic load with AngularJS

I'm trying to load Google Maps API using AngularJS:
<html data-ng-app="search-app">
<head data-ng-controller="GoogleMaps">
<script ng-src="{{mapsUrl}}" type="text/javascript"></script>
....
</head>
and controller for that part:
search.controller('GoogleMaps', [
'$scope','$sce',
function GoogleMaps($scope,$sce) {
var mapsUrl = '//maps.google.com/maps/api/js?sensor=false&key=my_api_key';
$scope.mapsUrl = $sce.trustAsResourceUrl(mapsUrl);
}
]);
but when the Google Map API is called within the search controller it throws and error
this.setMap is not a function
for
function CustomMarker(latlng, map, args) {
this.latlng = latlng;
this.args = args;
this.setMap(map);
}
but when I will replace {{mapsUrl}} with full URL in the HTML header it will works.
Any thoughts on that?
I have ended up appending URL to the header as a script on load event
function require(url, callback)
{
var element = document.createElement("script");
element.src = url;
element.type="text/javascript";
element.addEventListener('load', callback);
document.getElementsByTagName("head")[0].appendChild(e);
}

why factory don't work properly between the directives?

I make two directives .To communicate between two directives I used a factory .
but it not work properly ..I want to delete my text when I press delete button ..I take factory to do my task but it not working .I also try to take service .it also don't help
here is my code
http://plnkr.co/edit/Yenmira9J9XpjscQzRoX?p=preview
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
</head>
<body ng-app="app">
<a></a>
<b></b>
<script>
angular.module('app',[]).directive('a',function(){
return {
restrict :'E',
scope:{},
templateUrl:'a.html',
controller:'ts',
controllerAs:'vm'
}
}).controller('ts',function(sharedService){
var vm=this;
vm.delete=function(){
alert('--');
sharedService.deletepro();
}
}).directive('b',function(){
return {
restrict :'E',
scope:{},
templateUrl:'b.html',
controller:'bb',
controllerAs:'vm'
}
}).controller('bb',function(sharedService){
var pm=this;
pm.message= sharedService.sendData();
}).factory('sharedService', function() {
var data = {};
function deletepro(){
data = {};
}
function sendData(){
var obj = {name:"pQr"};
data = obj;
return data;
}
return {
sendData: sendData,
deletepro: deletepro
};
});
</script>
</body>
</html>
After your controller is first initialized, data and vm.message reference the same object, but when you run deletepro then data references a new object, but vm.message still references the old one.
If you want to pass data in this way, you must never replace data with a new object (otherwise, controllers will have to get the new object again).
Instead of data = {};, try data.name = '';
It looks like you're expecting that it will update because data is a shared reference. But you are resetting it to {}, which breaks the reference. You instead need to modify it:
function deletepro(){
for(var prop in data){
delete data[prop];
}
}
Also, keep in mind a and b are both real html tags, not sure if there are any issues ovewriting the standard ,

Angular Load LinkedIn Scripts From Partial

I am using LinkedIn API and I need to load their login scripts when my user hits a certain route. However , from what I've read in stackoverflow it is not possible to just put the script elements inside a partial .
my code is straight forward :
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular-route.js">
...
<script src="http://platform.linkedin.com/in.js">
api_key: ...
authorize: true
onLoad: onLinkedInLoad
</script>
<script type="in/Login">
Hello, <?js= firstName ?> <?js= lastName ?>.
</script>
<script src="js/linkedinFuncs.js"></script>
</div>
The 3 last scripts (the linkedin ones) only needs to be included when the user hits the 'login' route . Any thoughts?
If anyone experiences this issue :
1)Make sure you load jquery BEFORE angular
2)Run the latest angular (this issue is resolved on 1.2.9 but was unresolved for me on 1.2.0)
You could load it from the code in your relevant congtroller programmatically by using something like this
(function injectScript() {
var src = 'http://platform.linkedin.com/in.js',
script = document.createElement('script');
script.async = true;
script.src = src;
var api_key = 'YOUR_KEY_HERE';
//script.authorize = true;
script.text = 'api_key: ' + api_key + '\n' + 'authorize: true\n';
script.onload = function() {
IN.Event.on(IN, 'systemReady', function() {
loadDeferred.resolve(IN);
});
};
document.getElementsByTagName('head')[0].appendChild(script);
})();

Models/View in Different Files

I just tried to adopt backbone.js to my project with the todo example. In my app.js file I try to instantiate my views/models/collections etc. but I try to I get the error msg: app is not defined in TodoList.
HTML:
<head>
<script type="text/javascript" src="js/json2.js"></script>
<script type="text/javascript" src="js/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="js/underscore-min.js"></script>
<script type="text/javascript" src="js/backbone-min.js"></script>
<script type="text/javascript" src="js/backbone-localstorage.js"></script>
<script type="text/javascript" src="js/models/models.js"></script>
<script type="text/javascript" src="js/collections/collections.js"></script>
<script type="text/javascript" src="js/views/views.js"></script>
<script type="text/javascript" src="js/views/app.js"></script>
<script type="text/javascript" src="js/app.js"></script>
app.js(root for my application):
var app = {
models:{},
collections:{},
views:{}
};
jQuery(function($) {
var Todos = new app.collections.TodoList;
var test = new Todo;
var test2 = new TodoView;
var appView = new AppView({});
});
collections.js:
app.collections.TodoList = Backbone.Collection.extend({
model: Todo,
localStorage: new Store("todos"),
done: function() {
return this.filter(function(todo) {
return todo.get('done');
});
},
remaining: function() {
return this.without.apply(this, this.done());
},
nextOrder: function() {
if (!this.length) return 1;
return this.last().get('order') + 1;
},
comparator: function(todo) {
return todo.get('order');
}
});
You're trying to use a namespace before it's ready. Two options. First, have 'app.js' first but take out initialization code and put that into a 'bootstrap.js' that's loaded absolute last. Second option, and the one that I generally lothe, define your namespaces that you need in the file if they aren't already there. For example
var app = app || {};
app.collection = app.collection || {};
Basically, the code is loading in one by one. When you say namespace.subspace, the code expects that namespace has already been defined as something - generally an object in most cases that I've seen. Without that base piece the code will just flatline - it'll assume you're trying to do the equivalent of building a castle starting with the roof.

Why does Google App Engine Channel API (jsapi) fail to load in a Chrome extension?

I'm using the Channel API in a Chrome extension.
In Google App Engine Channel API Javascript Reference (Python) page it says that
Include the following in your html page before any JavaScript code
that refers to it:
<script type="text/javascript" src="/_ah/channel/jsapi"></script>
So, I put that in the header of my options.html file:
<html>
<head>
<title>Extension Options</title>
<script type="text/javascript" src="/_ah/channel/jsapi"></script>
</head>
but Chrome throws jsapiFailed to load resource error. What am I doing wrong?
Update
As per Moishe's answer I updated the call to jsapi like this:
<head>
<title>Extension Options</title>
<!-- this does not work because it is local
<script type="text/javascript" src="/_ah/channel/jsapi"></script>
-->
<script type="text/javascript" src="https://talkgadget.google.com/talkgadget/channel.js"></script>
</head>
Update
I added onopen and other properties. Now I get the onopen alert but I am not getting the evt.data alert. What am I doing wrong?
<html>
<head>
<title>Extension Options</title>
<!-- this does not work because it is local url
<script type="text/javascript" src="/_ah/channel/jsapi"></script>
-->
<script type="text/javascript" src="https://talkgadget.google.com/talkgadget/channel.js"></script>
</head>
<body>
<p>Enter your gmail address:</p>
<textarea id="getEmail" style="margin-bottom: 4px; width: 250px; height: 20px">
</textarea><br />
<button id="save">Save</button>
<!--<button id="save">Clear</button>-->
<script>
document.getElementById("getEmail").placeholder = "your gmail address" ;
//save entered gmail address
document.getElementById("save").addEventListener
(
"click",
function ()
{
var userEmail = document.getElementById("getEmail").value;
var formData = new FormData();
formData.append("extension_user", userEmail);
alert("after formData.append")
var channel;
var socket;
var handler =
{
onopen: function () { alert("onopen") },
onerror: function () { alert("onerror") },
onclose: function () { alert("onclose") },
onmessage:
function (evt)
{
//evt.data will be what the server sends in channel.send_message
console.log("evt.data received from authhandler: " + evt.data);
alert("evt.data is: " + evt.data)
}
};
var xhr = new XMLHttpRequest();
//changed to lowercase
xhr.onreadystatechange = function()
{
//alert("xhr.onReadyStateChange")
//error handling etc not included
if (xhr.readyState == 4 && xhr.status == 200)
{
token = xhr.responseText;
alert("token: " + token)
channel = new goog.appengine.Channel(token);
socket = channel.open(handler);
}
};
xhr.open("POST", "http://ting-1.appspot.com/authsender", true);
xhr.send(formData);
console.log("formData sent to authsender: " + formData);
}, false
)
</script>
</body>
</html>
In a chrome extension, you'll need to directly specify the path for the Channel javascript (https://talkgadget.google.com/talkgadget/channel.js). The request for /_ah/channel/jsapi can't be redirected by anything because the file that's trying to load it is local.

Resources