Office Dev PnP – SPFx/JavaScript Special Interest Group 4/27/17

A friend of mine mentioned an idea to me that I really liked and I could definitely see the need for. He suggested that, at the moment, there is no good synopsis of what happens in the Office Dev Patterns and Practices calls that happen on a regular basis. He said he didn’t have the time to take this on but I was free to if I wanted, thank you Eric. I’ll do my best to get a synopsis published the same day as the calls. I’ll run a synopsis of all the calls for PnP.

What is Office Dev PnP?

The Office Dev PnP is an open source, community driven initiative that is backed by Microsoft and Microsoft resources. The goals are to provide tools, samples, and guidance on developing for SharePoint and Office365. There are a lot of samples in the PnP GitHub Repository. There are other repositories in this area as well if you want to contribute to the PnP.

 

Special Interest Group Calls

There are a few Special Interest Group calls relating to different areas of the Office Dev PnP. Eric Overfield does a pretty good job of explaining what these calls are and how to join them so check out his article, Participating in the SharePoint / Office 365 Dev Patterns and Practices (PnP) Community Initiative.

 

April 27, 2017 SPFx/JavaScript SIG Call

4:17 – Opportunities to Participate

  • Demo an SPFx Webpart
  • Demo sp-pnp-js in a Project
  • Contribute on GitHub
  • Provide Feedback

5:14 – PNP-Core Updates

  • 2.0.4 Released – Fixes SPFx blocking bug
  • Doing beta releases now – sp-pnp-js@beta
  • Checkout wiki and developer guide
  • Search Improvements
  • Updated React SPFx samples to work with sp-pnp-js
  • Report issues in the GitHub issues list with enough details
  • Graph support in process though early stages, discussion in issues list

11:58 – SharePoint Framework Updates

17:30 – What engineering is working on

  • New capabilities
    • Web part to web part connections – sending events
    • Modern UI extensions
      • Modern JSLink and UserCustomAction
      • Additional Yeoman templates
    • Workbench improvements
    • SPFx improvements
    • Numerous topics for both online and on-premise.
    • Working on on-premise for 2016, not 2013 at first depending on demand
  • Other
    • SharePoint document consolidation to single location eventually to docs.microsoft.com/sharepoint
      • MSDN docs will go away because the platform is super old
      • All MSDN SharePoint docs will be moved to dev.office.com/sharepoint then to final location

20:34 – Demo Vesa SPFx VS Extension http://aka.ms/spfx-vs-extension

  • Still using Yeoman for templating
  • Early alpha release available
  • Other releases coming soon
  • Template shows under the Office/SharePoint section for new project
    • Launches wizard
    • Can click generate or go to Advanced to see/modify the full yeoman command
      • -skip-install switch to skip the npm install
  • Can add more web parts
    • Add new item to project
    • Select Office/SharePoint – SharePoint Framework
    • SPFx Web Part
    • Prompted with smaller wizard
  • F5 launches gulp serve
  • Does not introduce any new templates, just uses the yeoman templating
  • Missing Features
    • Native debugging
  • This is for those that want to do SPFx in Full Visual Studio instead of VS Code

30:09 – Demo Sebastien RiotJS

  • List Viewer Web Part

Available in Github https://github.com/sebastienlevert/spfx-riot-list

  • Need riot and riot-typescript added to package.json
  • tsconfig.json add experimental decorators to true
  • Extend RiotClientSideWebPart
    • Handles the mounting to the DOM
  • Riot allows separation of view and component like Angular
  • Using await/async from TypeScript
  • Using PnP getAs ODataEntity
  • Must call update to update the ui after asynchronous calls
  • Riot also includes a router

46:08 – Open Discussion

  • Any headway with gov farm support for SPFx?
    • No dates
    • Wrong people to ask
    • Right way to find right people to ask would be to have the gov client to contact the gov farm people.

Guidance on calling external APIs, WebApi/Azure Functions early next week

Next Meeting May 11th.

To TypeScript or Not to TypeScript

Many times, when I go speak on modern development, I get asked the same question, “Do I have to use TypeScript?” So I figured I would answer this through a blog post so I can make sure I answer all the pieces to this question. If you haven’t used TypeScript before I’ll cover what TypeScript is and provide the information so you can decide whether you want to use TypeScript in your projects. Continue reading “To TypeScript or Not to TypeScript”

SharePoint 2013 Add To Timeline Programmatically

With the release of SharePoint 2013 Microsoft added a couple new web parts that users may have seen but may not have realized that they can use them in their own pages. The two web parts are related and one includes the other. These web parts are the Project Summary Web Part and the Timeline. There have been several blog articles talking about these web parts so I am not going to go into too much detail but the part I will cover is the fact that both these can be used on your own web part pages.

Project Summary Web Part

ProjectSummary

 

The beauty of this web part is its ability to be able to give your users a quick at the status of the tasks within your site. In the top of this web part you can see an option to add a task and edit the list which will both allow you to interact with the list. There is also a scroll arrow which will take you to the a view of the next web part.

Timeline Web Part

TimelineScreenShot

 

The timeline web part is also made available as a user control that you can embed in your own web parts but it is a little tricky to figure out how to make it work, more on how to do that in another post. the Timeline web part is the section in the right portion of the screenshot. What the screen shot does not allow you to see is the fact that you can click and drag the tasks to different places to make it more legible. You can also change the styling and colors of the timeline through the ribbon. Both of these changes are persisted so when you come back later your changes will still be there.

This is cool but can I work with it programmatically?

Both of these web parts are tied to a task list in your site. So to add a task to your timeline it is easy to do from the task list. The menu for the task gives you an option to add to timeline. All this is cool but how do I work with the these web parts? I have searched high and low to figure out how to be able to manipulate the what tasks are shown on the timeline and I couldn’t find anything. So being a developer I decided that if Microsoft can do it in their own web parts then I should be able to do so myself. I found several blogs that mention a programmatic way of adding tasks to a timeline. The task data that is used to generate the timeline is stored as  a property on the root folder on the list called “Timeline_Timeline”. If you look at the value of this property you can see that it is just XML. Easy enough to work with right? This felt like a hack to me so I kept digging.

In my digging I found out that Microsoft added a JavaScript file called sp.ui.timeline.js to the SharePoint page whenever the timeline control is loaded. So I decided to go look at that file and see if I could find a way to add tasks without it being as much of a hack. The sp.ui.timeline.js file is located in the layouts folder under the SharePoint Root. The cool thing is that most of the SharePoint JavaScript files include a debug version that has not been minified. This makes it easier to read, not easy just easier. A lot of the functionality has gone through some sort of obfuscation but you can get some basics.

In digging through the debug file I found that Microsoft exposed a couple methods in the root(window) namespace. These methods are

  • AddItemsToTimeline
  • RemoveItemsFromTimeline

The parameters for both methods are the same. The parameters are included below in order:

  1. items – The items parameter was a tricky one to figure out. I tried a couple variations before I dug some more in the file. It turns out that it expects an array of objects and those objects need to have an id property.
  2. ListID – This is fairly self explanatory except that you need to make sure it is lower case without the curly braces.
  3. ViewName – The ViewName is always the same. The view is “Timeline”

Now with this information it is fairly easy to be able to setup a method to add a remove tasks from the timeline

function AddNewTaskToTimeline(itemID, listId){
    AddItemsToTimeline([{ 'id': itemID }], listId, 'Timeline');
}
function RemoveTaskFromTimeline(itemID, listId) {
    RemoveItemsFromTimeline([{ 'id': itemID }], listId, 'Timeline');
}

These functions could easily be placed as part of your own classes.

So we can add and remove tasks what next?

Now that we can add and remove the tasks on the timeline what is left for us to do? Oh, that’s right this is an enterprise system and we need to provide the user with proper feedback so that we can show them the proper text. Add when they want to add and remove when the task is already on the timeline. This one required some more noodling. I looked all over the JavaScript objects that are created as part of the sp.ui.timeline.js file and I found several places that contain the data that is on the timeline so I thought sweet I can just use those right? Wrong. You can see the items on the timeline but the script from Microsoft does not update these objects when the methods to add and remove are called. The out of the box functionality has the same limitation so I knew it wasn’t that I was adding them wrong. I tried reloading the page after adding but if you reloaded too quickly the add/remove didn’t happen.

After quite a bit of puzzling until my puzzler was sore I came up with an idea. A wonderfully awful idea. I realized that the information I needed was on the server so I thought I could write a service that would get me the data and verify if the task is on the timeline. As I started prepping for this route the SharePoint Developer in me said, “You don’t need to write your own service. Try the out of the box services.” So I did. It turns out that it requires a couple JSOM calls to get to the data I needed but the data is indeed available.

The first step is to load the list from the server. This is a fairly simple JSOM task so that one wasn’t too tricky.

function LoadList() {
    var ctx1 = SP.ClientContext.get_current();
    List = ctx1.get_web().get_lists().getByTitle(ListName);
    ctx1.load(List, "Title");
    ctx1.executeQueryAsync(LoadListCallback, QueryFailed);
}

I snag the list on page load after client context is loaded. I then hold on to this as a variable in my class so that I can use it for the second part.

The list didn’t have the value I needed so I had to go one step further. In looking at the approaches mentioned previously I realized that the data that I wanted is part of the Root Folder of the list. So I had to make another call to SharePoint to get the root folder.

IsTaskOnTimeline: function (taskId, Callout) {
    checkingId = taskId;
    callout = Callout;
    var ctx1 = SP.ClientContext.get_current();
    properties = List.get_rootFolder().get_properties();
    ctx1.load(properties);
    ctx1.executeQueryAsync(CheckTaskCallback, QueryFailed);
}

In my implementation I was  showing the option as part of a callout, another cool piece of functionality that was added in SharePoint 2013. I take the context and the list and go get the root folder  and its properties. Once I have this information in the callback I can now check if the task in question is on the timeline and update the action on the callout accordingly. This method, IstTaskOnTimeline is called every time that the callout is rendered thus making sure it is up to date. Thanks to jQuery parsing since the data is stored as XML it makes it easy to query the XML to see if the task I am looking for is on the timeline. The data comes back as some pretty ugly all on one line XML. But here it is formatted to make it easier to work with.

<TLViewData>
	<fmtSet>
		<fmt id="0" clr="FFEE2222" thm="0001" t1="0" t2="1" type="0" />
		<fmt id="1" clr="FFEE2222" thm="0001" t1="2" t2="3" type="1" />
		<fmt id="2" clr="FFEE2222" thm="0001" t1="4" t2="5" type="2" />
		<fmt id="3" clr="FFEE2222" thm="0001" t1="6" t2="7" type="3" />
	</fmtSet>
	<fltSet>
		<ft id="{00000000-0000-0000-0000-000000000000}" uid="4294967295" uidSrc="1" onTL="0" fmt="1" y="4294967282" x="0" h="20" />
	</fltSet>
	<tskSet>
		<t id="{00000000-0000-0000-0000-000000000000}" uid="4294967295" uidSrc="1" onTL="0" fmt="0" ch="4294967295" />
		<t id="{D8B18823-A736-4A9A-A804-0BD23BF1552D}" uid="23" uidSrc="1" onTL="1" fmt="0" ch="4294967295" />
		<t id="{52B9A142-C89C-40DC-83C8-33A00CF03B6C}" uid="26" uidSrc="1" onTL="1" fmt="0" ch="4294967295" />
		<t id="{E89E7256-F104-4AA9-BC53-13439E0EAA42}" uid="25" uidSrc="1" onTL="1" fmt="0" ch="4294967295" />
		<t id="{B6074014-2A71-45D5-BBAF-FB0DA2A201A1}" uid="24" uidSrc="1" onTL="1" fmt="0" ch="4294967295" />
		<t id="{C5789532-D420-4C9B-B704-D5CDEA3350EF}" uid="22" uidSrc="1" onTL="1" fmt="0" ch="4294967295" />
		<t id="{EFC756A8-1B16-495C-AB7D-112B31A59534}" uid="21" uidSrc="1" onTL="1" fmt="0" ch="4294967295" />
		<t id="{990078A6-BF87-4121-A720-ACFB1685A8B2}" uid="79" uidSrc="1" onTL="1" fmt="0" ch="4294967295" />
		<t id="{2FFCFA8C-7CD1-429F-BDA6-E6BB4BC3A71C}" uid="80" uidSrc="1" onTL="1" fmt="0" ch="4294967295" />
		<t id="{2D77DD43-A9F6-4B1D-8E6C-12474F3E3CFB}" uid="82" uidSrc="1" onTL="1" fmt="0" ch="4294967295" />
	</tskSet>
	<options dateFormat="3" panZoomT="9" ProjSummFmt="3" showDates="1" showProjSummDates="1" showToday="1" showTS="1" timelineHeight="110" timelineWidth="-1" timescaleT="8" todayT="10" />
	<mlSet>
		<m id="{00000000-0000-0000-0000-000000000000}" uid="4294967295" uidSrc="1" onTL="0" fmt="2" y="35" x="0" />
		<m id="{D8B18823-A736-4A9A-A804-0BD23BF1552D}" uid="23" uidSrc="1" onTL="1" fmt="2" y="17" x="60" />
		<m id="{52B9A142-C89C-40DC-83C8-33A00CF03B6C}" uid="26" uidSrc="1" onTL="1" fmt="2" y="5" x="49" />
		<m id="{E89E7256-F104-4AA9-BC53-13439E0EAA42}" uid="25" uidSrc="1" onTL="1" fmt="2" y="4294967273" x="4294967249" top="1" />
		<m id="{B6074014-2A71-45D5-BBAF-FB0DA2A201A1}" uid="24" uidSrc="1" onTL="1" fmt="2" y="4294967262" x="0" top="1" />
		<m id="{C5789532-D420-4C9B-B704-D5CDEA3350EF}" uid="22" uidSrc="1" onTL="1" fmt="2" y="4294967263" x="0" top="1" />
		<m id="{EFC756A8-1B16-495C-AB7D-112B31A59534}" uid="21" uidSrc="1" onTL="1" fmt="2" y="13" x="33" />
		<m id="{990078A6-BF87-4121-A720-ACFB1685A8B2}" uid="79" uidSrc="1" onTL="1" fmt="2" y="35" x="0" />
		<m id="{2FFCFA8C-7CD1-429F-BDA6-E6BB4BC3A71C}" uid="80" uidSrc="1" onTL="1" fmt="2" y="35" x="0" />
		<m id="{2D77DD43-A9F6-4B1D-8E6C-12474F3E3CFB}" uid="82" uidSrc="1" onTL="1" fmt="2" y="25" x="4294967020" />
	</mlSet>
	<txtSet>
		<style id="0" type="0" clr="FFEE2222" thm="0001" sz="8" font="Segoe UI" bold="0" ital="0" und="0" strk="0" />
		<style id="1" type="1" clr="FFEE2222" thm="0001" sz="8" font="Segoe UI" bold="0" ital="0" und="0" strk="0" />
		<style id="2" type="2" clr="FF999999" thm="0001" sz="8" font="Segoe UI" bold="0" ital="0" und="0" strk="0" />
		<style id="3" type="3" clr="FFB3B3B3" thm="0001" sz="8" font="Segoe UI Light" bold="0" ital="0" und="0" strk="0" />
		<style id="4" type="4" clr="FF525051" thm="0001" sz="10" font="Segoe UI" bold="0" ital="0" und="0" strk="0" />
		<style id="5" type="5" clr="FFB3B3B3" thm="0001" sz="8" font="Segoe UI Light" bold="0" ital="0" und="0" strk="0" />
		<style id="6" type="6" clr="FF999999" thm="0001" sz="9" font="Segoe UI" bold="0" ital="0" und="0" strk="0" />
		<style id="7" type="7" clr="FF999999" thm="0001" sz="8" font="Segoe UI" bold="0" ital="0" und="0" strk="0" />
		<style id="8" type="8" clr="FF999999" thm="0001" sz="8" font="Segoe UI" bold="0" ital="0" und="0" strk="0" />
		<style id="9" type="9" clr="FFFFA614" thm="0001" sz="8" font="Segoe UI Semibold" bold="1" ital="0" und="0" strk="0" />
		<style id="10" type="10" clr="FFFFA72B" thm="0001" sz="10" font="Segoe UI Semibold" bold="0" ital="0" und="0" strk="0" />
	</txtSet>
</TLViewData>

If you look at this XML you can see there is a lot going on. This is where all the position and formatting for the tasks is stored. The part that we care about is under the tskSet node. This node contains all the tasks that have ever been on the timeline on the list. This is important to keep in mind. If you remove a task from the timeline this task will remain in the tskSet node. Luckily it is easy to identify which tasks are on the timeline and which are not by the onTL attribute. Also on the t nodes there is a uid attribute. This is the item ID from the list item. So now that we have this information we can use jQuery to search the xml for us and tell us whether the task is on the timeline or not. Since jQuery parses HTML and for the most part now days HTML is XML, or really XML can be parsed like HTML and jQuery doesn’t care.

Here is out jQuery statement that will check for our task.

jQuery(properties.get_item("Timeline_Timeline")).find("t[uid='" + checkingId + "'][onTL='1']").length == 1

All we are doing is loading the XML into the jQuery parser then we call find on the jQuery object that represents our XML. We then call the find method and pass in selectors that will limit the objects returned to the one we care about. We are looking for all t nodes that have an attribute of uid that equals the id we are checking and an attribute called onTL with a value of one. If this returns any elemens, length greater than zero then the task is on the timeline. So we update the callout to display the appropriate value of Add or Remove. Then when the action is clicked on in the callout we call the appropriate add or remove function.

To me this is a much more elegant solution than changing the XML from the server or client. I’ll let Microsoft deal with their data the way they like. The beauty of this solution as well is that it will work with an on-premise SharePoint installation and it will also work if you want to use it in Office 365 or SharePoint Online.

Please let me know if you have any thoughts or questions.