We have a requirement to refresh the form after saving (to ensure that some hide/show logic works as expected based on a field value).
Currently the form does not automatically refresh after saving the record.
I have gone through some articles and found this reference:
http://msdn.microsoft.com/en-us/library/dn481607%28v=crm.6%29.aspx
When I try to do either of the below, it results in an infinite loop and throws a 'Callstack exceeded Max limit' error.
OnSave(context)
{
//my logic
...
...
Xrm.Page.data.save.then(SuccessOnSave, ErrorOnSave);
}
function SuccessOnSave()
{
//force refresh
Xrm.Page.data.refresh();
}
function ErrorOnSave()
{
//do nothing
}
OnSave(context)
{
...
...
//force refresh
Xrm.Page.data.refresh(true).then(SuccessOnSave, ErrorOnSave);
}
function SuccessOnSave()
{
//do nothing
}
function ErrorOnSave()
{
//do nothing
}
Can someone please explain me how to use the refresh or save method to do a force refresh of the form ??
Rajesh
I use to achieve it with following code
Xrm.Page.data.save().then(
function () {
Xrm.Page.data.entity.attributes.forEach(function (attribute, index) {
attribute.setSubmitMode("never");
});
Xrm.Utility.openEntityForm(Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId());
},
function (errorCode, message) {
}
);
For me this is what solved the purpose (CRM 2015)
// Save the current record to prevent messages about unsaved changes
Xrm.Page.data.entity.save();
setTimeout(function () {
// Call the Open Entity Form method and pass through the current entity name and ID to force CRM to reload the record
Xrm.Utility.openEntityForm(Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId());
}, 3000);
If you want to do a hard refresh on the form data, you will likely want to do a location reload. What I've done in the past is put the refresh logic in a function that is called when the Form is loaded (after being saved). The tricky part about this is that the function can get called if the form is auto-saved in CRM 2013. You also want to take into account that you don't want to refresh the form on the first load, since this would result in an infinite reloading loop. Here's an example:
var formLoaded = false;
function formLoad() {
if (formLoaded) {
window.location = location.href;
}
formLoaded = true;
}
You have attached the OnSave() method to OnSave event. So, logically if you call save again within the same event, the calls goes recursively.
From MSDN
Xrm.Page.data.refresh(save).then(successCallback, errorCallback);
Parameter: save - A Boolean value to indicate if data should be saved
after it is refreshed.
So, you will have to pass 'false' to this method (You just need to refresh, no save is required)
As I couldn't find the complete code for this written in a 'generically' reusable way, here goes:
var triggeredSave = false;
//Attach the OnSave Form event to this OnSave function
//and select passing of context as the first parameter.
//Could instead be attached programmatically using the code:
//Xrm.Page.data.entity.addOnSave(OnSave);
function OnSave(context) {
var eventArgs = context.getEventArgs();
var preventedAutoSave = false;
//Preventing auto-save is optional; remove or comment this line if not required.
preventedAutoSave = PreventAutoSave(eventArgs);
//In order to setup an after save event function, explicitly
//invoke the save method with callback options.
//As this is already executing within the OnSave event, use Boolean,
//triggeredSave, to prevent an infinite save loop.
if (!preventedAutoSave && !triggeredSave) {
triggeredSave = true;
Xrm.Page.data.save().then(
function () {
//As the form does not automatically reload after a save,
//set the save controlling Boolean, triggeredSave, back to
//false to allow 'callback hookup' in any subsequent save.
triggeredSave = false;
OnSuccessfulSave();
},
function (errorCode, message) {
triggeredSave = false;
//OPTIONAL TODO: Response to failed save.
});
}
}
function PreventAutoSave(eventArgs) {
if (eventArgs.getSaveMode() == 70 || eventArgs.getSaveMode() == 2) {
eventArgs.preventDefault();
return true;
}
return false;
}
//Function OnSuccessfulSave is invoked AFTER a save has been committed.
function OnSuccessfulSave() {
//It seems CRM doesn't always clear the IsFormDirty state
//by the point callback is executed, so do it explicitly.
Xrm.Page.data.setFormDirty(false);
//TODO: WHATEVER POST SAVE PROCESSING YOU REQUIRE
//e.g. reload the form as per pre CRM 2013 behaviour.
ReloadForm(false);
//One scenario this post save event is useful for is retriggering
//Business Rules utilising a field which is not submitted during save.
//For example, if you implement a Current User field populated on Form
//Load, this can be used for user comparison Business Rules but you
//may not wish to persist this field and hence you may set its submit
//mode to 'never'.
//CRM's internal retriggering of Business Rules post save doesn't
//consider changes in fields not submitted so rules utilising them may
//not be automatically re-evaluated or may be re-evaluated incorrectly.
}
function ReloadForm(preventSavePrompt) {
if (preventSavePrompt) {
Xrm.Page.data.entity.attributes.forEach(function (attribute, index) {
attribute.setSubmitMode("never");
});
Xrm.Page.data.setFormDirty(false);
}
Xrm.Utility.openEntityForm(Xrm.Page.data.entity.getEntityName(),
Xrm.Page.data.entity.getId());
//Another way of trying Form reload is:
//window.location.reload(true);
}
Use Mscrm.Utilities.reloadPage();
I found this post helpful in demonstrating the difference between Xrm.Page.data.refresh() and Xrm.Utility.openEntityForm(entityName, id).
TL;DR - if you want the screen to repaint, consider using Xrm.Utility.openEntityForm(entityName, id).
You can achieve by placing Modified On field on the form. And set visible by default property to false.
Use below JS to refresh the form
function refreshCRMFORM()
{
setTimeout(function () {
// Call the Open Entity Form method and pass through the current entity name and ID to force CRM to reload the record
Xrm.Utility.openEntityForm(Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId());
}, 1000);
}
Create On Change event on Modified on field and provide above function name.
Related
I have a working Active Form, which can be submitted, and validated via Yii PHP. However, I would like to determine if the form is valid, when clicking a Next button.
I can pass error messages to the user via this:
$("#form").yiiActiveForm("validate", true);
But this function doesn't return anything; I don't know if there are indeed any errors or not. I tried this:
$error_count = document.getElementsByClassName("help-block").length
but this does not work; the errors are counted before the UI has updated. If I press the button again, a second time, then error_count is what I'd expect.
This doesn't seem to do anything:
$("#form").yiiActiveForm("validate");
I also tried this:
$('#form').on('afterValidate', function (event, messages, errorAttributes) {}
But this is only triggered after the fact so I'm not sure what to do.
Any advice would be appreciated.
If you need to react to button you simply need to combine both events:
let isNextClicked = false;
$('.next-btn').on('click', function(e) {
e.preventDefault();
isNextClicked = true;
$("#form").yiiActiveForm("validate", true);
});
$('#form').on('afterValidate', function(event, messages, errorAttributes) {
if (!isNextClicked) {
//validation wasn't triggered by clicking next
return;
}
// reset the state
isNextClicked = false;
if (errorAttributes.length > 0) {
//there are errors so we won't let user to continue
//we can also show some alert() here
return;
}
// ... code to show the next tab of form ...
});
I am using jquery dirrty plugin to check the state of a form and prevent it from reloading if there are some unsaved changes.
Link to jquery.dirrty github
Flow:
1) I have initialized jquery.dirty as follows
$(function(){
initializeDirtyForm();
})
/** dirty form initialization*/
function initializeDirtyForm(){
$("#uAForm").dirrty().on("dirty", function(){
$("#uAFormSubmit").removeAttr("disabled");
}).on("clean", function(){
$("#uAFormSubmit").attr("disabled", "disabled");
});
}
2) There is a table on the page and when it click on the td cells it make an ajax call.
function ajaxCallEdit()
{
var result = checkDirtyStatus();
if(result === false) {
return;
}
$.ajax({
....
....
....
....
success:function(){
initializeDirtyForm();
}
)}
}
/** check if the form is dirty */
function checkDirtyStatus(){
dirtyStatus = $("#uAForm").dirrty("isDirty");
if(dirtyStatus == true){
if (confirm("Changes you made may not be saved. Do you still want to reload?")) {
return true;
}else{
return false;
}
}
}
If the form is dirty, it works totally fine and show warning message.
ISSUE
Here is the main issue, if I force it to reload, (this means it won't actually reload page, instead it would make an ajax call) it is still setting the status of the form as dirty. I have tried by re-initializing the jquery form but still the form is flagged as dirty.
To make it extra sure and actually what is the the status of the form, I checked the status and tried to console log on ajaxSuccess.
....
success: function(){
initializeDirtyForm();
var result = checkDirtyStatus();
console.log(result);
}
...
However, consoling this result showing the value as undefined.
I could not find any documentation related to the setting it manually and reinitalizing is not working as intented.
So, if you are javascript wizard could you please check the jquery.dirrty.js file attached above and check if I could trigger following part from the js file or any other hacks that helps me to solve the problem.
setClean: function(){
this.isDirty = false;
this.history[0] = this.history[1];
this.history[1] = "clean";
}
If you need any further details please let me know.
try $("#uAForm").dirrty("setClean");
I just got into needing to do the same thing and that worked for me.
Cheers
I have an action that I want to allow only to an authenticated user. If a user who hasn't yet logged in clicks this link, the Webserver will return a not-logged-in response. I then present the login form in a floating "lightbox" div. If the user successfully logs in, I want the browser to retry the action.
I'm having trouble finding a good place to store the function that I want to retry. Currently, I'm attaching it to a global object, which seems very hacky. The jQuery "deferred" pattern seems to fit here, but I would still need to retain a reference to the deferred object somewhere, and I can't see the right place to do it.
Suggestions?
Here's some simplified code (in reality, the Ajax response determines whether we show the login form or a confirmation of the action):
$(function() {
$('li.secured').find('a').click(function() {
var link = this;
var action = function() { // captures 'link'
if(RootObj.IsAuthenticated() ) { // already logged in
window.location.assign( link.href );
} else {
// pass ourself in as a callback to be repeated after authentication
// Yes, this seems to work even in the midst of our own definition...
RootObj.ShowLoginForm( action );
}
return false; // don't go immediately to the new url
}
return action();
});
});
RootObj.ShowLoginForm = function( callback ) {
var lb = $('#lightbox');
if(lb.length == 0) { lb = $('<div id="lightbox" class="lightbox"></div>').appendTo('body'); }
lb.load('login.html');
RootObj.callback = callback; // is there a better place to store this callback function?
}
I have this code in my view.
After my database adding the message is showing perfectly but my Grid is not showing the updated result. If I keep
return true;
when I click Submit button I am getting popup window immediately and then adding to the database? This showing my updated result in the grid.
If you have return true at the end of your submit function, then the browser will go to the url in the action attribute of the form. I think you should always return false if you have taken care of the form data in the ajax call. You need to get the updated grid as part of the ajax success method.
You can't have the ajax popup and still let the form submit the normal way.
$(function () {
$('#form4').submit(function () {
...
$.ajax({
...
success: function (result) {
...
// update grid
grid_container = $("#grid").html('');
$("<table>").appendTo(grid_container);
for (ii = 0; ii < result.grid.length; ++ii) {
tr = $("<tr>").appendTo(table);
for (jj=0; jj < result.grid[ii].length; ++ii) {
td = $("<td>").text(result.grid[ii][jj]).appendTo(tr);
}
}
// show success message
alert('Saved NewServiceTypeCategory Successfully. Thank you!');
}
});
return false;
});
});
Now all you have to do is make your server side post handler return the XML with the grid data.
The AJAX request is asynchronous, so it finishes the form submit routine before the callback finishes, which might explain the result you are getting. I think there may be an async setting you can turn off so that the operation/callback must complete first.
Basing on your code and as far as i'm aware of, you have no way on loading the new data.
The reason why when you return true; the new data is being loaded is because the submit event successfully perform it's default operation which is submitting to the server and refreshing the page.
On the other hand, return false; prevents this operation (including the refresh page) and instead just run the XHR.
What you could do is:
Return the new data as a result of the
XHR and parse it accordingly.
I'm unsure of the best practice for modifying the DOM based on an ajax response. I'll try to let the code do the talking because it's hard to explain.
// page has multiple checkboxes
$("input[type='checkbox']").live('click', function {
var cb = $(this); // for the sake of discussion i need this variable to be in scope
$("form").ajaxSubmit({ dataType: "script" });
}
The server sends a response back, and the js gets eval'd and that means "cb" is out of scope.
What I've done so far is create a couple of helper functions:
var target = undefined;
function setTarget(val) {
target = val;
}
function getTarget() {
return target;
}
And that turns the first snippet of code into this:
// page has multiple checkboxes
$("input[type='checkbox']").live('click', function {
setTarget($(this));
$("form").ajaxSubmit({ dataType: "script" });
}
Then on the server's response I call getTarget where I need to. This seems hackish... Any suggestions?
It's unclear what you're actually trying to do, but I feel like you want to be looking at the success parameter for that AJAX call. The success callback function should execute in parent scope and do what you're looking for.
See 'success' on this page in the jQuery docs.
So what you are trying to do is get the form to submit the content via ajax whenever the user checks/unchecks a checkbox? And because there are several checkboxes, you need to find out which one triggered the submit, so you can change its value to whatever is stored on the server?
If you submit the entire form everytime, why don't you reply with all the checkboxes values, and then change each and every one of them? If not, get the server to reply with the id and the value of the checkbox, then use jquery to find the checkbox with that ID and then change it's value.
How about:
jQuery(function($) {
// give it scope here so that the callback can modify it
var cb,
cbs = $('input[type="checkbox"]');
cbs.live('click', function {
// taking away var uses the most recent scope
cb = $(this);
// disable checkboxes until response comes back so other ones can't be made
cbs.attr('disabled', 'true'); // 'true' (html5) or 'disabled' (xhtml)
// unless you are using 'script' for something else, it's best to use
// a callback instead
$('form').ajaxSubmit({
success : function(response) {
// now you can modify cb here
cb.remove(); // or whatever you want
// and re-enable the checkboxes
cbs.removeAttr('disabled');
}
});
}
});