Sunday, February 12, 2012

Subtle differences using ViewFields vs. Include with Client Object Model

I’ve been diving more and more into Client OM/JSOM, in order to get my roboCAML codeplex project up to snuff. Of course, I’m going to be hacking away at examples and trying to make sense of it all. The first thing I needed was to visualize the data I’m working with; muy importante para mi.  Hacking away in the console, I was able to find where all of the awesome data is hidden. So the stage is set...

Using a standard function, I’m successfully retrieving data from SharePoint.  So far, so good...  Here’s the code I’m using:

function queryList() {
//...-->SNIP<--...//

var viewXml = roboCAML.Query({
listName: "Child",
closeCaml: "ClientOM",
//ViewFields: ["MultiSelectLookup", "MyDollars", "DateAndTime"],
config: [
{
filter: "&&",
op: "=",
staticName: "MultiSelectLookup",
value: 4,
lookupId: true
},
{
filter: "And",//I really like “&&”, but added this to show flexibility
op: "=",
staticName: "FSObjType",
value: 0
},
{
op: "IsNull",
staticName: "MyDollars"
}
]
});

$("#QueryOutput").val( viewXml );

try {
//Get the current client context
this.spContext = new SP.ClientContext.get_current();

//Create local vars.
var targetList = spContext.get_web().get_lists().getByTitle( listName ),
camlQuery = new SP.CamlQuery();

//Set CAML Query.
camlQuery.set_viewXml( viewXml );
//Cache listItems var for use later.
this.listItems = targetList.getItems( camlQuery );
//Load and execute queries.
spContext.load( listItems /*'Include(Author,MultiSelectLookup,MyDollars)'*/ );
spContext.executeQueryAsync(ReadListItemSucceeded, ReadListItemFailed);
}
catch( e ) {
console.log( e );
}
}

This works as expected.  Just for clarity my  query above looks like this:

<View>
<ViewFields>
<FieldRef Name='DateAndTime' />
<FieldRef Name='MyDollars' />
<FieldRef Name='MultiSelectLookup' />
</ViewFields>
<Query>
<Where>
<And>
<Eq>
<FieldRef Name='MultiSelectLookup' LookupId='True' />
<Value Type='LookupMulti'>4</Value>
</Eq>
<And>
<Eq>
<FieldRef Name='FSObjType' />
<Value Type='Lookup'>0</Value>
</Eq>
<IsNull>
<FieldRef Name='MyDollars' />
</IsNull>
</And>
</And>
</Where>
</Query>
</View>

When the executeQueryAsync is successful, it’ll call the ReadListItemSucceeded function. Pretty straightforward, nothing magical...  What is magical, is finding a method that will display all of the JSON that has been returned. I can’t find it... I did *fix* that though. In the ReadListItemSucceeded function, I have a simple for loop, that expands and logs all of these properties.  Here’s what that looks like:

function ReadListItemSucceeded(sender, args) {

for( var i=0; i<listItems.$2_1.length; i++ ) {
console.dir( listItems.$2_1[ i ].$4_0.$1F_0.$m_dict );
}
//...-->SNIP<--...//
}

***Please note: console.dir() didn’t make it into Internet Explorer until v9***


This code will iterate through all of the JSON that is returned and display nicely in the console all of the data for each item.  Here’s an example of 1 item that was returned in my JSON.

{
DateAndTime : Wed Feb 22 09:00:00 EST 2012,
MyDollars : null,
MultiSelectLookup : [object Object],[object Object],[object Object],[object Object],[object Object],[object Object],
_ModerationStatus : 0,
_Level : 1,
ID : 24,
UniqueId : eeda73be-0e32-4d90-a461-2237ca2b1081,
owshiddenversion : 2,
FSObjType : "0",
Created : Mon Jan 30 15:23:58 EST 2012,
Modified : Sun Feb 12 14:01:49 EST 2012,
FileRef : "/URL/Lists/GrandChild/24_.000"
}

Notice all of the extra columns?  What’s up with that?  It’s not as verbose as the Web Services, but I didn’t ask for all of this data, you know?


What’s the fix for the hefty <ViewFields>?


Use Include(‘ColumnName’) within your .load() call.  If you look above at my code sample, it’s commented out.  I don’t know what exactly happens under the hood that makes the difference, but here’s a sample result from my console.dir() iteration.

{
Author : [object Object],
MultiSelectLookup : [object Object],[object Object],[object Object],[object Object],[object Object],[object Object],
MyDollars : null
}

Now, I’m getting exactly what I want without any overhead data.  Because of this behavior, I’m very much thinking of adding another method to roboCAML, to make this easy to use.


And if you were wondering... Using <ViewFields> and Include together do not mix! You will promptly receive an error.


image


To get around this, my updated CAML looks like this:

<View>
<Query>
<Where>
<And>
<Eq>
<FieldRef Name='MultiSelectLookup' LookupId='True' />
<Value Type='LookupMulti'>4</Value>
</Eq>
<And>
<Eq>
<FieldRef Name='FSObjType' />
<Value Type='Lookup'>0</Value>
</Eq>
<IsNull>
<FieldRef Name='MyDollars' />
</IsNull>
</And>
</And>
</Where>
</Query>
</View>

ProTip: If you hate writing CAML, use roboCAML... It’s free and has attractive qualities to the opposite sex.

1 comment:

Cheap Fifa 14 Coins said...

That's great. Many thanks with regard to discovering this. A few great suggestions presently there, this particular will probably be a genuine period kitchen sink. I'm the the far east visit enthusiast,

elo boosting


Buy Fifa 14 Coins