I have form and button on that form in CRM 2015. By clicking on button on form, user triggers on-demand workflow. When workflow is done, it updates value of one of the fields on the form. However, this server data change is not reflected on the user UI.
What is the best way to register JS callback which will refresh form if workflow execution is successful?
Reading this: https://msdn.microsoft.com/en-us/library/gg334701.aspx it looks like I can't use OnChange() event, because I change data programatically.
First of all I would suggest to use Sync Workflow. After workflow is executed just execute following code:
Xrm.Page.data.refresh(false);
I had such requirements once. I had to reflect changes on the Form that are changed by the Async Workflow, and for some reason i had to keep the Workflow Async.
Following is a work-around i did for requirements like this.
Add a new field to the entity, on which the workflow is executed.
FieldName: "isworkflowexecutedsuccessfully"
FieldType: "TwoOption"
Default Value: "false"
Then in your code where you have written the workflow code, write this:
function someFunctionOfYours() {
RunWorkflow(); //
WaitForWorkflowToCompleteProcessingAndThenReload();
}
function isWorklowExecutionCompleted(TimerId, updateIsWorkflowExecutedSuccessfully) {
var entityName = Xrm.Page.data.entity.getEntityName();
var entityGuid = Xrm.Page.data.entity.getId();
var retrievedOpportunity = XrmServiceToolkit.Soap.Retrieve(entityName, entityGuid, new Array("isworkflowexecutedsuccessfully")); //synchronous call
if (retrievedOpportunity.attributes["isworkflowexecutedsuccessfully"].value = true) {
clearInterval(TimerId);
setTimeout(function () {
setIsworkFlowExecutedSuccessfullyToFalse(updateIsWorkflowExecutedSuccessfully);
}, 3000);
}
}
function WaitForWorkflowToCompleteProcessingAndThenReload() {
var TimerId = setTimeout(function () {
isWorklowExecutionCompleted(TimerId);
}, 5000);
}
function setIsworkFlowExecutedSuccessfullyToFalse(updateIsWorkflowExecutedSuccessfully) {
var entityName = Xrm.Page.data.entity.getEntityName();
var entityGuid = Xrm.Page.data.entity.getId();
var updateOpportunity = new XrmServiceToolkit.Soap.BusinessEntity(entityName, entityGuid);
updateOpportunity.attributes["isworkflowexecutedsuccessfully"] = false;
if (updateIsWorkflowExecutedSuccessfully == false || updateIsWorkflowExecutedSuccessfully == null) {
XrmServiceToolkit.Soap.Update(updateOpportunity);
}
Xrm.Utility.openEntityForm(entityName, entityGuid) //refresh form
}
Related
In context of one of my assignment I was trying to use RappidJS. I was trying the below code for connecting an element by dropping it over another element.
https://resources.jointjs.com/tutorial/connecting-by-dropping
But I am getting exception as below
rappid.min.js:14 Uncaught TypeError: Cannot read property 'start' of null
at child.update (rappid.min.js:14)
at child.onRender (rappid.min.js:14)
at child.e.render (rappid.min.js:14)
at child.configure (rappid.min.js:14)
at child.addTools (rappid.min.js:14)
at child.element:pointerup (diagram.js:106)
at triggerEvents (backbone.js:338)
at triggerApi (backbone.js:322)
at eventsApi (backbone.js:110)
at child.Events.trigger (backbone.js:312)
I also took reference from below link
https://resources.jointjs.com/tutorial/link-tools .
Can any one suggest me what wrong I am doing here.
My code snippet is as below
function attachLinks(paper) {
paper.on({
'element:pointerdown': function (elementView, event) {
event.data = elementView.model.position();
},
'element:pointerup': function (elementView, event, x, y) {
var coordinates = new joint.g.Point(x, y);
var elementAbove = elementView.model;
var elementBelow = this.model.findModelsFromPoint(coordinates).find(function (el) {
return (el.id !== elementAbove.id);
});
// If the two elements are connected already, don't
// connect them again (this is application-specific though).
if (elementBelow && self.graph.getNeighbors(elementBelow).indexOf(elementAbove) === -1) {
// Move the element to the position before dragging.
elementAbove.position(event.data.x, event.data.y);
// Create a connection between elements.
var link = new joint.shapes.standard.Link();
link.source(elementAbove);
link.target(elementBelow);
link.addTo(this.model);
// Add remove button to the link.
var removeLinkTool = new joint.linkTools.Remove();
var tools = new joint.dia.ToolsView({
tools: [ removeLinkTool]
});
var linkView = link.findView(this);
linkView.addTools(tools); // getting exception at this place.
}
}
});
}
This issue is reproducible only in the paper async mode.
Make sure the view for the link is rendered when you add the link tools. For this please use joint.dia.Paper.prototype.requireView().
// It is fine to use the method in the `sync` mode as the view is rendered/updated
// synchronously as soon as the link is added to the graph.
var linkView = link.findView(paper);
// This method makes sure the view is ready to use in the `async` mode.
// It forces the view to render/update synchronously.
var linkView = paper.requireView(link);
Alternatively, you could pass async = false flag when adding the link to the graph to tell the paper to render the particular view synchronously.
link.addTo(graph, { 'async': false });
I am trying to execute some code every time a specific fabric object is "deselected". Is there any deselection event I can handle? I already have a function for when the object is selected, via the selected event, but have not found any documentation about the deselected one. At the canvas level I have the selection:cleared and selection:created events, but nothing for the deselection either.
Cheers,
Gonzalo
Use the before:selection:cleared event and get the active object or group. After that you can check if it corresponds to your specific fabric object.
canvas.on('before:selection:cleared', function() {
var clearedObject;
if(typeof(canvas.getActiveObject()) !== 'undefined') {
clearedObject = canvas.getActiveObject();
}
else {
clearedObject = canvas.getActiveGroup();
}
//do stuff with the deselected element if it is the specific one you want.
});
Just to let you all know that the newest versions of Fabric.js include a deselected event for the Object class. The only thing you need to do now is:
var aFabricObject = <create your fabric object>
aFabricObject.on('deselected', function (options) {
// your code here
});
Update for the upper answer:
The 'deselected' event fires a async method, so you can not get 'options.deselected' immediately.
var aFabricObject = <create your fabric object>
aFabricObject.on('deselected', function (options) {
console.log(options.deselected) // output undefined
setTimeout(() => {
console.log(options.deselected) // output the correct target
}, 0)
});
I am using the source at http://blog.symprogress.com/2010/11/ribbon-insert-any-web-part-using-javascript/ to handle user web part button click event.
The function 'addWebPart()' calls a function 'SP.Ribbon.WebPartComponent.getWebPartAdder()' which is supposed to return adder instance but sometimes it returns undefined.
If I add a while loop to wait for the instance value to return correctly, the browser in my VM stalls for some time. When an instance is returned, the browser becomes responsive again. This only happens in some instances.
I am using SharePoint 2013 and the section of code I am referring to is:
addWebPart = function (wpCategory, wpTitle) {
var webPartAdder = SP.Ribbon.WebPartComponent.getWebPartAdder();
while (webPartAdder == undefined)
webPartAdder = SP.Ribbon.WebPartComponent.getWebPartAdder();
// ... Other stuff ...
}
How can this issue be resolved?
For anyone looking for an answer to this question, turns out you have to call 'LoadWPAdderOnDemand()' function then wait for the event '_spEventWebPartAdderReady'. Then query for 'window.WPAdder':
addWebPartDelayed = function (webPartAdder, wpCategory, wpTitle) {
var webPart = findWebPart(webPartAdder, wpCategory, wpTitle);
if (webPart) {
var zone = WPAdder._zones[0];
var wpid = WPAdder._createWebpartPlaceholderInRte();
WPAdder.addItemToPageByItemIdAndZoneId(webPart.id, zone.id, 0, wpid);
}
else
alert('ERROR: Web part not found! Please try again after sometime.');
},
addWebPart = function (wpCategory, wpTitle) {
var webPartAdder = window.WPAdder;
if (webPartAdder == undefined) {
LoadWPAdderOnDemand();
ExecuteOrDelayUntilEventNotified(
function () {
var webPartAdder = window.WPAdder;
addWebPartDelayed(webPartAdder, wpCategory, wpTitle);
},
"_spEventWebPartAdderReady");
}
else
addWebPartDelayed(webPartAdder, wpCategory, wpTitle);
};
My WinJS app uses the single navigation model. There is some common code that I would like to apply to every page in the app. Instead of placing the code in each page's ready function, I would like to be able to able to define a "global" ready function that will be executed when a page's ready event is fired. Any ideas?
you can define a Mixin object with utility function used for all pages.
utils.js:
PageMixin = {
ready: function ready(element, options)
{
this.element = element;
this.options = options;
this.initialize();
this.onready();
},
initialize: function initialize()
{
// write common initialize code here
}
};
page.js:
var Page = WinJS.UI.Pages.define('/pages/mypage/page.html',
{
onready: function onready()
{
// page specific initialization code here
}
});
// this will make all PageMixin util methods available on Page.
WinJS.Class.mix(Page, PageMixin);
refer WinJS.Class.mixin for details.
I would like to use chrome.storage API to save the settings of my users instead of localStorage in my Chrome extension.
Currently my options.js (with localStorage and JSON) file looks like this:
$(function(){ //jQuery Ready
// INIT
$("#notifysav").hide();
// STORAGE
if(localStorage['options']){
var o = JSON.parse(localStorage['options']);
$("#option1").attr('checked', o.option1);
$("#option2").attr('checked', o.option2);
.... [list of options]
}
// Save Button Click event
$("#save").live('click', function(){
localStorage['options'] = JSON.stringify({
"option1":$("#option1").attr('checked'),
"option2":$("#option2").attr('checked'),
.... [list of options]
});
// notification
$("#notifysav").fadeIn("slow").delay(2000).fadeOut("slow");
// reload to apply changes
chrome.extension.sendRequest({action: 'reload'});
});
});// #jQuery ready
My question is how to convert my current code to use the chrome.storage API. From what I understand, I would apply those changes:
$(function(){
// INIT
var storage = chrome.storage.sync;
$("#notifysav").hide();
// Load Options
loadOptions();
// Save Button Click Event
$("#save").live('click',function(){ saveOptions(); });
function loadOptions() {
storage.get( /* Something */ )
}
function saveOptions() {
var option1 = $("#option1").attr('checked');
var option2 = $("#option2").attr('checked');
storage.set({"option1":option1,"option2":option2}, function() {
// Notification
$("#notifysav").fadeIn("slow").delay(2000).fadeOut("slow");
// Reload Event to apply changes
chrome.extension.sendRequest({action: 'reload'});
});
}
});
Thanks for your help!
If I understand correctly, your main problem is how to retrieve data from the storage. Here is what can be done:
chrome.storage.local.get(null, function(all){console.log(all)});
will return you an object with all keys and values stored in the storage, in your case it will output:
Object {option1: "value1", option2: "value2"}
Also you can get just one of the keys:
chrome.storage.local.get("optionkey", function(all){console.log(all)});
or an array of keys:
chrome.storage.local.get(["opt1", "opt2"], function(all){console.log(all)});
In any case you can access data in the callback just by key names.
Thanks for your reply. I finally managed to get something working by changing my original code as follows:
$(function(){
// INIT
const storage = chrome.storage.sync;
var options = new Array();
$("#notifysav").hide();
loadOptions();
// STORAGE
// Save Button Click event
$("#save").live('click', function(){ saveOptions(); });
function loadOptions() {
storage.get('options', function(o) {
$("#option1").attr('checked', o.options[0]);
$("#option2").attr('checked', o.options[1]);
...
});
}
function saveOptions() {
options[0] = $("#option1").prop('checked');
options[1] = $("#option2").prop('checked');
...
//console.log(options);
storage.set({'options':options},function(){
$("#notifysav").fadeIn("slow").delay(2000).fadeOut("slow");
chrome.extension.sendRequest({action: 'reload'});
});
}
});