I am building an Angular App with Firebase.
My intention is to create an object (say Rooms) at the root with 3 child objects (say Room1, Room2 & Room3) . Also, I am trying to create a logic that would check if the Rooms object is there - it wont create it again.
My code was :
var ref = new Firebase(firebaseURL);
ref.child('Rooms').once('value', function (snapshot){
if(snapshot.numChildren() == 0){
// Create Room within a loop
ref.child('Rooms').child(i).set(roomObj);
}else if(snapshot.numChildren() > 0){
// do not create
}
}
But when the code runs - it always enters into the if block !! And creates the child Rooms.
What is my mistake in the code ??
Most likely the value event will be triggered again with the value you expect.
Your solution is to run the code in a transaction.
var ref = new Firebase(firebaseURL);
ref.child('Rooms').transaction(function (data){
if(!data){
var rooms = {};
for (var roomNum=0; roomNum < 3; roomNum++) {
rooms['room'+roomNum] = { name: 'Room '+roomNum };
}
return rooms
}
}
So if the rooms don't exist yet, the above code creates them. If they already exist, the code does nothing (not returning a value, leaves the data unmodified).
Be sure to read the Firebase documentation for transaction.
Related
I'm using API Jira
I'm doing some functions but before to use function, I need to verified if a value exist or not
If he exists so I can launch functions else do nothing.
I'm doing this :
// Call the file functions.js
var functions = require('./functions.js')
/*
Function getAllIssueForSCII displays all the issues in the form of a JSON and that browses all the keys that are in the SCII project
Function pushInitialization initializes the computer score card to 80 on Jira
*/
functions.getAllIssueForSCII().then(function(json){
for (let i=0; i<json.issues.length;i++){
if(json.issues[i].fields.customfield_14038 = null){ // i'm doing this
console.log(json.issues[i].key);
functions.pushInitialization(json.issues[i].key);
}
}
});
/*
A delay is added so that Jira can correctly recover the value 80.
Thanks to this value, we can do all the calculation (Function calculate)
Function pushComputerScoreCard push the final value into the computer score card.
Function generateJSON generates a JSON.
Function replaceCharacter solve the problem of array inside the JSON
*/
setTimeout(function() {
functions.getAllIssueForSCII().then(function(json){
for (let i=0; i<json.issues.length;i++){
functions.calculate(json.issues[i]);
functions.pushComputerScoreCard(json.issues[i].key);
functions.generateJSON(json.issues[i].key);
functions.replaceCharacter();
}
});
}, 1000)
My problem: After the settimeout, he recover value already exist and do the calcul...
I need to verified my condition in all of the script .
Thanks for your help
You are assigning null value in an if condition:
if(json.issues[i].fields.customfield_14038 = null){ // i'm doing this
You need to compare values:
if(json.issues[i].fields.customfield_14038 === null){ // You need to do this:
After the user validate an order, the status of the order is set so validated and it is sent to another system X, the problem is that the plugin is fired twiced in some cases even more than twice and that lead to sending this entity multiple time to the system X. I tried to correct that by using the context.depth, but all the time is equal to 1.
JS Method:
Validate: function () {
Xrm.Page.getAttribute("eoz_validated").setValue(true);
Xrm.Page.data.entity.save();
ABE.Order.HideVisibleField();
Xrm.Page.ui.clearFormNotification('ProductError');
}
}
Plugin Execute method:
protected void ExecutePostOrderUpdate(LocalPluginContext localContext)
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}
if (localContext.PluginExecutionContext.Depth > 1)
{
return;
}
tracingService = localContext.TracingService;
var order = (Entity)localContext.PluginExecutionContext.InputParameters["Target"];
bool isValidated = order.GetAttributeValue<OptionSetValue>("abe_isValidated").Value : false;
if (isValidated )
{
SendToSystemX(localContext.OrganizationService, order.Id);
SendProductsToOwner(localContext.OrganizationService, order.Id);
}
var statecode = order.Contains("statecode") ? order.GetAttributeValue<OptionSetValue>("statecode").Value : -1;
}
If your plugin is registered to trigger on update of "eoz_validated" and also updates "eoz_validated" then you can have an infinite execution loop.
To avoid this, before updating your context entity, reinstantiate it:
var updatedEntity = new Entity { LogicalName = context.LogicalName, Id = context.Id };
This removes all attributes that would otherwise have been updated such as "eoz_validated" which is contained within the context entity. Note that in your code you store the context entity within a variable called order.
I'm just guessing here (and don't have 50 reputation to ask a question). If this is happening in your code then presumably it's within SendToSystemX(IOrganizationService, Guid) or SendProductsToOwner(IOrganizationService, Guid).
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
I'm working on adding notifications to my chrome extension that lets users chat with one another. Each user has a chats section that lists all the chatrooms they're in, along with the last message they saw. To check if a message was sent while they were away, I'm trying to loop through the chatrooms they're in and seeing if the last message sent was added to the database after the last message they saw was added.
Here's my code, I'm using a noSQL database called firebase:
var checkNotifications = function(user){
var notification = false;
firebase.database().ref('users/'+user+'/chats').once('value', function(snapshot){
for (var chat in snapshot.val()){
var lastMessage = snapshot.val()[chat];
firebase.database().ref('chats/'+chat+'/msgs').once('value', function(snap){
if (Object.keys(snap.val())[0] > lastMessage){
notification = true; // Message is newer than their last seen message
}
});
}
});
}
I'm running into the issue where before the second database call to return the messages from the chatroom currently being looked at in the for loop the for loop already moves on and updates the variable lastMessage, so that variable ends up holding the last message from a chatroom further down the chain.
Is there a way to make this less asynchronous so that the variable lastMessage has the same value for each step in the for loop?
To check if a message was sent while they were away, I'm trying to
loop through the chatrooms they're in and seeing if the last message
sent was added to the database after the last message they saw was
added.
transactions take a function and are the proper tool for this.
When working with data that could be corrupted by concurrent
modifications, such as incremental counters, you can use a transaction
operation. You can give this operation an update function and an
optional completion callback. The update function takes the current
state of the data as an argument and returns the new desired state you
would like to write. If another client writes to the location before
your new value is successfully written, your update function is called
again with the new current value, and the write is retried.
For instance, in the example social blogging app, you could allow users to star and unstar posts and keep track of how many stars a post has received as follows:
function toggleStar(postRef, uid) {
postRef.transaction(function(post) {
if (post) {
if (post.stars && post.stars[uid]) {
post.starCount--;
post.stars[uid] = null;
} else {
post.starCount++;
if (!post.stars) {
post.stars = {};
}
post.stars[uid] = true;
}
}
return post;
});
}
This should work. It captures the variable in a closure: (function(){ ... })();
var checkNotifications = function(user){
var notification = false;
firebase.database().ref('users/'+user+'/chats').once('value', function(snapshot){
for (var chat in snapshot.val()){
(function(){
var lastMessage = snapshot.val()[chat];
firebase.database().ref('chats/'+chat+'/msgs').once('value', function(snap){
if (Object.keys(snap.val())[0] > lastMessage){
notification = true; // Message is newer than their last seen message
}
});
})();
}
});
}
Or this:
var checkNotifications = function(user){
var notification = false;
firebase.database().ref('users/'+user+'/chats').once('value', function(snapshot){
var func = function(lastMessage) {
firebase.database().ref('chats/'+chat+'/msgs').once('value', function(snap){
if (Object.keys(snap.val())[0] > lastMessage){
notification = true; // Message is newer than their last seen message
}
});
};
for (var chat in snapshot.val()){
func(snapshot.val()[chat]);
}
});
}
i have a Meteor Application which is very "slow" as there are a lot of API-Calls.
What i try to do is to break apart the loading/calls.
What i just did is:
i have loading template via iron-router
i waitOn for the first API-Call has finished
then i start the next API-calls in the Template.myTemplate.rendered - function
This was already a big benefit for the speed of my Application, but i want to break it up even more as the second call is in fact more like 5-25 API-calls.
So what i try to do now is inside the rendered function is a self-calling function which calls itself as long as there are no more to do and saves the response inside a session. (Until now it just rewrites, but even to this point i can´t get)
Template.detail.rendered = function(){
//comma separated list of numbers for the API-Call
var cats = $(this.find(".extra")).attr('data-extra').split(',');
var shop = $(this.find(".extra")).attr('data-shop');
var counter = 0;
var callExtras = function(_counter){
var obj = {
categories : [cats[_counter]],
shop : shop
};
if(_counter <= cats.length){
Meteor.subscribe('extra', obj,function(result){
//TODO dickes todo... nochmal nachdenken und recherchieren
//console.log(_counter);
Session.set('extra',Extra.find('extra').fetch()[0].results);
counter++;
callExtras(counter);
});
}
};
callExtras(counter);
Session.set('loading_msg', '' );
};
Now i have again problems with my reactive parts of the app desscribed here - Meteor: iron-router => waitOn without subscribe As i can´t find a proper way to update my client-side per user base collection. Also in the docs it is described the publish method also creates a new collection. (The new document´s ID) here - http://docs.meteor.com/#/full/publish_added
here is the publish from server
Meteor.publish('extra', function(obj){
var that = this;
Meteor.call('extra', obj, function(error, result){
if (result){
//console.log(result);
that.added("extra", "extra", {results: result});
//that.changed('extra','extra',{results: result});
that.ready();
} else {
//that.ready();
}
});
});
So my question is: Is there from scratch a better way to structuring my code means solving the problem somehow different? If not how can i achive it the cleanest way? Because for my understanding this is just strange way to do it.
EDIT:
For example.
Can i do a per-user-collection (maybe only client-side like now) and push data from the server and just subscribe to this collection? But then how can i check when the async API-Call has finshed to start the next round. So the view gets data piece by piece. I am just confused right now.
My fault was simple as i thaught: You don´t need to use subscribe.
I just added "error,result" in the callback of Meteor.call
Only "result" leads to the result is always undefined.
var cats = $(this.find(".extra")).attr('data-extra').split(',');
var shop = $(this.find(".extra")).attr('data-shop');
var counter = 0;
var callExtras = function(_counter){
var obj = {
categories : [cats[_counter]],
shop : shop
};
if(_counter <= cats.length){
Meteor.call('extra', obj,function(error,result){
var actual_session = Session.get('extra');
if(actual_session === false){
actual_session = [];
}
actual_session = actual_session.concat(result);
Session.set('extra',actual_session);
counter++;
callExtras(counter);
});
}
};
callExtras(counter);
Then in the template helper
"extra" : function(){
return Session.get('extra');
},
My AngularJS CRUD application processes it's information over a WebSocket Server. (This was mainly so that updates from one user would get automatically pushed to all users without the need for massive HTTP polling)
I realized early on that I would have to set up my services differently than I normally do with HTTP services. Normally, for each Model that I am working with, I give them their own service to populate that particular Model. However, this is not feasible with a Websocket Connection, because I don't want a separate connection for each service. Therefore, there are a couple of solutions.
1) set up a single service that establishes a connection, then share that connection with other services that will use that service to make their specific queries
2) make a single, type-agnostic service that will be used by all controllers that need access to the connection and data.
Option 2 seemed much easier to manage and would be reusable across applications, so I started on that. That was when I realized that this was actually an opportunity. Rather than explicitly creating models for each type of data that the Client could receive, I could create a master data object, and dynamically create child objects of myService.data as needed when data flows in from requests. Thus, if I ever need to update my Model, I just update the Model at the server level, and the client already knows how to receive it; it will just need a Controller that knows how to use it.
However, this opportunity brings a drawback. Apparently, because myService.Data is an empty, childless object at creation, any Scope that wants to reference its future children have to simple reference the object itself.
For example, $scope.user = myService.data.user throws an error, because that object doesn't exist at declaration. it would appear that my only option is for each controller to simply have $scope.data = myservice.data, and the view for each controller will simply have to use
< ng-model='data'>, with the declarations being something like {{data.user.username}}. I have tested it, and this does work.
My question is this; Is there any way I can get the best of both worlds? Can I have my service update it's data model dynamically, yet still have my controllers access only the part that they need? I? I was feeling quite clever until I realized that all of my Controllers were going to have access to the entire data model... But I honestly can't decide if that is even a huge problem.
Here is my Service:
app.factory('WebSocketService', ['$rootScope', function ($rootScope) {
var factory = {
socket: null,
data: {},
startConnection: function () {
//initialize Websocket
socket = new WebSocket('ws://localhost:2012/')
socket.onopen = function () {
//todo: Does anything need to happen OnOpen?
}
socket.onclose = function () {
//todo: Does anything need to happen OnClose?
}
socket.onmessage = function (event) {
var packet = JSON.parse(event.data);
////Model of Packet:
////packet.Data: A serialised Object that contains the needed data
////packet.Operation: What to do with the Data
////packet.Model: which child object of Factory.data to use
////packet.Property: used by Update and Delete to find a specific object with a property who's name matches this string, and who's value matches Packet.data
//Deserialize Data
packet.Data = JSON.parse(packet.Data);
//"Refresh" is used to completely reload the array
// of objects being stored in factory.data[packet.Model]
// Used for GetAll commands and manual user refreshes
if (packet.Operation == "Refresh") {
factory.data[packet.Model] = packet.Data
}
//Push is used to Add an object to an existing array of objects.
//The server will send this after somebody sends a successful POST command to the WebSocket Server
if (packet.Operation == "Push") {
factory.data[packet.Model].push(packet.Data)
}
if (packet.Operation == "Splice") {
for (var i = 0; i < factory.data[packet.Model].length; i++) {
for (var j = 0; j < packet.Data.length; j++){
if (factory.data[packet.Model][i][packet.Property] == packet.Data[j][packet.Property]) {
factory.data[packet.Model].splice(i, 1);
i--;
}
}
}
}
// Used to update existing objects within the Array. Packet.Data will be an array, although in most cases it will usually only have one value.
if (packet.Operation == "Update") {
for (var i = 0; i < factory.data[packet.Model].length; i++) {
for (var j = 0; j < packet.Data.length; j++) {
if (factory.data[packet.Model][i][packet.Property] == packet.Data[j][packet.Property]) {
factory.data[packet.Model][i] = packet.Data[j]
i--;
}
}
}
}
//Sent by WebSocket Server after it has properly authenticated the user, sending the user information that it has found.
if (packet.Operation == "Authentication") {
if (packet.Data == null) {
//todo: Authentication Failed. Alert User Somehow
}
else {
factory.data.user = packet.Data;
factory.data.isAuthenticated = true;
}
}
$rootScope.$digest();
}
},
stopConnection: function () {
if (socket) {
socket.close();
}
},
//sends a serialised command to the Websocket Server according to it's API.
//The DataObject must be serialised as a string before it can be placed into Packet object,which will also be serialised.
//This is because the Backend Framework is C#, which must see what Controller and Operation to use before it knows how to properly Deserialise the DataObject.
sendPacket: function (Controller, Operation, DataObject) {
if (typeof Controller == "string" && typeof Operation == "string") {
var Data = JSON.stringify(DataObject);
var Packet = { Controller: Controller, Operation: Operation, Data: Data };
var PacketString = JSON.stringify(Packet);
socket.send(PacketString);
}
}
}
return factory
}]);
Here is a Simple Controller that Accesses User Information. It is actually used in a permanent header <div> in the Index.html, outside of the dynamic <ng-view>. It is responsible for firing up the Websocket Connection.
App.controller("AuthenticationController", function ($scope, WebSocketService) {
init();
function init() {
WebSocketService.startConnection();
}
//this is the ONLY way that I have found to access the Service Data.
//$scope.user = WebSocketService.data.user doesn't work
//$scope.user = $scope.data.user doesn't even work
$scope.data = WebSocketService.data
});
And here is the HTML that uses that Controller
<div data-ng-controller="AuthenticationController">
<span data-ng-model="data">{{data.user.userName}}</span>
</div>
One thing you could do is store the data object on the root scope, and set up watches on your various controllers to watch for whatever controller-specific keys they need:
// The modules `run` function is called once the
// injector is finished loading all its modules.
App.run(function($rootScope, WebSocketService) {
WebSocketService.startConnection();
$rootScope.socketData = WebSocketService.data;
});
// Set up a $watch in your controller
App.controller("AuthenticationController", function($scope) {
$scope.$watch('socketData.user', function(newUser, oldUser) {
// Assign the user when it becomes available.
$scope.user = newUser;
});
});