Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Tuesday, October 1, 2013

Simple[r] jQuery Content Filter

A post titled: Simple jQuery Content Filter for Office 365 Public Website by Doug Hemminger came by my twitter stream that caught my eye.  A real quick win and all around goodness for everyone.  Reading through, I noticed some things I could tidy up.  So I asked him if he'd be okay if I re-factor the code that he blogged about and he said absolutely!

UX

It's almost expected these days to be able to click the text next to a checkbox and have it just work.  Doug is using <span> for the text, so changing this to use <label> and wrap the <input> is a real quick win.

As I was writing this up, I also had another idea...  Why not have all of the filters show no matter how far down you scroll?  This is also a quick and easy win, so it's in there too.

DRY

DO NOT REPEAT YOURSELF! 
I'm totally guilty of this, but I try to do my best every time.  I checked the source of the page where the code was added and found:
        $(document).ready(function () {
            function SPSToggleView() {
                if (!$("#SPSBusinessCheckbox").prop("checked")) {
                    $(".SPSBusiness").hide();
                } else {
                    $(".SPSBusiness").show();
                }

                if (!$("#SPSInformationWorkerCheckbox").prop("checked")) {
                    $(".SPSInformationWorker").hide();
                } else {
                    $(".SPSInformationWorker").show();
                }

                if (!$("#SPSCertificationCheckbox").prop("checked")) {
                    $(".SPSCertification").hide();
                } else {
                    $(".SPSCertification").show();
                }

                if (!$("#SPSBusinessIntelligenceCheckbox").prop("checked")) {
                    $(".SPSBusinessIntelligence").hide();
                } else {
                    $(".SPSBusinessIntelligence").show();
                }

                if (!$("#SPSDeveloperCheckbox").prop("checked")) {
                    $(".SPSDeveloper").hide();
                } else {
                    $(".SPSDeveloper").show();
                }
                if (!$("#SPSITProCheckbox").prop("checked")) {
                    $(".SPSITPro").hide();
                } else {
                    $(".SPSITPro").show();
                }
                if (!$("#SPSCloudCheckbox").prop("checked")) {
                    $(".SPSCloud").hide();
                } else {
                    $(".SPSCloud").show();
                }
                if (!$("#SPSSocialCheckbox").prop("checked")) {
                    $(".SPSSocial").hide();
                } else {
                    $(".SPSSocial").show();
                }
                if (!$("#SPSGeneralCheckbox").prop("checked")) {
                    $(".SPSGeneral").hide();
                } else {
                    $(".SPSGeneral").show();
                }
            };
            $("#SPSInformationWorkerCheckbox").attr("checked", true);
            $("#SPSBusinessCheckbox").attr("checked", true);
            $("#SPSCertificationCheckbox").attr("checked", true);
            $("#SPSBusinessIntelligenceCheckbox").attr("checked", true);
            $("#SPSDeveloperCheckbox").attr("checked", true);
            $("#SPSITProCheckbox").attr("checked", true);
            $("#SPSCloudCheckbox").attr("checked", true);
            $("#SPSSocialCheckbox").attr("checked", true);
            $("#SPSGeneralCheckbox").attr("checked", true);

            $("#SPSInformationWorkerCheckbox").click(SPSToggleView);
            $("#SPSBusinessCheckbox").click(SPSToggleView);
            $("#SPSCertificationCheckbox").click(SPSToggleView);
            $("#SPSBusinessIntelligenceCheckbox").click(SPSToggleView);
            $("#SPSDeveloperCheckbox").click(SPSToggleView);
            $("#SPSITProCheckbox").click(SPSToggleView);
            $("#SPSCloudCheckbox").click(SPSToggleView);
            $("#SPSSocialCheckbox").click(SPSToggleView);
            $("#SPSGeneralCheckbox").click(SPSToggleView);
        });


Even though this is a simple solution, I knew it could be made simpler. :) I got it down to this:

    
$(document).ready(function () {
        var $wrapper = $("#wrapper");

        $wrapper.on("change", "input[data-filter]", function (event) {
            var $this = $(this),
                filter = $this.data("filter");

            $wrapper.find("div[data-filter='" + filter + "']").slideToggle();
        });
    });

The magic is done by hiding the value(s) we are going to hide/show directly onto the <input> as a `data-filter` attribute.  When the change event is fired, that value is surfaced.  Then a simple query of the DOM to find the correct <div>'s that this filter relates to.  The jQuery#slideToggle method knows whether or not the elements are hidden or displayed, so there's no need to check the state of the <input>.

Results

Tuesday, February 12, 2013

Setting field values using CSOM client side - Another look

Last night after hitting publish, I enjoyed a long ride home on the metro...  I was able to catch up on some of my reading.  It's a relaxing part of my day and sometimes exciting because I get to grind away on concepts that I'm working on.  Last night did not disappoint.

spUtils - setColumnVal


As I said in the previous post, I've already tackled this problem, however, I didn't really like the implementation.  So, here's my bright idea... Toggle the library back to use setColumnVal and see what the XML looks like under the hood.  Doing just that, here's what's produced( I've snipped this for brevity ):


<Method Name="SetFieldValue" Id="26" ObjectPathId="21">
  <Parameters>
<Parameter Type="String">AssignedTo</Parameter>
<Parameter Type="Array">
<Object TypeId="{c956ab54-16bd-4c18-89d2-996f57282a6f}">
<Property Name="LookupValue" Type="String">DEV\Administrator</Property>
<Property Name="LookupId" Type="Number">-1</Property>
</Object>
<Object TypeId="{c956ab54-16bd-4c18-89d2-996f57282a6f}">
<Property Name="LookupValue" Type="String">DEV\spUser</Property>
<Property Name="LookupId" Type="Number">-1</Property>
</Object>
</Parameter>
</Method>


So based on that, it's easy to see the people picker XML has to be an array of objects.  Let's give that a shot now using this code mixed with parseAndSetFieldValue.

spUtils - parseAndSetFieldValue revisited


spUtils.updateListItems({
listName: "spUtils",
updates : {
1 : {
"Title" : spUtils.isoDate(),
"AssignedTo" : [
{
LookupValue: "DEV\\Administrator",
LookupId: -1
},
{
LookupValue: "DEV\\spUser",
LookupId: -1
}
]
}
},
success: function( data, ctx ) {
debugger;
}
});

Using the code above produces this XML ( snipped as well for brevity ):

<Method Name="ParseAndSetFieldValue" Id="44" ObjectPathId="21">
  <Parameters>
<Parameter Type="String">AssignedTo</Parameter>
<Parameter Type="Array">
<Object Type="Dictionary">
<Property Name="LookupValue" Type="String">DEV\Administrator</Property>
<Property Name="LookupId" Type="Number">-1</Property>
</Object>
<Object Type="Dictionary">
<Property Name="LookupValue" Type="String">DEV\spUser</Property>
<Property Name="LookupId" Type="Number">-1</Property>
</Object>
</Parameter>
  </Parameters>
</Method>


It's remarkably close to the XML that actually works.  The only thing that is different is the Object Type.  Sadly, this is all that it takes for this to FAIL.  Yep, that's right...  Trying to be smarter than the average bear, let's give it another shake.  This time, I'm going to take some code out of the setColumnVal method and drop it into an array.  Take a look at this:

spUtils.updateListItems({
listName: "spUtils",
updates : {
1 : {
"Title" : spUtils.isoDate(),
"AssignedTo" : [  SP.FieldUserValue.fromUser("DEV\\Administrator"),
  SP.FieldUserValue.fromUser("DEV\\spUser")
]
}
},
success: function( data, ctx ) {
debugger;
}
});


This in turn produces XML that *should* work!

<Method Name="ParseAndSetFieldValue" Id="44" ObjectPathId="21">
  <Parameters>
  <Parameter Type="String">AssignedTo</Parameter>
  <Parameter Type="Array">
  <Object TypeId="{c956ab54-16bd-4c18-89d2-996f57282a6f}">
  <Property Name="LookupValue" Type="String">DEV\Administrator</Property>
<Property Name="LookupId" Type="Number">-1</Property>
  </Object>
  <Object TypeId="{c956ab54-16bd-4c18-89d2-996f57282a6f}">
<Property Name="LookupValue" Type="String">DEV\spUser</Property>
<Property Name="LookupId" Type="Number">-1</Property>
  </Object>
  </Parameter>
  </Parameters>
</Method>


The only difference this time is the Method Name attribute.  Sadly, even this FAILS! I was going to continue with using numbers, but with this being a show stopper, I'm convinced I've researched this thoroughly enough.  This may be different in SP2013, it simply doesn't work in SP2010, therefore unreliable.

What's next?


Since I need the context of the list item to set its values when using the .update() method, it's not feasible to change what I have currently.  To set lookups and people picker values in CSOM, you have to use the code I've already written.  Guess it's time I start documenting the API, eh?

Thursday, November 29, 2012

Binding Event Handlers to SharePoint Content Types (Sandbox)

Fiddling around in SharePoint's Sandbox land can and will test your sanity.  There are things that are the way they are and you just have to deal with it.  All in all, a learning experience indeed...

Here's some key takeaways from my current project:
I opted not to bind this to the Content Type because I didn't want the Event Handler's behavior to persist on any other list.  I doubt the CT will be reused, but if it is, I know my code will not run erroneously.

Once that is added to your list, then you can dive into the properties and detect the Content Type GUID for the item that was just added.  A simple example:

       public override void ItemAdded(SPItemEventProperties properties)
       {
           base.ItemAdded(properties);
           if (properties.ListItem.ContentTypeId.Parent.ToString().ToUpper() == Constants.ctGuid.ToUpper())
           {
            // Snipped
           }
This is only a small part of what I'm in the middle of building.  Hopefully, I can abstract more out and show off some of the innards.

Cheers!

***Update***
Shortly after posting this @SharePointAlex and I started to discuss binding Event Handlers to lists.  Based on my tests, I've confirmed that using a Site-scoped solution does in fact fire the Event Handler on all lists in your web.  Using the same technique but having the solution Web-scoped will only bind the Event Handler to the list you declare.  Thanks Alex for making dig a bit deeper to further get better documentation.