The onChange event for a field on my opportunity form is being called twice and I'm trying to track down the source of the second call. I've already passed the execution context into the onChange function but don't know any way to see the source of the call from there. Now I'm wondering if there's a way to see the pending events for an XRM page, does anyone know where that information is?
My code is basically this, tied to onLoad of the opportunity form. There could be a plugin or 3rd party library or something updating the field, but nothing jumps out.
function onLoad() {
Xrm.Page.data.process.addOnStageChange(handleStageChange);
}
function handleStageChange() {
var dateFieldName = "new_enteredstage1"
var dateFieldAttr = Xrm.Page.getAttribute(dateFieldName);
if (dateFieldAttr) {
dateFieldAttr.setValue(new Date());
}
}
If new_enteredstage1 is null when the stage change occurs then 2 calls occur and the value is set and immediately reset to null. If new_enteredstage1 has a value the value is updated as expected. Again, there could be some third party code that I'm missing but I have no idea how to track it down.
UPDATE:
This only happens on date fields, and it happens on all date fields. If I replace the code with a number field the value is not reset to null.
There's a know issue (example) related to the "new" form rendering engine (Turbo Forms) that can cause this issue.
You can try using the legacy form rendering engine and see if it solves the problem:
Related
When I register an OnChange function to a field, it gets fired if the field is updated on a plugin on the server side (On Dynamics Crm 2015)
Can this behavior be prevented (In a supported way)?
Example code:
1. Client side:
Xrm.Page.getAttribute("org_myfield").addOnChange(function () { alert("org_myfield was changed") });
2. Server side:
internal void OnPreUpdateRequest(org_myentity target, org_myentity preImage)
{
target.org_myfield = "some value";
}
3. Result (after saving the record and plugin finished its run):
"org_myfield was changed"
Required result: The alert should not be fired.
I believe this is what you are trying to get:
1. When user makes change on this field, call the JavaScript on change function;
2. When the record is saved, a plugin might change the value of the field as well;
3. If the field value is altered by the plugin, skip the JavaScript function.
If this is correct, I would try this way:
Remove the onChange event from the field, instead check the field status in onSave event of the form.
Use Xrm.Page.getAttribute("org_myfield").getIsDirty() to determine whether the user makes any change on the field. It only checks the changes on client side so it won't be affected by plugin.
Currently I am working on a scenario that I need to change the data attached to a dragging element after the dragging starts. Basically the drop zones are input fields or textareas so i would like to use the native event.dataTransfer.setData since the native drag-drop can make the caret move with mouse.
Everything works perfectly at the beginning if i just call the setData() synchronously in the listener of dragstart event.
dragItem.addEventListener("dragstart",function(event){
event.dataTransfer.setData("text/plain","data set in dragstart");
})
However, my scenario could be that the data is from an asynchronouly callback function like an AJAX request. Then I attempted to call setData() in this callback function but nothing seems to be successfully set.
dragItem.addEventListener("dragstart",function(event){
event.dataTransfer.setData("text/plain","data set in dragstart");
//like a callback in Ajax or resolve function of a promise.
setTimeout(function(){
console.log("attempt to set data asynchonrously after drag start");
event.dataTransfer.setData("text/plain","asynchonrously set data");
//looks like failed, the console output is empty, even not the original set one
console.log(event.dataTransfer.getData("text/plain"));
},200)
})
I also tried to change the data in dragenter and even drop event listeners of the drop zones. But there was still no luck.
This plunker shows what i have tried.
Then i referred to the MDN API document to find offical api description of the dataTransfer object. But there is nothing about problems like asynchronously using setData after drag start. One really weird thing is that if i try to compare the two dataTransfer references in dragstart and drop event, the are NOT the same object. Now I have no clue what is actually happening.
So my questions are
Is it possible to set the data in dataTransfer after the dragging is started with the native APIs (without using event.preventDefault) ?
If the first question's answer is NO, what kind of workaround could I try? I could think of something about how to save and get the data to transfer. My main concern is that if event.preventDefault() is used on drop, it is not easy to get the caret move with mouse like the native dropping does.
Here is your answer.
Firstly,You can only set data in your dragstart event. So, every time any dragstart event starts it sets value and what ever you set asynchronouly will not get reflected no matter what.
So, one thing that you can do is have a global object and set that on drag start event like this:
var someObj = {
asd : 'something'
}
and set in you dragstart callback, like this:
dragItem.addEventListener("dragstart",function(event){
event.dataTransfer.setData("text/plain", someObj.asd);
dataTransferObject = event.dataTransfer;
setTimeout(function(){
console.log("attempt to set data asynchonrously after drag start");
//event.dataTransfer.setData("text/plain","asynchonrously set data");
someObj.asd = 'asynchonrously';
//looks like failed, the console output is empty
console.log(event.dataTransfer.getData("text/plain"));
}, 100)
})
Object is by default reference type.
Now, You can set someObj.asd to any value you want and you will see new value reflected. But there is a problem with this approach, value will be reflected after drop happens means after event has been ended.
So, to solve your problem what you can do is don't set any value on dragstart just set some value to someObj.asd on drag start and use someObj.asd on drop.
Here is a link of what i'm trying to explain:
https://plnkr.co/edit/SZhI9lGRI37eEd1nWfhn?p=preview
see console on drop event you will see reflected value there.
DON'T SEE UI JUST GO FOR CONSOLE
I'm currently working on an app which has brand_field_id as one of it's parameters. It will hold the Custom Ticket Field ID for "Brand". The thing is: it is not required as the user might want or not to use the functionality it provides. Expected behaviour:
If this field is filled with an ID, an event listener for it's .change will be added on the custom field (and stuff will get done);
If it is left blank, nothing should happen. It means user opted to not use it.
Zendesk already provides the perfect listener for Custom Ticket Field change event, which works like this (dynamically using brand_field_id):
events: {
"ticket.custom_field_ticket.custom_field_{{brand_field_id}}.changed": "myCustomFieldChangedFunction"
}
... aaand it would be really awesome if the app didn't crash when brand_field_id is empty.
Since I don't want the app crashing if brand_field_id is left blank, I figured to validate it before adding the event listener, but didn't manage to get it done. I've tried a few variations of the following code within app.created event, but all unsuccessful.
if(!_.isEmpty(this.setting('brand_field_id'))){
console.log('Brand change event added');
this.events['ticket.custom_field_ticket.custom_field_{{brand_field_id}}.changed'] = 'brandChangedHandler';
}
console.log gets fired, so the validation is ok. Unfortunately the event never fires for the custom field.
So my question is: how can I add an event listener on the go? Or is there another way to achieve what I need?
I've even posted about this in Zendesk's community on a similar thread but no answers so far. The workaround I've found there actually works, but I'm not very fond of it:
events: {
// [...]
'*.changed': 'anyFieldChangedHandler'
},
// [...]
anyFieldChangedHandler: function(e){
// If the changed field was {brand_field_id}
if(!_.isEmpty(this.setting('brand_field_id')) && e.propertyName === 'ticket.custom_field_'+ this.setting('brand_field_id')){
this.brandChangedHandler(e);
}
},
It's much too broad and will fire whenever any field is changed on the ticket. I would like to find a better, cleaner and more elegant solution.
I'm in the same boat as I am aware of how difficult it is to find answers related to the Zendesk Apps framework.
I would do something like this
events: {
"ticket.custom_field_ticket.custom_field_{{brand_field_id}}.changed":"itChanged"
},
itChanged: function(){
if(ticket.custom_field_ticket.custom_field_{{brand_field_id}} !== null && ticket.custom_field_ticket.custom_field_{{brand_field_id}} !== undefined){
//Your custom field exists and has a value. Do something with it.
}
else{
//Your custom field does not exist and/or does not have a value. do nothing
}
}
Would something like this work for your app? I'm not sure if this would have the same functionality as !_.isEmpty() but you might give it a try. I use this a lot in my app sometimes just as a safe guard incase my app loads faster than the actual ticket, which causes some values to be "undefined" at the time the app loads.
Hope this helps
I am writing javascript code to change the form of a entity in Dynamics CRM based on the value of a field on each form.
To change the form, the user has to change the value of the field.
Then during the onChange event, my js comes in, triggers saving, has to wait for the result and then change the form. (If you save and change at the same time, there is still a window shown asking the user to confirm leaving unsaved changes)
Now there should be a way to do that:
Xrm.Page.data.save(saveOptions).then(successCallback, errorCallback)
as it is described on msdn:
Saves the record asynchronously with the option to set callback functions to be executed after the save operation is completed.
I am using it as such:
var campaignType = Xrm.Page.getAttribute('typecode').getValue();
if (xxx.Forms.hasOwnProperty(campaignType)) {
Xrm.Page.data.save().then(function () { xxx.redirectToForm(xxx.Forms[campaignType]); }, null);
But the form change is still triggered immediately during the save.
What am I doing wrong?
I faced a similar problem while trying to update the process bar.
Xrm.Page.data.save().then
(function () {
window.location.reload(true);
},
function () {
windows.alert("broken");
}
);
I strongly suggest you to try to apply the logic on a vanilla CRM, for me what was breaking the logic was a third party component called N52 Rules, their code was interfering with the callback forcing the refresh of the page before the save event. Your code seems correct.
Hey the Save and Refresh Calls are Asynchronous! that is why it hits the success handler immediately.
What you can try is using SDK.REST.js file for CRM
function updateFunction(entityId) {
var campaignType = Xrm.Page.getAttribute('typecode').getValue();
if (xxx.Forms.hasOwnProperty(campaignType)) {
var entity= {};
entity.typecode= campaignType;
SDK.REST.updateRecord(
entityId,
entity,
entityName, //"Account"
function () {
writeMessage("The record changes were saved");
xxx.redirectToForm(xxx.Forms[campaignType]);
},
null
);
}
}
https://msdn.microsoft.com/en-us/library/gg334427(v=crm.7).aspx
Here you can call updateFunction given above onChange and in the onSuccess handler you can try calling the form you want to call. I haven't tried it the way you want, but let me know if it works.
check this link out as well
https://msdn.microsoft.com/en-us/library/gg334720.aspx#BKMK_entityOnSave
I have a Dojo SubmitButton with jsId="saveParamButtonWidget". I overrided its onClick method by putting:
saveParamButtonWidget.onClick = editParam
I defined the editParam() function like this:
function editParam(eventObj) {
dojo.stopEvent(eventObj);
// ...
}
dojo.stopEvent() is supposed to stop event bubbling and default processing. However, the browser will submit the form anyway. I also tried with the following:
function editParam(eventObj) {
eventObj.stopPropagation();
eventObj.preventDefault();
// ...
}
Same thing. The only way I've managed to prevent form submission is by returning "false" from the event handler:
function editParam(eventObj) {
// ...
return false;
}
Can someone tell me why the first two ways did not work? Thanks.
Okay, after doing some digging through the source, I believe I can answer your question definitively.
The reason dojo.stopEvent() doesn't work, but return false does, is entirely due to how dijit.form.Button is coded. If you're interested, it's time for a little field trip. Keep your hard hats on.
When a dijit.form.Button is clicked...
The button's _onButtonClick method is invoked. (This is hooked up in the template, to the special ondijitclick event which captures not only mouse click but also certain keypresses, for a11y purposes.)
The _onButtonClick method first invokes the _onClick method, which, presuming the button is not disabled (which it's not in this case), invokes and returns the result of the onClick method. This is of particular interest since it's the method you're overriding!
Coming back to _onButtonClick, if _onClick returned precisely false (e.g. if your onClick handler returned false), _onButtonClick immediately bails out. This is why returning false makes your code work as desired. But what happens if it doesn't bail out there? Let's follow the trail further...
Next, _onButtonClick checks whether this button not a descendant of an actual HTML form, but is a descendant of a widget with an _onSubmit method (duck-typing). I'm assuming that in your case it is inside a real form (dijit.form.Form counts), so we'll skip over this. (I am under the impression that this code path wouldn't actually end up submitting, whereas yours apparently does.)
One final condition is checked: if the button has a valueNode defined (it does), the click method of this node is invoked. Unfortunately, this produces an entirely new event object on an invisible input type="submit" node under your form, and thus anything you tried to tell the original event is rendered immaterial, and the form goes on to submit! This is why dojo.stopEvent did not work - this code in dijit.form.Button pays it absolutely no heed.
I cooked this up as a somewhat-limited proof of concept (be sure to open firebug/etc. to get the logs): http://jsfiddle.net/Bf5H8/
Perhaps this is something that should be logged as a bug, but I suppose the initial thought may have been that supporting the well-known return false mechanism would be enough.
All this being said, it's quite possible that overriding onSubmit of the form is more in-line with your interests than overriding the button's onClick anyway (as S.Jones suggested), but at least this should solve the mystery.
Interesting question. +1
I believe you have to use dojo.connect to connect your function to a DOM event to get access to those methods with an event object.
See: The Event Object (DojoTollkit.org Reference Guide)
The Event Object
When you connect a function to a DOM
event with dojo.connect,
Dojo passes your function a normalized
event object. This means that,
regardless of the client's browser,
you can count on a set of standard
attributes about the event and a set
of methods to manipulate the event.
Assume that your function has been
called by dojo.connect and takes an
argument named event, like:
dojo.connect(dojo.byId("node"), "onclick", function(event){
// the var 'event' is available, and is the normalized object
});
...
Dojo normalizes the following methods with an event object:
event.preventDefault — prevent an event's default behavior (e.g., a link from loading a new page)
event.stopPropagation — prevent an event from triggering a parent node's event
Additionally, dojo.stopEvent(event)
will prevent both default behavior any
any propagation (bubbling) of an
event.
That said, placing a function like the one below in your form to perform some logic before submitting it, is a fairly clean, easily understood & maintainable approach.
<script type="dojo/method" event="onSubmit">
if (!this.validate()) { // or whatever else you'd like to evaluate
// insert calls here...
return false;
}
return true;
<script>
Cheers.
I had the same issue for using dojo.stopEvent
This issue is solved the form submission issue like this - here it is a simple form used to connect through dojo:
this.formId = dojo.byId("formId");
dojo.connect(this.formId, 'onsubmit', function(evt) {
var val_main = validate_this_form(0);
if(val_main == false)
dojo.stopEvent(evt);
});