In most case I try to do the validation for InforCRM form input on the server side. This is generally easier to implement (sometimes a lot easier), more reliable, and the performance is usually close (sometimes even better, if there are a lot of queries involved, as each one requires an sdata round trip when performed from the client side!) Reporting an error to the user and interrupting processing is very easy, all that is required is throwing an instance of a Sage.Platform.Application.ValidationException object:

throw new ValidationException("No way!");

However sometimes the server-side validation is not enough. The best example is when you need to ask confirmation from the user. This always requires some javascript to do the actual prompt – you can still drive the process from the server side by using hidden buttons triggered from the script but it can get cumbersome very quickly. Client-side validation can also result in a more responsive interface, especially when it can be performed synchronously (without an sdata query).

Here is a simple example that gives the user a confirmation prompt if they have not selected enough products on a new opportunity:

    String script = String.Format(@"
require(['dojo/ready', 'dojo/on', 'dojo/dom', 'dijit/registry'], function(ready, on, dom, registry) {{
    function onSaveClick(e) {{
        var grid = registry.byId('OpportunityProductsgrdOppProducts');
        var prodCount = grid.store.dataCacheToArray().length;
        if(prodCount <= 1){{
            if(!confirm('Are you sure you wish to submit this opportunity with less than 2 products?')) {{
                e.preventDefault();
            }}
        }}
    }}

    ready(function() {{
        on(dom.byId('{0}'), 'click', onSaveClick);
        on(dom.byId('{1}'), 'click', onSaveClick);
    }});
}});
", cmdSave.ClientID, cmdSaveNew.ClientID);
ScriptManager.RegisterStartupScript(Page, GetType(), "ProductValidation", script, true);

If you need an asynchronous validation this is a little bit more complex because you need to interrupt the normal flow of the button then resume it if the validation succeeds. You can use __doPostBack to that effect. Here is the same validation, using an asynchronous dialog. Keep in mind raiseQueryDialog will return immediately and note the e.preventDefault() that is invoked before the dialog actually returns a result.

    String script = String.Format(@"
require(['dojo/ready', 'dojo/on', 'dojo/dom', 'dijit/registry'], function(ready, on, dom, registry) {{
    function onSaveClick(e) {{
        var grid = registry.byId('OpportunityProductsgrdOppProducts');
        var prodCount = grid.store.dataCacheToArray().length;
        if(prodCount <= 1){{
            Sage.UI.Dialogs.raiseQueryDialog('Not enough products', 
                'Are you sure you wish to submit this opportunity with less than 2 products?', 
                function(result) {{                    
                    if(result) {{
                        __doPostBack(e.target.name, '');
                    }}
                }}, 'OK', 'Cancel');
            e.preventDefault();            
        }}
    }}

    ready(function() {{
        on(dom.byId('{0}'), 'click', onSaveClick);
        on(dom.byId('{1}'), 'click', onSaveClick);
    }});
}});
", cmdSave.ClientID, cmdSaveNew.ClientID);
ScriptManager.RegisterStartupScript(Page, GetType(), "ProductValidation", script, true);