Programming, technology, and CRM – from a Belgian programmer exiled to Missouri
  • rss
  • Home
  • Contact Me
  • Welcome

Tips & Tricks of the Web Client

Nicolas Galler | May 15, 2009

This is a collection of random tricks, pitfalls etc that I am encountering on the current web client project. I will update within the next few weeks with other tidbits I find out.

General Tips

  1. Do not use quickforms.  Ever.  Seriously.  For real.  Well, there are exceptions:
    • A form with really trivial layout, no dynamically set controls, and no validation logic
    • When trying to figure out the basic syntax to declare a Saleslogix control
    • When making a trivial change to an existing form
    • If you have limited knowledge of HTML and really, really don’t want to learn: this it is NOT an exception because you will need to know HTML in order to troubleshoot the quickform!
  2. In case of an error, start with the event log.  Especially for lookups.
  3. Use CSS to your advantage when laying out the forms!  The code in quickform smartparts can afford to be sloppy and repetitive because it’s automatically generated, but we can’t! 
  4. In order to add a linked entity, use code like:
    ISEOppUtility oppUtility = EntityFactory.Create<ISEOppUtility>();
    // link to the parent entity ...
    oppUtility.Opportunity = parentEntity;
    // ... and add to the parent's collection.  The link is bidirectional.
    parentEntity.SEOppUtilities.Add(oppUtility);
    oppUtility.Save();
    // save the parent... in theory this should cascade and 
    // save the child entity... but that does not always work
    parentEntity.Save();
    // ensure that all views get refreshed - this is not always necessary
    PageWorkItem.Services.Get<IPanelRefreshService>().RefreshAll();
  5. Do not try to rename anything in the App Architect – it will mix stuff up. It is quicker and safer to do it on the backend XML instead.
  6. Run the web site on .NET 3.5 using the following steps:
    • Global replace of 1.0.61025.0 to 3.5.0.0 in web.config (do NOT replace in the other files)
    • Add "dependentAssembly" tag under runtime/assemblyBinding:
      <dependentAssembly>
              <assemblyIdentity name="System.Web.Extensions" culture="neutral" publicKeyToken="31bf3856ad364e35"/>
              <bindingRedirect oldVersion="1.0.61025.0"
                               newVersion="3.5.0.0"/>
            </dependentAssembly>
    • Use this for compilation tag (under system.web):
    •     <compilation debug="true" defaultLanguage="C#">
            <assemblies>
              <!--
              Cannot add System.Core - it conflicts with LinqBridge which is required by SLX
              <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
              -->
              <add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
              <add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
              <add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
            </assemblies>
          </compilation>
    • As you cannot add System.Core not all features will be available. Most importantly System.Linq.Expressions.
  7. Do not use <script> tags for JavaScript on smart parts because they will get lost after a postback. Use ScriptManager.RegisterClientScriptBlock instead. Unfortunately this means a large performance hit if the script is big as the client will have to download and parse it every time. Alternatively, disable the top-level UpdatePanel, and use smaller updatepanels to do the job (this will make the page a LOT more responsive on slow links but requires a bit of work to make sure everything is still getting refreshed as it should)

Data Access

  1. Events on property changes have 2 serious drawbacks:
    • “BeforeUpdate” events do not fire at all
    • “AfterUpdate” events only fire when the entity itself is saved, thus they are not terribly useful as you might as well put that code in the OnUpdate event of the entity itself. Also, because it is processed after the entity is actually saved, you cannot easily modify the entity at this point (you have to resave it, but because it is already inside of a Save call, it may cause NHibernate to crash)
  2. Events on an “extension” entity do not always trigger correctly on “cascading” save. Basically if you have a rule on ContactExt.BeforeUpdate, and you make a change to the ContactExt field, then call Account.Save, the changes get saved, but the event rule does not necessarily get called. It is a bit confusing, but you will recognize it when it happens.
  3. Do not Delete and Save an entity within the same transaction (this includes Save / Delete that are made as part of a cascade operation)

Lookups

  1. You can have a lookup exclude the entities that were already selected using code like:

    lueAddUtility.LookupExclusions = parentEntity.SEOppUtilities.ToArray();

  2. To invoke a lookup programmatically the following code works, sloppy as it may look:
    ScriptManager.RegisterClientScriptBlock(this, GetType(),
                "ShowLookup",
                // we need a slight delay here to give the lookup a chance to initialize
                "$(document).ready(function() { setTimeout(function() { " + lueAddUtility.ClientID + "_luobj.show() }, 500) })",
                true);

    It is also possible to set up a lookup as "Button only" which creates the lookup button… good for "associate a record" buttons.

  3. It is possible to use custom HQL in a lookup, by doing a PreFilter that points to a non-string property (this may well break in a later version).  EG (taking advantage of the fact that TYPE is defined as an enum and not a string, this creates a condition to select remote, network and concurrent users only):
    <SalesLogix:LookupPreFilter PropertyName="Type" CondOperator="Not Equal to" FilterValue="'' and User.Type in ('N', 'M', 'C')" />

    Note the “Not Equal to” bit is case sensitive.

  4. If adding a LookupPreFilter on a field that is not a string (including enums), you have to add the single quotes explicitely.
  5. I am not sure if you are supposed to use CondOperator or OperatorCode in the LookupPreFilters. The documentation seems to mention CondOperator, but Saleslogix uses OperatorCode… the possible values for “CondOperator” are in the help file, but in addition to the ones mentioned there one can also use “Not Equal to”.
  6. Lookups get cached the first time they are accessed… if the definition changes on the smart part, Log Off, and log back in. Closing IE or restarting IIS is not necessary. To some extent this may make it hard to modify lookups on the fly. Interesting tidbit: if LookupExclusion is not null then the cache won’t be used. So if you want to set up the LookupPreFilter dynamically you may have to make sure you set up a LookupExclusion (this may be an empty array). Will probably break in a later version.
  7. Lookups with a displaymode of DropDownList behave completely differently and may as well be considered different controls. None of the above comments apply to them.

Databinding

  1. Remember that the Saleslogix databinding works with events (i.e. when TextChanged fires is when the property will be populated on the entity). This has 2 important consequences:
    • You can’t bind to a property that does not have a corresponding change event (e.g. DropDownList.SelectedValue)
    • Within a Change handler, it is hard to know whether the properties have all been brought in yet or not. Say you want to change the CurrentEntity.Price when the lookup returns – if txtPrice fires a TextChanged event after the LookupResultValueChanged event did, then the property will be overwritten. To be safe it has to be written both to the entity and to the control.
  2. On the property itself there is a way to characterize the datatype (for some property types). For example for a “Double” type property there is a way to indicate that the property refers to a percentage. This is probably useful in some situation but other times it messes up with the rounding and the display. So I turned it off (a few years ago I wrote a little piece of Javascript to format percentages in textboxes and it still works pretty well for me).
  3. There is a client-side component for the databinding (responsible for undoing the changes when the user hits the close button, for example). When messing with the DOM be careful not to get this one confused (do not move controls too far afar in the document – if they get under a different “workspace” some of the bindings won’t work).
  4. It is possible for a smart part to interfere with the databinding of other smart parts. For example calling Page.DataBind is a NO-NO. If you notice that some values are getting cleared when they shouldn’t (often happens with the values that are loaded from ControlState, e.g. GridView.DataKeys), either check the other smartparts on the page or use a work around (e.g. instead of using grid.DataKeys we can use a CommandArgument).
Categories
Programming, Saleslogix
Comments rss
Comments rss
Trackback
Trackback

« Using IChangedState to track before/after values of an entity SlxGridHelper – Make the SlxDataGrid more convenient in our custom smart parts »

2 Responses to “Tips & Tricks of the Web Client”

  1. Nicolas Galler says:
    February 27, 2010 at 11:11 pm

    Adding another one just so I dont forget… to show a message box, use Sage.Services.getService(’WebClientMessageService’).showClientMessage(’Some message’);

  2. Nicolas Galler says:
    March 10, 2010 at 11:39 am

    Example of a user lookup filtered by team – this one uses LookupBindingMode of String so it can be saved directly to a user id:

    <SalesLogix:LookupControl runat=”server” ID=”lueSVP” LookupBindingMode=”String” InitializeLookup=”true”
    LookupEntityName=”UserInfo” LookupEntityTypeName=”Sage.Entity.Interfaces.IUserInfo, Sage.Entity.Interfaces”>
    <LookupPreFilters>
    <SalesLogix:LookupPreFilter PropertyName=”Id” CondOperator=”Not Equal to”
    FilterValue=”‘xxx’ and Id in (select sr.User.Id from OwnerRights sr where sr.Owner.OwnerDescription=’Lit Fulfillment’) and Id not in (select u.Id from User u where u.Type=’R')” />
    </LookupPreFilters>
    <LookupProperties>
    <SalesLogix:LookupProperty PropertyHeader=”User Name” PropertyName=”UserName” />
    <SalesLogix:LookupProperty PropertyHeader=”Title” PropertyName=”Title” />
    <SalesLogix:LookupProperty PropertyHeader=”Department” PropertyName=”Department” />
    <SalesLogix:LookupProperty PropertyHeader=”Phone” PropertyName=”Phone” />
    </LookupProperties>
    </SalesLogix:LookupControl>

Leave a Reply

Click here to cancel reply.

Categories

  • Experiments (4)
  • Interesting (1)
  • MSCRM (1)
  • Programming (60)
  • Rant (3)
  • Saleslogix (34)
  • Tricks (8)
  • Uncategorized (24)

Post History

  • 2010
    • January (3)
    • March (1)
  • 2009
    • March (2)
    • April (1)
    • May (3)
    • June (3)
    • July (1)
    • September (3)
    • October (2)
    • December (5)
  • 2008
    • January (9)
    • February (4)
    • March (9)
    • April (1)
    • May (5)
    • June (8)
    • July (1)
    • August (2)
    • September (1)
    • November (1)
    • December (3)
  • 2007
    • January (3)
    • February (7)
    • March (1)
    • April (3)
    • May (6)
    • June (2)
    • July (1)
    • August (2)
    • September (5)
    • October (3)
    • November (5)
    • December (4)
  • 2006
    • January (2)
    • September (1)
    • November (3)
    • December (4)
  • 2005
    • April (1)

Meta

  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
rss Comments rss valid xhtml 1.1 design by jide powered by Wordpress get firefox