How to tell if Save was triggered by User or Javascript function - javascript

I have an issue in which users aren't updating case status when saving cases. The status field is required and usually always populated. I have a requirement from the client to prompt the user to make sure the case status is updated and correct. Without building a bunch of code for this, I leveraged a form, made everything required not visible (it will be there anyways initially) and only made the case status fields visible.
I did this so that it's always navigated to OnSave as seen as follows:
The following is the JS:
function OnSave(context)
{
debugger;
var formContext = context.getFormContext();
var saveCounter = 0;
LoadCaseStatusModal(formContext, saveCounter);
}
function OnLoad(context)
{
var formContext = context.getFormContext;
}
function LoadCaseStatusModal(formContext, saveCounter)
{
debugger;
formContext.data.entity.save();
var formContext = formContext;
if (formContext.ui.getFormType()==2)
{
var lblForm = "Case Status Modal";
if (formContext.ui.formSelector.getCurrentItem().getLabel() != lblForm)
{
var items = formContext.ui.formSelector.items.get();
for (var i in items)
{
var item = items[i];
var itemId = item.getId();
var itemLabel = item.getLabel()
if (itemLabel == lblForm)
{
item.navigate();
}
}
}
}
}
The problem here is when I navigate here:
function LoadCaseStatusModal(formContext, saveCounter)
{
debugger;
formContext.data.entity.save();
The formContext.data.entity.save();kicks the OnSave() off again and the user gets a prompt asking them to save again when they already saved. Totally kills the whole flow.
So I thought I'd create an OnSave helper variable like so: var saveCounter = 0;
I immediately knew this would cause probs.
Then I found this: https://learn.microsoft.com/en-us/power-apps/developer/model-driven-apps/clientapi/reference/save-event-arguments/getsavemode
The problem here is that it doesn't same to tell me if the user executed this or was this kicked off by JS? -- The OnSave function.
Am I thinking to hard? am I missing something? any extra eye would help.
Regards,

I suppose .save() to be async... That is why I suggest a setTimeout() below. Anyway, using a global flag to make an exception on the programmatic save is a way to investigate.
let savingProgramatically = false
function OnSave(context)
{
debugger;
// return if the flag is on
if(savingProgramatically) return
var formContext = context.getFormContext();
var saveCounter = 0;
LoadCaseStatusModal(formContext, saveCounter);
}
function OnLoad(context)
{
var formContext = context.getFormContext;
}
function LoadCaseStatusModal(formContext, saveCounter)
{
debugger;
// Set the flag true
savingProgramatically = true
// save
formContext.data.entity.save();
// set the flag back to false asap -- Adjust the delay
setTimeout(()=>{savingProgramatically = false}, 100) // maybe 0 delay works... Or no setTimeout at all.
...

Related

Why my PostUpdateOrder Plugin executed twice CRM 2013

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).

Java/Firebase Script Executing Multiple Times

I am having an interesting issue. The general idea of what I am doing is pulling data from a Firebase database, and populating a table based on that data. Everything runs perfectly during initial population--cells and rows are populated as they should be, but the weird issue is that the scripts seem to execute again randomly. I've logged the incoming data to the console, and can see it print twice after some amount of time.
This second execution does not happen if I am to navigate between pages, or reload the page--in either of those cases everything works as it should. The problem SEEMS to happen when I log back into my computer after locking it??? Does anybody have ANY idea what could be going on here? Relevant portion of script below:
const table = document.getElementById('myTable');
firebase.auth().onAuthStateChanged(firebaseUser => {
if (firebaseUser) {
let user = firebase.auth().currentUser;
let uid = user.uid;
const dbRef = firebase.database().ref().child("data/" + uid);
dbRef.once('value', snap => {
var dataCount = snap.child("secondData").numChildren();
var datalist = snap.child("secondData").val();
var dataArray = Object.keys(datalist).map(function(k) {
return datalist[k]
});
pullAllInfo(dataCount, dataArray);
});
}
});
function pullAllInfo(count, array) {
let k = 0;
let dataArray = [];
for (i = 0; i < count; i++) {
let specificRef = firebase.database().ref().child("secondData/" + array[i]);
specificRef.once('value', snap => {
var optionsTag = array[k];
k++;
var dataId = snap.child("id").val();
var dataName = snap.child("name").val();
var dataCount = snap.child("data").numChildren();
dataArray.push(dataId, dataName, dataCount, optionsTag);
if (k == count) {
buildTable(dataArray);
console.log(dataArray);
}
});
}
}
As you can see from the code above I AM calling .once() for each reference, which would prevent data duplication from the typical .on() call. Just cant seem to figure this one out. ALSO I have an iMac, just for anyone curious about my potential computer unlock diagnosis.
Thanks all!
Most likely, the auth state is changing and setting off your function. Try throwing a log under firebase.auth().onAuthStateChanged like this:
firebase.auth().onAuthStateChanged(firebaseUser => {
console.log( 'auth state changed', firebaseUser );
if (firebaseUser) {
My guess is that you'll see that the AuthState is changing when you log out/log in from your computer.
I solved this issue by creating another global boolean called preLoaded. At the beginning, it is set to false and, once the data is loaded and passed off to build the table, it is set to true. It now looks like this:
if(k == count && preloaded == false){
preloaded = true;
console.log(dataArray);
buildTable(dataArray);
}
All set!

how can i add data to this object in node?

on the client side, when you open the socket it will send this message:
socket.onopen = function(event) {
var jsonstring = JSON.stringify({"type":"join", "id":myStorage.username});
socket.send(jsonstring);
};
on the server side we have an object called clientMessages, but i can't seem to tell if any data is getting added to it. additionally, it needs to somehow hold all the active users on the server.
var clientMessages = {};
socket.on("message", function (data) {
var parsed = JSON.parse(data);
if (parsed.type === "join") {
var jsonstringy = JSON.stringify({ type: "history", value: clientMessages});
socket.send(jsonstringy);
clientMessages[parsed.id] = [parsed];
} else if (parsed.type === "leave") {
delete clientMessages[parsed.id];
} else {
clientMessages[parsed.id].push(parsed);
}
});
You can console.log(clientMessages) every time you get a message to check it, but definitely this way should be ok. I do not see any problem there.
EDIT:
Ok i saw the source code, var clientMessages = {};is inside wss.on('connec...this means that everytime someone connects it will reset that variable. you should move the var clientMessages = {}; above that wss.on('..
basically you will end up with something like this
var clientMessages = {}; //line 20
wss.on("connection", function (socket) { //line 21 not edited
whichever function whats to keep a watch on this object, send them the clientMessages.length. This method can then perform the next necessary actions.

how to run multiple nested calls with javascript

My web application is developed on Ruby on Rails environment.
I want to execute the same set of actions by the controller, a certain number of times, that's determined by 3 inputs from the user. These are a_count, b_count, and c_count, so the total number of calls should be a_count*b_count*c_count.
Requests are sent to the server every 100ms.
I tried this Javascript code, but it is not working
function runBatch() {
var timestamp = Date.now();
var i_a = 0;
var i_b = 0;
var i_c = 0;
var a_count = parseInt(document.getElementById('a_id').value);
var b_count = parseInt(document.getElementById('b_id').value);
var c_count = parseInt(document.getElementById('c_id').value);
var aVar=setInterval(function() {
var bVar=setInterval(function() {
var cVar=setInterval(function() {
updateFields('Batch',timestamp);
i_c++;
if (i_c==c_count) {
clearInterval(cVar);
}
},100);
i_b++;
if (i_b==b_count) {
clearInterval(bVar);
}
},c_count*100);
i_a++;
if (i_a==a_count) {
clearInterval(aVar);
}
},b_count*c_count*100);
}
What I am seeing is that if I have only one loop, all works as expected. But if I add the 2nd and the 3rd loops, they will execute indefinitely.
What am I doing wrong here? Is there a better way to achieve what I'm trying to do?
I fixed my issue by simply making variables aVar, bVar and cVar global, and without changing anything else, as follows :
var aVar;
var bVar;
var cVar;
function runBatch() {
....
}

Chrome Extension with Database API interface

I want to update a div with a list of anchors that I generate from a local database in chrome. It's pretty simple stuff, but as soon as I try to add the data to the main.js file via a callback everything suddenly becomes undefined. Or the array length is set to 0. ( When it's really 18. )
Initially, I tried to install it into a new array and pass it back that way.
Is there a setting that I need to specify in the chrome manifest.json in order to allow for communication with the database API? I've checked, but all I've been able to find was 'unlimited storage'
The code is as follows:
window.main = {};
window.main.classes = {};
(function(awe){
awe.Data = function(opts){
opts = opts || new Object();
return this.init(opts);
};
awe.Data.prototype = {
init:function(opts){
var self = this;
self.modified = true;
var db = self.db = openDatabase("buddy","1.0","LocalDatabase",200000);
db.transaction(function(tx){
tx.executeSql("CREATE TABLE IF NOT EXISTS listing ( name TEXT UNIQUE, url TEXT UNIQUE)",[],function(tx,rs){
$.each(window.rr,function(index,item){
var i = "INSERT INTO listing (name,url)VALUES('"+item.name+"','"+item.url+"')";
tx.executeSql(i,[],null,null);
});
},function(tx,error){
});
});
self._load()
return this;
},
add:function(item){
var self = this;
self.modified = true;
self.db.transaction(function(tx){
tx.executeSql("INSERT INTO listing (name,url)VALUES(?,?)",[item.name,item.url],function(tx,rs){
//console.log('success',tx,rs)
},function(tx,error){
//console.log('error',error)
})
});
self._load()
},
remove:function(item){
var self = this;
self.modified = true;
self.db.transaction(function(tx){
tx.executeSql("DELETE FROM listing where name='"+item.name+"'",[],function(tx,rs){
//console.log('success',tx,rs)
},function(tx,error){
//console.log('error',tx,error);
});
});
self._load()
},
_load:function(callback){
var self = this;
if(!self.modified)
return;
self.data = new Array();
self.db.transaction(function(tx){
tx.executeSql('SELECT name,url FROM listing',[],function(tx,rs){
console.log(callback)
for(var i = 0; i<rs.rows.length;i++)
{
callback(rs.rows.item(i).name,rs.rows.item(i).url)
// var row = rs.rows.item(i)
// var n = new Object()
// n['name'] = row['name'];
// n['url'] = row['url'];
}
},function(tx,error){
//console.log('error',tx,error)
})
})
self.modified = false
},
all:function(cb){
this._load(cb)
},
toString:function(){
return 'main.Database'
}
}
})(window.main.classes);
And the code to update the list.
this.database.all(function(name,url){
console.log('name','url')
console.log(name,url)
var data = []
$.each(data,function(index,item){
try{
var node = $('<div > '+item.name + '</div>');
self.content.append(node);
node.unbind();
node.bind('click',function(evt){
var t = $(evt.target).attr('href');
chrome.tabs.create({
"url":t
},function(evt){
self._tab_index = evt.index
});
});
}catch(e){
console.log(e)
}
})
});
From looking at your code above, I notice you are executing "self._load()" at the end of each function in your API. The HTML5 SQL Database is asynchronous, you can never guarantee the result. In this case, I would assume the result will always be 0 or random because it will be a race condition.
I have done something similar in my fb-exporter extension, feel free to see how I have done it https://github.com/mohamedmansour/fb-exporter/blob/master/js/database.js
To solve a problem like this, did you check the Web Inspector and see if any errors occurs in the background page. I assume this is all in a background page eh? Try to see if any error occurs, if not, I believe your encountering a race condition. Just move the load within the callback and it should properly call the load.
Regarding your first question with the unlimited storage manifest attribute, you don't need it for this case, that shouldn't be the issue. The limit of web databases is 5MB (last I recall, it might have changed), if your using a lot of data manipulation, then you use that attribute.
Just make sure you can guarantee the this.database.all is running after the database has been initialized.

Categories

Resources