I added an Enable rule with a custom javascript rule to hide a ribbon button whenever required. It works fine, but it's called twice.
I added a console.trace() call to see what's the stack trace, and saw the following - You can see that the second call is doing a refresh.
I did another few tests, and eventually figured out that what causes the second call is a call to any of the Xrm utility functions (so that if myButtonFunc execution doesn't contains this kind of call, it won't be fired twice).
First call:
(anonymous function)
evaluate
InjectedScript._evaluateOn
InjectedScript._evaluateAndWrap
InjectedScript.evaluateOnCallFrame
myRibbonObj.myButtonFunc <-- my ribbon's button function
Mscrm.CommandHandler.$Bz_1
Mscrm.CommandHandler.$FU_1
Mscrm.CommandHandler.$By_1
Mscrm.CommandHandler.$FQ_1
Mscrm.CommandHandler.$Eb_1
Mscrm.CommandHandler.canHandleCommand
Mscrm.CommandData.isControlEnabled
Mscrm.CommandBarData.isControlEnabled
Mscrm.CommandBar.$7b
Mscrm.CommandBar.evaluateCommands
Mscrm.CommandBarBuilder.$BY_0
Mscrm.CommandBarBuilder.$BS_0
Mscrm.CommandBarBuilder.$EF_0
Mscrm.CommandBarBuilder.$Hg_0
Mscrm.CommandBarBuilder.buildCommandBar
Mscrm.RibbonManager.$Cg_3
$v_0
Second call:
(anonymous function)
evaluate
InjectedScript._evaluateOn
InjectedScript._evaluateAndWrap
InjectedScript.evaluateOnCallFrame
myRibbonObj.myButtonFunc <-- my ribbon's button function
Mscrm.CommandHandler.$Bz_1
Mscrm.CommandHandler.$FU_1
Mscrm.CommandHandler.$By_1
Mscrm.CommandHandler.$FQ_1
Mscrm.CommandHandler.$Eb_1
Mscrm.CommandHandler.canHandleCommand
Mscrm.CommandData.isControlEnabled
Mscrm.CommandBarData.isControlEnabled
Mscrm.CommandBar.$7b
Mscrm.CommandBar.evaluateCommands
Mscrm.CommandBar.refreshCommandBar
Mscrm.CommandBar.refresh
Mscrm.CommandBarBuilder.refreshCommandBar <-- note this refresh
Mscrm.RibbonManager.$Hl_3
Mscrm.RibbonManager.$Hm_3
Mscrm.RibbonManager.$Hr_3
(anonymous function)
Obviously, I want the function to execute once. I tried what's suggested here:
var triggered = false;
function RibbonButtonClick() {
CallMyFunction();
}
function CallMyFunction() {
if (triggered == false) {
triggered = true;
// ... your function body follows ...
return false; // hide the button
}
}
but it doesn't work - apparently, the button is displayed/hidden based on the return value of the second call!
So I tried the opposite - made the required logic to run on the second call:
function CallMyFunction() {
if (triggered == false) {
triggered = true;
var dummy = Xrm.Page.data.entity.getId();
} else {
// ... your function body follows ...
return false; // hide the button
}
}
but this is really hacky - it needs to have a dummy call to an Xrm function to enable the second call.
My question: Can this be resolved otherwise?
Related
I have function getData() which make an ajax call and retrive JSON data. On success i call another function which is marquee() . inside marquee on finish event i call getData() again, But each time getData() when get called, it increases it's request to mentioned file data.php, For example on first call it call once, Second call it request twice, and then twice become 4times,8times and more and more, how to avoid this?!
function getData()
{
$.get('data.php).done(function(response)
{
var data = JSON.parse(response);
if(data.Direction == "left")
{
$(".marquee").html("<span data-direction='"+data.Direction+"'>"+data.Message+"</span>");
}else if(data.Direction == "right"){
$(".marquee").html("<span data- direction='"+data.Direction+"'>"+data.Message+"</span>");
}
});
}
function marquee()
{
$(".marquee").marquee({duration : 10000}).bind("finished",function()
{
getData();
});
}
I hope i was clear... Appreciate each answer.
Every time you are calling marquee function, you are basically binding an event finished on to it. On multiple such function calls, you will have duplicate events. In your code setup, you need to unbind the function before binding it. Something like
$(".marquee").marquee({duration : 10000}).unbind("finished",getData).bind("finished",getData)
Ideally, you should bind only once so you do not have to unbind it again and again.
I am using react-redux 5.0.6 and have a reducer with the following code:
export default (state = [], action) => {
switch(action.type) {
case 'ADD_ENGAGEMENT':
let newArr = state.slice();
newArr.push(action.payload);
return newArr;
case 'UPDATE_ENGAGEMENT':
console.info('UPDATE_ENGAGEMENT')
return state.slice();
// return state;
default:
return state;
}}
The issue occurs within the 'UPDATE_ENGAGEMENT' case -- the actual logic has been removed and replaced with the simplest example to demonstrate the problem.
When a new array created from state via state.slice() is returned, a loop is triggered, causing the action to be dispatched until an 'Uncaught RangeError: Maximum call stack size exceeded' error is raised. Screenshot of the browser console during the issue's occurrence
The issue is not limited to 'slice()' and occurs whenever an array containing any element of state is returned e.g. return [state[0]].
When the original state is returned, the issue does not occur.
I am completely baffled by this behavior and cannot fathom how anything in my application could be causing it. Any insight would be immensely appreciated.
To provide some additional context, below is the code involved in the action's being dispatched:
componentWillReceiveProps(newProps) {
let engagementTemplateData = newProps.selectedEngagementTemplate;
let engagements = newProps.engagements;
if (engagementTemplateData && engagementTemplateData.engagementUuid === this.props.uuid) {
let template = engagementTemplateData.template;
this.updateEngagementTemplate(template);
}
}
updateEngagementTemplate(template) {
let url = `/engagements/${this.props.uuid}`;
let requestHelper = new AjaxRequestHelper(url);
let data = {template_uuid: template.uuid};
this.props.updateEngagement({uuid: this.props.uuid, template: template});
// requestHelper.put(data, response => {
// this.props.updateEngagement({uuid: this.props.uuid, template: template});
// });
}
Basically, the function which triggers the action is called in componentWillReceiveProps as a result of another action. However, I am not sure how helpful this information is, since the reducer itself appears to be working properly when responding to the action -- it's just that something strange is happening with the state, which prevents its elements from being returned.
From the sounds of it (and from the react callstack), I imagine the array changing (by reference) in the store is being picked up by a react component props, which in its should/did update logic is calling that action without a guard. This is often a mistake when calling actions or setState from componentDidMount/Update -
It works when the original state is returned as the reference is the same so React does not continue with its update logic, and hence call your code that publishes the action
Consider this pure component that will cause an endless loop with your reducer code...
export interface IMyProps {
myArray: any[],
updateEngagementAction: () => void
}
export class EndlessLoopFromArrayPropComponent extends React.PureComponent<IMyProps> {
// PureComponent compares props by reference only
// so will cause update if this.props.myArray reference has changed in store (ie from slice())
render() {
// blahblah...
}
componentDidUpdate() {
// this will call action after every update
// as every time this action is called it passes in a new reference this.props.myArray to this component
// so react will update this component again, causing the action to be called again
// ... endless loop
this.props.updateEngagementAction()
}
}
Your implementation will differ of course but this will be the principal that is causing it to happen, so you need to add a guard condition in whatever code path leads to your action being called.
For the code above you would need to check an escape condition before sending the action OR implement shouldComponentUpdate or similar to do deeper prop comparison to guard against unnecessary updates, and hence it would not reach that action code in the componentDidUpdate method
EDIT This was written before the react code was added to question. Here I refer to the action being called without guard in componentDidUpdate however the same applies when called in any of the other lifecycle methods triggered by a prop change, in this case componentWillRecieveProps. To be fair it did have a guard already, but never returned false as a more in-depth props check was needed, so caused a loop to occur via willreceive -> true -> action -> reducer -> willreceive -> true ........
This is my example: enter link description here
How to reload automatically?
beforeProcessing: function (data, status, xhr) {
if (data.rows === '') {
$('#jqGridPreparate').jqGrid('clearGridData');
return false;
}
if (data.inputCol) {
$("td.ui-search-input input#id_prep").val('');
$("td.ui-search-input input#id_opisanie").val(data.inputCol);
var rplc = $.parseJSON($("#jqGridPreparate")[0].p.postData.filters);
for (var i=0; i < rplc.rules.length; i++) {
if (rplc.rules[i].field === 'prep') {
rplc.rules[i].field = 'opisanie';
}
}
$.extend($("#jqGridPreparate")[0].p.postData,{filters:JSON.stringify(rplc)});
$("#jqGridPreparate")[0].triggerToolbar(); // not WORK
}
}
I'm not sure that I full understand what you want to implement. I can guess that you want to apply the filter if some additional information will returned by from the server.
It seems that you implemented some custom agreement between jqGrid and the server about returned data. rows part of the server response should be array of items. In your case the server could set empty string as the value of rows instead. You try to use inputCol property of the server response to force filtering of previously loaded data. You makes some custom modifications of existing filter, you fill some fields of the filter toolbar and finally you try to use triggerToolbar. The code is very unclear and it contains a lot of assumptions which you don't described directly. It's not clear for me whether you want to start local filtering or jqGrid should send new request to the server with modified filter.
In any way, if you want to start some kind of reloading/filtering inside of other callback function, you shuld take in consideration some basic rules. First of all the method triggerToolbar or triggering reloadGrid works synchronously. It means that jqGrid first makes triggerToolbar, which makes reloadGrid, till the end and then will be executed the next line of code after $("#jqGridPreparate")[0].triggerToolbar();. Because of that it's strictly recommended to place all calls of triggerToolbar or reloadGrid inside of setTimeout body. It allows to process the current callback til the end and only then to make reloading. If the response from the server contains your custom information instead of typical jqGrid data then you should return false from beforeProcessing to stop the standard processing.
If you can construct postData.filters then you don't need to use triggerToolbar to apply the filter. Instead of that you need just set search: true parameter of jqGrid additionally and just trigger reloadGrid.
The corresponding code could be about the following:
beforeProcessing: function (data, status, xhr) {
var $self = $(this), p = $self.jqGrid("getGridParam");
if (data.rows === "") {
$self.jqGrid("clearGridData");
return false; // don't process the data by jqGrid
}
if (data.inputCol) {
var i, rplc = $.parseJSON(p.postData.filters);
for (i = 0; i < rplc.rules.length; i++) {
if (rplc.rules[i].field === "prep") {
rplc.rules[i].field = "opisanie";
}
}
p.postData.filters = JSON.stringify(rplc);
setTimeout(function () {
// apply the filter
p.search = true;
$self.trigger("reloadGrid", [{page: 1}]);
}, 50);
return false; // don't process the data by jqGrid
}
}
Using Knockout 2.0 and MVC3 Razor forms, I am not able to make a dependent observable work when I introduce an ajax method. I have set up a set of observables that are a part of a calculation, and when I return the product of those observables, I am able to set a SPAN tag with the correct result. However, when I try to use the ajax method to handle those observables and return a result, I get unpredictable behavior. First, it appears the ajax POST does not pick up one of the observables when the INPUT fields are updated (var b POSTs to the action method as 0, but then is eventually updated), and then it seems that I am not able to set the result even when it evaluates correctly. It appears there is timing issue with either the observables or the ajax call. Although simply keeping performing the calculation in javascript works fine, my intent is to call ajax methods for more complicated logic. I have removed the call to ko.applybindings from doc.ready(), and also moved the SCRIPT methods to the bottom of the page- this was the only way I found to make this partly functional. My viewModel is set up as follows:
var viewModel = {
a: ko.observable(0),
b: ko.observable(1),
c: ko.observable(2),
// commented this out, since
// the dependent observable will handle this
// d: ko.observable(0)
};
In my dependent observable:
viewModel.d = ko.dependentObservable(function () {
var theResult = 0;
$('.theLabel').css("visibility", "visible");
theResult=viewModel.a() * viewModel.b() * viewModel.c();
// if we return here we get a valid result
return (theResult);
// prefer to call ajax method
// first check to ensure one variable is set
if (viewModel.a() > 0) {
$.ajax("/myCalculation/getResult", {
data: ko.toJSON(viewModel),
type: "post",
context: viewModel,
contentType: "application/json",
success: function (result) {
// can't set visibility here
$('.theLabel').css("visibility", "visible");
// the POST does not pick up some observables, or
// does not the set dependent observable at all
return result;
}
});
}
});
There is quite a bit wrong with the function you have setup.
1.) You are returning from your function before making your AJAX call. The code after your return statement will never execute.
2.) Even if you omit the first return statement, your AJAX call is Asynchronous... which means it will execute in the background and return control to the outer scope immediately. Since you don't have a return statement, then you are going to return undefined.
3.) The comment in your success callback suggests you are expecting the return statement to propagate all the way up to your computed observable. THAT return statement is only scoped to the callback, and not the outer observable. The return value will be used by jQuery, and your observable will long since have returned.
If you want an observable to call an AJAX function you need a seperate value to store the results of the asynchronous call.
Here is a simplified example:
var viewModel = function(){
var $this = this;
$this.a = ko.observable();
$this.b = ko.observable();
$this.results = ko.observable();
//No need to assign this computed observable to a variable
// because the results will be stored in '$this.results'
// we just need this to handle the automatic updates
ko.computed(function(){
var data = {
a: $this.a(),
b: $this.b()
};
$.post("/do/some/stuff", data, function(results){
$this.results(results);
});
});
};
I'm trying to make two Ajax calls to get data to populate different bits of a web page, and as you'll already know, only the second happens.
So I thought I'd do this:
callAjax1('a'); callAjax2('b');
function callAjax1(data) {
ajax(data);
}
function callAjax2(data) {
ajax(data);
}
function ajax(data) {
// calls XMLHttpRequestObject etc
}
The idea was that instead of calling ajax() twice, now, I'd have two independent instances of ajax that would run independently.
It works .. but only if I put in an alert at the top of ajax() to let me know I've arrived.
So I'm thinking that alert gives the first request time to finish before the second is called. Therefore, I've not managed to separate them properly into separate instances. Is that not possible?
What am I missing?
All the best
J
UPDATE:
I'm thinking this, do I stand a chance?
tParams = new Array (2); // we intend to call ajax twice
tParams[0] = new Array('ajaxGetDataController.php', 'PROJECT', 'id');
tParams[1] = new Array('ajaxGetFileController.php', 'FILE', 'projectId');
<select name='projectSelector' onchange=\"saveData(tParams, this.value);\">\n";
// gets called, twice
function saveData(pParams, pData) // pParams are: PageToRun, Table, Field
{
if (XMLHttpRequestObject)
{
tPage = pParams[0][0]+'?table='+pParams[0][1]+'&pField='+pParams[0][2]+'&pData='+pData;
XMLHttpRequestObject.open('GET', tPage);\n
XMLHttpRequestObject.onreadystatechange = callAjax(pParams, pData);
XMLHttpRequestObject.send(null);
}
}
function callAjax(pParams, pData)
{
if (XMLHttpRequestObject.readyState == 4 && XMLHttpRequestObject.status == 200)
{
var tReceived = XMLHttpRequestObject.responseXML;
options = tReceived.getElementsByTagName('option'); // fields and their values stored in simplest XML as options
popForm(options, pParams[0][1]); // goes off to use the DOM to populate the onscreen form
pParams.shift(); // cuts off pParams[0] and moves all elements up one
if (pParams.length>0)
{
saveData(pParams, pData);
}
}
}
I would create a ready state variable for the AJAX function:
function ajax(data) {
readyState = false;
// calls XMLHttpRequestObject etc
}
And then check for the ready state before executing the second call:
function callAjax2(data) {
if(readyState == true) {
ajax(data);
readyState = true;
}
}
And make sure to change the readyState back to false after the AJAX calls have executed. This will ensure the first AJAX call has finished executing before the second one tries to fire.