Vue.js & SharePoint

Vue.js & SharePoint (scratching a niche)

Hope you find this informative (I’m probably gonna catch a lot of sh|t for this, but hold your flaying until the end?).

[trigger warning] If you are a Vue purist, be warned, this is not for you or the faint of heart.

Intended audience:

This is really ONLY intended for those who have a corporate SharePoint system that can be utilized, NOT to be considered by those who are building applications with “dedicated” backend web and database services.

This is not a short article.  I have put as much into it as I could think of off the top of my head, based on what I’m currently actually doing (and I am using this in production).  I may ramble a bit, but I tried to put things into some sort of order. 🙂

Some history about my experiences… (skip this if you don’t need to sleep)

I had been successfully working with Microsoft LightSwitch (MS LS) a few years back and since Microsoft pulled the plug on that product line (evidently because of a patent troll), I was at a loss for something even remotely similar (which has proven to be unobtainium).

I turned to jQuery as a front end JavaScript(JS) solution to many of my JS woes and a Microsoft MVC .NET backend (a constraint).  I began writing Single Page Apps (similar to MS LS) which had caught on as a promising pattern and found that I was writing A LOT of code to just keep my view model and DOM in sync.  I knew there had to be a better way, so I looked into JS frameworks because they were supposed to help with that.  I saw Angular and React and Vue as the three modern frameworks that were prevalent in the ecosystem a couple years back.  I looked Angular and it looked familiar as an MVC structure, but it seemed redundant to create the same MVC structure on the server and client (I was using Microsoft .NET MVC pattern). I read that Vue was the easiest to learn and so I decided to check it out.  What follows is what I have learned and how I have used Vuejs to leverage my corporate IT Microsoft SharePoint as a headless backend to my Vue SPA client app(s).

I current am using the CDN method of Vue (which i know will probably cause 99% of you reading this to swipe left), NOT the NPM build method.  There are a few reasons I am using it in this way, which I will elaborate on in this article.

Microsoft SharePoint (MS SP) (2007-SP Online):

Microsoft SharePoint began adding a SOAP api in v2007 (maybe earlier) and a REST API in v2010.  The REST API follows the Microsoft initiated Odata spec which gives the user some neat features with respect to REST queries (select, filter, orderby, etc.). (I could have those versions later than actually implemented).  There are minor changes to the REST API but nothing too significant.

This gives MS SP the capability to be a “back end” to any modern “front end” method of SPAs.

Now, naturally, Microsoft would like you to continue to use their “front end” method or programming (.Net *.aspx, etc.) so that they have sway over your development methodologies.  That’s all well and good, but does not really embrace current development trends like SPAs and JS frameworks.

Why not use Nodejs?

Now I know you’re probably saying “Why not just use Nodejs server-side and be done?”.  Well, that’s a good question and I’ll address it here.  Many organizations have an existing implementation of SharePoint, but may not have any implementation of Nodejs (currently).  Nodejs typically replaces legacy web servers (like IIS natively or Apache, etc.) or similar functionality but you still need a database to capture most data sets for most applications (and even more than that for line of business applications).  If you are “stuck” using on-premise technologies (SQL Server, Oracle, etc.), then you don’t have the flexibility and freedom to pick any back end database that you can quickly spin up on a cloud service (MongoDB, etc.).

Additionally, some organizations have legacy systems that they prefer to continue to use because of money spent on those systems, so “new” implementations like nodejs create a whole new support requirement that they may not currently have in place.

Leveraging existing technologies:

SO, if you have an existing corporate on-premise (or SP Online will work too) SP system, this may just change the way you do things (I know it did for me).

Your company spent $$$ on a SharePoint system (or subscription) for mainly document web folder sharing and SharePoint list sharing (similar to an Excel worksheet).  That’s where SharePoint’s sweet spot is.  The rest of SharePoint is mostly hyperlinks with some CSS to give it a new look every version.   I know there are Access, Visio, and Excel, etc. services (btw, the latter removed from on-premise in 2016 and you have to install the online version of it), but I don’t know too many who actually “use” those technologies to their full potential (some folks hesitate to use them because they fear MS will discontinue that technology as they push customers to the “cloud”, see former part of this sentence for an example of that).

Since your company has so much $$$ invested in their SharePoint system, it really makes them happy when you can bring a web application to fruition within that framework.  That is exactly what I am doing currently and my customers (who don’t care where an application is hosted) and corporate executives (who invested in SharePoint) couldn’t be happier.

There are some constraints that you should be aware of:

You will find several articles on the internet about how SharePoint is NOT a database.  This is definitely true technically in the sense that SharePoint  does not have (inherently) many functions of a database server backend which can provide services such as replication, duplication, backup, etc.  BUT as a front end web developer, I want PERFORMANCE and don’t care about how the backend works as long as my REST end points work fast because it’s all about the UX.

That being said and acknowledged, SharePoint has the exact same standard structure (web client aka browser talking to a REST API for data consumption/storage) and most Microsoft Office products use it as well (Excel, Access, InfoPath, Visio, Project etc.).  So I asked myself “If Microsoft uses it for a viable REST data source, why can’t I?”.  The truth is that it works GREAT as a REST API backend to my Vuejs front end applications.

Ahhh, but the constraints…

SharePoint lists (everything is a list in SharePoint under the covers) have a 30,000,000 item limit which is pretty large, BUT SP Out-Of-The-Box (OOTB) will (by default) only display around 4500 “records” before it poops out (something about performance, blah, blah, blah!).  So if you want to store 30,000,000 records and display them all at the same time, this methodology is not for you.  BUT I have found that MANY line-of-business applications (especially workgroup/department level applications with 10s-100s of users)

[I guarantee that you can find at least one process that YOUR department implements that uses email (and possibly Excel) as the workflow/repository which takes significant unnecessary labor hours to aggregate or report against.]

do not require that level of record retention.  In addition, you can shard the data in any way you would like across multiple SharePoint lists (by month, by year, by last name, etc.) which allows you to have flexibility in your backend structure.

This is the only constraint that I have found that is significant in the development of web applications hosted in SharePoint.  I have not run into anything else that I could not do in the SharePoint environment (that means there may be more, but I haven’t found them yet).

Fragility (access control, access control, and access control):

With any application, it is as fragile as the people managing it permit.  If a DBA is not locking down the database or the web server administrator is not doing their due diligence in ensuring that everything is configured properly, the application is at risk of failure or data loss, data leakage, etc.  With a separation of duties, web administrators don’t typically have access to databases and DBAs don’t have access to web server configurations (I think you get the idea).

Within SharePoint, permissions are similar to Microsoft NTFS permissions (cumulative permissions, either inherited or direct, at the resource) and therefore access control is paramount in keeping the right people in and the wrong people out of certain areas.  For example, you would not want users being able to write/delete from the code document library, so care should be taken to ensure that the proper access is granted.  You would not want general users changing configuration for the entire application, so care should be taken there as well. I hope you’re getting the picture that access control is the key to keeping your application stable and available.

One saving grace of SharePoint is that it has a 30 day (30 by default) recycle bin for each site which can immediately restore a deleted item(s), returning inadvertent deletion of data or code back to production.  I don’t know for sure, but I don’t think SQL or Oracle does that.

What SharePoint provides OOTB:

MS SP provides authentication (usually integrated with Active Directory, which provides non-repudiation for those that need it, I use it for “signing” workflows without the need to digitally sign a PDF document because of the security link between the user’s AD account and their SP account, if your organization uses smart cards, then the user’s digital certificate is typically also associated with their AD account), authorization (SharePoint groups and site access control),  web hosting (SharePoint document libraries are perfect for name spacing and all relative paths are to the root of the doc lib), and fully relational data sources (SP Lists and Document Libraries) with full ODATA REST API end points per site.  SharePoint lists can reference another list (think of the lists as SQL tables) which provides a foreign key for the lookup list which is fully relational, just as a SQL table does which makes SharePoint lists capable of relational normalized data structures (I know, I know, it’s NOT a database).  Another capability that I have yet to investigate is the ability to have access control on a single item in a document library (a SP list cannot do this), which would give the ability to restrict access on an item level which typical database backends can’t even do.

So what that leaves me with (as a developer) is front end coding for my application and the business logic I need with respect to the UI.

Now some would argue that you should put your business logic on the server and if i were developing a public application that I wanted to make sure the “source” code was obfuscated, then I would agree.  BUT if I’m building an application for a known set of users and their roles, then this works VERY well.

Again, I am using the CDN version of Vuejs, which makes most developers run because it’s not as “slick” or “elegant” as a NPM build and requires more understanding of a WHOLE application.  Many web app developers only touch a portion of a complete application and don’t need to understand the entire application.  For my purposes, I am the only developer for a specific application and have to understand the entire application.  My method is somewhat unconventional but it was driven by constraints that I had to work within which pointed me to this methodology.  So, as a consequence, I write the “built” code instead of the “source” (which then gets transpiled into “built” code).  This is somewhat more coding, but i find that I know MUCH more about how my entire application works when i go this route.

SharePoint lists can have file attachments per item, but I typically use a document library for file uploads/downloads, it just makes the data storage cleaner.  SP Lists can have varied data types: string (0-255 char), long string(0-infinite), date, number, choice: (you specify the choices), SharePoint user object: (contains metadata about the authenticated user), lookup (think foreign key), and a few others.  The SharePoint REST API exposes all of these types.

SharePoint has server side JS libraries that you can load to perform server side functions (the most common for me is email).  I can generate an email in code, then use SOAP/REST to POST the message to the server and it will queue and forward with a SharePoint server side worker process.

Root file Naming:

SharePoint OOTB uses default.aspx as it’s default file name if none is specified in the URL.  i.e. if no file is specified, SharePoint will look for a default.aspx file to render.  This is an IIS setting, but SharePoint sets it OOTB.  So, by placing my HTML file (typically index.html) in the root of a document library and renaming it to default.aspx, I can reference the application with a [http://mysharepoint.mydomain.com/mydoclib/] which will automatically load the default.aspx file from SharePoint.  So instead of using index.html (as you might with an NPM build), I rename the file to default.aspx.  SharePoint is expecting aspx code, but it interprets the HTML perfectly without aspx code.

Vuejs:

I load Vuejs in my default.aspx file with a <script> tag and load all of my other dependency libraries as well.  I have a single Vue instance instantiated and my view model is contained within the <script> tag.  I typically use Vuejs filters for display filtering as well.

Other JS libraries:

I typically use Vuetifyjs for a Material Design UI setup (again, loading from a local folder in SharePoint).  I use axios for AJAX calls and momentjs for date formatting.  If I need graphing capabilities, I typically use D3js and C3js, depending on the graphing needs.

Development and Deployment:

When developing code, I use VSCode to edit my default.aspx.  I am old school and find (as a single developer on an application) that a single file for this type of application works best.  I know a co-worker prefers to work with smaller files, that’s great for him, but my preference is to work on a single file in VSCode (here’s where I’ll get a ton of sh|t, LOL).  One of the beauties of Vuejs is that it gives your code structure within the Vue instance, so you have markup (HTML) and the Vue view model.

Deployment could not be easier.  I literally drag my updated default.aspx file to my SharePoint document library and refresh my browser.  This is almost as fast as hot-reloading on NPM development, but it’s actually on the system that will host it, so dev/prod configurations are not an issue.  I typically have a DEV document library and a PROD document library, so when I am ready to “publish” my code, it’s literally a simple upload of my updated default.aspx file to my PROD document library.

Code re-usability:

My “built” code is stored in a SharePoint library and I can literally copy it to another library and it works because the root URL is determined in code.  I just have to build the “backend” data structure(s) (Schema) and voila, I have a full copy of the same application running in SharePoint at a different namespace.  If I need to re-brand it or change some things, it’s a matter of updating the code in the new location to the new requirement.

Keeping the data model out of the view model (dynamic schema detection):

OK, so here’s where some of the methodology shines.  I was typically adding the view model structure in my Vuejs code.  I was having to replicate the schema from SharePoint in my view model, so that when I did queries, they would return the correct information.  I realized that I could use REST to get the SCHEMA of the SharePoint list(s) and then use REST to query for the data.  So I build a getSPListSchema() method which returns the fields (columns) of the SharePoint list which I populate the view model with (using Vue.set()).  This allows for a VERY dynamic capability in the backend.  I can add a field (column) in SharePoint and then refresh my browser and the view model is automatically updated to the new schema.  Then I can just add it to my markup where I need it and immediately have a new field (column) in my application.  I cannot tell you how much time this saves when modifying the backend structure.  If a customer wants a “notes” field, I simply add it to my SharePoint list, add it to my markup and update my default.aspx file.

Leveraging SharePoint backend to control UI:

Typically, I need some sort of CRUD screen(s) (and Vuetifyjs has that OOTB), so I have to decide which fields (columns) are displayed in the list (grid) and which are displayed in a create/edit screen.  I would typically have to hard-code those in my view model, so that the create/edit screen has all of the fields required, but  you wouldn’t want all those fields in the grid.  I figured out that the REST API call which returns the fields of the list includes the Description field, which is typically not used but I decided to leverage that field in SharePoint to control some front end behavior (see fragility above).  I embed in the SharePoint list description field for a column the following settings:

show=0; this determines whether the field is displayed in a CRUD grid

order=1; this determines the order of the field in a CRUD grid

rename=My Renamed Field; this renames the field if required (instead of hard coding this in my view model)

These are obviously easily changed by someone with permissions, so see fragility above.

This can be used for pretty much anything you can think of within the constraints of the Description field.

Asynchronous programming:

Almost all of the CRUD applications that I have built use AJAX to retrieve/save data to SharePoint.  It took a little re-thinking about how I coded to get the AJAX right.  I typically need to do a REST call THEN do something else.  I typically use a callback passed to the AJAX call which is executed after the AJAX results are returned by the promise.  I only include this section to re-iterate that AJAX is obviously a key to making this methodology work.

Summary:

How I have implemented Vuejs in SharePoint is definitely not a common pattern.  Most SharePoint developers will want to embed Vuejs into a SharePoint master file or web part, etc. trying to retain the SharePoint “look and feel”.  I prefer to use Vuejs in a stand-alone SPA and control the entire browser, not relying on SharePoint front end structures at all.   Many SharePoint users complain of not being able to find anything in SharePoint, so I “hide” SharePoint from them and give them an interface that makes sense for what they want/need.

I hope this has given you some food for thought.  This is NOT intended to be a one-size-fits-all solution (because it isn’t).  There will be plenty of haters out there who will say this is an absolute abomination.  But given the constraints that have been placed on my organization and the tools that I DO have, I have found this VERY successful.

By using SharePoint as your hosting platform and database (I know, it’s NOT), you have effectively become a full stack developer without the constraints of having to deal with another department just to get your application up and running.

😉

Let the flaying begin…

 

…My next project is using Microsoft Project server (REST API) and a graphQL API to aggregate different data sources in a single UI.

 

Advertisements
Posted in Uncategorized | 1 Comment

Html client org chart

This assumes you know a bit about Microsoft LightSwitch.  If you don’t, go here first. 🙂

I needed an org chart for my LightSwitch client so I looked around and found the free Caprica jquery orgchart on github.

I created a LightSwitch application.

create

Then created an employee entity (this entity will be used for the entire org chart data):

created

Then added entity properties (Name <required>, Title, Department):

employees

Then added a circular reference back to this entity for the Manager (Many to zero/one):

relation

There are other ways to do this, but it seemed the most efficient way, but complexifies (is that a word?) the javascript.

Then created a “common screen set” for Employees (including Employee Employees aka Manager)

screen1

Changed the default Employees control from a “tile list” to a “table”

screen2

Ran (F5) the app to add employees. (note: the first employee (top of the org chart) does not have a manager).

screen3

Then I added more employees with all of them having Managers (i.e. the top node aka “top dog” doesn’t have a manager, everybody else does):

screen4

Completed list of employees:

screen15

I created another browse screen for the org chart view (OrgChartView):

screen6

Change the Employees contentItem control from the default “Tile List” to “Custom Control”:

screen7

In the Employees Query “Manage Included Data”, include the Manager:

screen12

Then click the “Edit Render Code” link from the properties box to create a stub in the orgchartview.js file :

screen8

At this point, all of this has been OOTB LightSwitch,  now I needed to get the orgchart.

I downloaded the Caprica jquery orgchart on github.  Copied the CSS and JS files into my solution:

screen9

Added a reference to them in the default.htm:

screen10

set the org chart view screen as the home screen:

screen11

Under the Render method of the orgchartview.js, added the following JavaScript code (this is what took the most time to figure out) .

NOTE: This code is for the structure of the entity that I created.  If you have a different structure, you’ll have to modify the code for your environment but this will give you a starting point:

screen19

Ran (F5) the app and get this!

screen16

I didn’t like the default coloring so I tweaked the jquery.orgchart.css file:

screen17

On hover, the active nodes highlight:

screen18

The nice thing about the the Caprica jquery orgchart on github is that it “collapses” when you hide child elements and re-aligns the level.

screen20

Notice that the spacing between “Spock” and “Checkov” has decreased.

ENJOY!

 

 

 

Here’s the text of the RENDER code (sorry, it doesn’t look pretty until you copy it into VS):

myapp.OrgChartView.Employees_render = function (element, contentItem) {
// Write code here.
contentItem.screen.getEmployees().then(function (employees) {
var html = ‘ <div> <ul id=”chartdata”><li><em>U.S.S. Enterprise Crew</em><ul>’
employees.data.forEach(function (employee) {
if (employee.Manager == null) {
var nodedata = ‘<li id=”‘ + employee.Id + ‘”>’ +
employee.Name + ‘<br/>’ +
employee.Department + ‘<br/>’ +
employee.Title + ‘<ul></ul></li>’
html = html + nodedata
} else {
var nodedata = ‘<li id=”‘ + employee.Id + ‘”>’ +
employee.Name + ‘<br/>’ +
employee.Department + ‘<br/>’ +
employee.Title + ‘<ul></ul></li>’
var existingmgrnode = html.indexOf(‘id=”‘ + employee.Manager.Id + ‘”‘)
var ulindex = html.substring(existingmgrnode).indexOf(“<ul>”);
ulindex = ulindex + 4
if (existingmgrnode) { // if the manager’s node has already been created
html = html.substring(0, existingmgrnode + ulindex) +
nodedata +
html.substring(existingmgrnode + ulindex)
}else{
html = html + nodedata
}
}
});
html = html + ‘</ul></li></ul></div> <div id=”main”> </div>’

$(element).append(html)
$(‘#chartdata’).orgChart({ container: $(element), interactive: true, fade: true, speed: ‘slow’ }); // use container: $(‘#main’) when testing to verify org chart structure
});
};

Posted in Uncategorized | 2 Comments

Microsoft Project Server type buttons

I like the Microsoft Project Server buttons that have the slide up “help” on them and so I thought I’d write up a post on how to add them to your HTML client.  I would also like to thank my friend Fernando Guerra for assisting me with the JavaScript modifications.

NOTE: This is only useful on DESKTOP applications, mobile apps don’t really have a “hover” like a mouse, but you can try hovering over it with your finger and see if it works?  😉

Quick Steps:

  1. obtain an icon for your button and load it into your project
  2. create a button in your html client screen
  3. modify the postrender() method with CSS to your style liking
  4. add JavaScript file reference to default.htm
  5. add JavaScript file to project

1. Creating an icon

I “borrowed” an editing icon from the internet for the button.  I typically use paint.NET to edit my images, so I just captured  the green from the MSProject Server buttons,

msprojbuttons

then created my 50×50 icon in paint.net (changed the colors and size) and then added it to my solution.

edit

2. Create a button on the HTML client screen (I called mine ShowBingButton and changed the display to “SHOW BING”):

createbutton

3. Modify the button properties in the postrender() method:

buttonpost

Add the following CSS to the postrender() method to style the button:

buttonpostcode 

myapp.BrowseVideos.ShowBingButton_postRender = function (element, contentItem) {
    // Write code here.
    $(“a”, element).css({“height”: “25px”}); // sets the hyperlink height
    $(element).children().css({  // sets properties of the button
        “padding-top”: “100px”,  // pads the top of the button to ‘push’ the text to the bottom
        “background”: “#317529”, // sets the green background
        “color”: “white”,        // sets the white text color
        “background-image”: “url(‘content/images/edit.png’)”,  //  sets the image to be used
        “background-repeat”: “no-repeat”,  // sets the background to only show once
        “background-position”:”center”  // positions the background image to center
    });
    $(element).attr(“title”, “This is the title of the button which (when clicked) will take you to BING.com”);  // sets the text that will slide up
    $(element).dropCaptions();  // calls the dropcaptions JavaScript method
    $(“.caption”).css({  // sets the properties of the caption that slides up
        “color”:”white”,  // white font
        “background”: “#333”,  // dark gray background
        “height”: “100px”,  // height of the caption
    });
};
JAVASCRIPT:
I modified the CatchMyFame’s  jquery.dropcaptions.js here to the following (then saved it as jquery.mydropcaptions.js:
/*
* jQuery Drop Captions plugin
* @version 2.0.0
* @date June 4, 2013
* @category jQuery plugin
* @copyright (c) 2009 admin@catchmyfame.com (www.catchmyfame.com)
* @license CC Attribution-Share Alike 3.0 – http://creativecommons.org/licenses/by-sa/3.0/
*/
(function($){
    $.fn.extend({
        dropCaptions: function(options){
            var defaults = {
                showSpeed : 300,
                hideSpeed: 300,
                showOpacity : .7,
                hideOpacity : 0,
                showEasing: ‘swing’,
                hideEasing: ‘swing’,
                showDelay: 0,
                hideDelay: 0
            };
            var options = $.extend(defaults, options);
            return this.each(function() {
                var o=options;
                var obj = $(this);
                //var objwidth = $(obj).width();
                var caption = $(obj).attr(‘title’); // Get the text of the caption from the image’s title attribute
                $(obj).wrap(‘<div></div>’).before(‘<div>’+caption+'</div>’).attr(‘title’,”); // Create a wrapping div, caption div, and remove the title from the image
                $(obj).parents(‘div.captainer’).width(150).css(‘float’, $(obj).css(‘float’));
                //$(obj).parents(‘div.captainer’).width($(obj).width() + parseInt($(obj).css(‘paddingLeft’), 10) + parseInt($(obj).css(‘paddingRight’), 10) + parseInt($(obj).css(‘borderLeftWidth’), 10) + parseInt($(obj).css(‘borderRightWidth’)), 10).css(‘float’, $(obj).css(‘float’)); // Copy the image’s width (+ border) and float to the wrapper div
                //$(obj).parents(‘div.captainer’).width(objwidth); // Copy the image’s width
                $(‘.captainer’).css({ ‘position’: ‘relative’}); // Set the wrapper div’s position
                $(‘.caption’).css({ ‘position’: ‘absolute’, ‘z-index’: ‘1’, ‘bottom’: ‘0’, ‘opacity’: o.hideOpacity, ‘color’: ‘white’, ‘left’: ’10px’,’font-size’:’12px’ }); // set the caption div’s position
                $(obj).parents(‘div.captainer’).css({‘margin’:$(obj).css(‘margin-top’)+’ ‘+$(obj).css(‘margin-right’)+’ ‘+$(obj).css(‘margin-bottom’)+’ ‘+$(obj).css(‘margin-left’)});   // Move the margin from the image to the wrapper div
                $(obj).css({‘position’:’relative’,’margin’:0});
                var moveAmount = parseInt($(obj).prev(‘div.caption’).height()+35, 10) + parseInt($(obj).prev(‘div.caption’).css(‘paddingTop’), 10) + parseInt($(obj).prev(‘div.caption’).css(‘paddingBottom’), 10);
                $(obj).on(‘mouseenter touchstart’, function (e) {
                    e.preventDefault();
                    setTimeout(function(){$(obj).prev(‘div.caption’).stop(true,false).animate({marginBottom:’+’+moveAmount +’px’,opacity:o.showOpacity},o.showSpeed,o.showEasing)},o.showDelay);
                }).on(‘mouseleave touchend’, function(){
                    setTimeout(function(){$(obj).prev(‘div.caption’).stop(true,false).animate({marginBottom:’0px’,opacity:o.hideOpacity},o.hideSpeed,o.hideEasing)},o.hideDelay);
                });
            });
        }
       });
})(jQuery);
4. Add the jquery.mydropcaptions.js file to the default.htm file
editdefault
5. Add the jquery.mydropcaptions.js file to the project:
addjstoproj
Run the project (F5)
demo1
when you hover over the “SHOW BING” text, you should see this:
demo2
You can tweak the colors, etc. to get the effect you like.
Congratulations!  🙂
Posted in Uncategorized | 1 Comment

HTML5 video in a LightSwitch HTML client

I was working on a project and needed to be able to play videos.  I knew  that HTML5 supported video streaming natively (with certain video file types) and so I decided to add it to my LightSwitch html client.  Here’s how I did it.

This is a pretty detailed instruction, but it starts from scratch.  Hope it helps you!

Note: I’m using Visual Studio 2012 for my screen shots, but this should be the same in Visual Studio 2013.  I have also selected C# as my language, but VB should work just as well!

Create a new LightSwitch html project (I called it “MyVideos”).

newproject

In the Solution Explorer, under Server, create a new table:

newtable

Rename the table to “Video” (the plural of “Videos” shows in Solution Explorer).

addfields

Add “URL”, “Caption”, “Poster” and “PosterURL” entity fields (I’ve made the caption and poster optional by unchecking the Required checkbox).

use:

URL – the url is the path that will be used by the html client to “find” the video and then render it in the client.  This can be a web address (for example: “http://myserver/Videos/myvideo.mp4&#8221;) or a relative address (for example: “videos/myvideo.mp4”).

Caption – the text you want to show when playing your video (again, this is optional).

Poster – the image you want to display in the video player before the video is played (again, this is optional)

PosterURL – the url to the image you want to display in the video player before the video is played.  This is the string used by the html5 tag to display the image before playing because the html5 tag cannot use the image in the database (again, this is optional).   Note: when you are testing, you will want to add the image to the “Content/Images” folder in your project so that it renders when you run the debugger.

Add a “Browse Videos” html client screen:

In the Solution Explorer, under HTMLClient(Startup), create a new screen:

addscreen

Select the browse screen template, select Videos from the dropdown ScreenData:

addbrowse

Formatting the “Browse Videos” screen:

In the designer, select the dropdown next to “Videos” and change it to “Tile List”

browseformat1

Now that we have a “browse” screen, we need to add a screen so that we can actually add records to the video table:

Add a “Add Videos” html client screen:

In the Solution Explorer, under HTMLClient(Startup), create a new screen:

addscreen

Select the “Add/Edit Details” screen template, select Videos from the dropdown ScreenData:

addadd

In order to “upload” a picture for the “poster” field, you’ll need the image uploader found here under “Add a custom control to upload Photos” (it also provides a user interface to select files from your computer).

Here are the screenshots in my project after the uploader code has been added:

Default.htm file:

imageupload1

Scripts folder:

imageupload2

Adding javascript code to “render” event in AddEditVideo screen

1. select “Poster” in designer, change it from Image to Custom Control.

imageupload2b

, 2. Click “Edit Render Code” in properties):

imageupload3

AddEditVideo.js file:

imageupload4

Back on the “Browse Videos” screen, add a button (for adding entries in the video table (entity)):

addbutton

In the “Add Button” dialog box, under “Choose an Existing Method”, select the “addAndEditNew” “built-in” method.

selectmethod

Your browse screen (now with a button) should look like this:

browsewithbutton

Now, if you start the debugger (press F5), you should see this…

f5

Clicking on the “Add Video” button…

addeditvideoscreen

Add a URL, caption, Poster image and Poster URL:

(note: check here for supported video types)

To test the video playback in development, create a folder called “Videos” in your solution folder under: “MyVideos\MyVideos\Bin\Debug\HTMLClient\” and place your video file(s) there.

Note: this would have to be copied to your server for production use. (you can use another location, just make sure you have the path relative to the server URL in production).

addeditvideoscreenadded

Click the SAVE button and you should see:

browseadded

So let’s clean up this view a bit and make it nicer to look at:

Stop the debugger (close IE) and then open the BrowseVideos screen.

browseedit

Right-Click the URL (and PosterURL) in the tree view and delete them:

deleteurleditbrowse5

change the order of Caption and Poster and change the parent layout from Rows to Columns:

editbrowse2

Now it should look like this:

modifiedbrowse

Up to now, this has all been “standard” LightSwitch html client stuff.

Here’s where the simple addition of some javascript code allows you to play the video in the html client:

Add another screen to play the video:

addplay

select the “View Details Screen” and select “Video” for the screen data:

 

 

viewvideo

In the BrowseVideos screen, add a TAP method to “view” the video:

editbrowse3

Select the “View Selected” under Video:

editbrowse4

(this will open the “View Video” screen)

In the “ViewVideo” screen, change the following:

viewvideo1

Delete the Rows Layout that contains the “poster”.

viewvideo2

Delete the URL:

viewvideo3

Add a second Caption and change the “lower” Caption to a custom control:

viewvideo7

Edit the Render Code in the Properties box:

viewvideo5

using the html code i found here:

<video controls>
<source src=”devstories.webm” type=’video/webm; codecs=”vp8, vorbis”‘/>
<source src=”devstories.mp4″ type=’video/mp4; codecs=”avc1.42E01E, mp4a.40.2″‘/>
</video>

we add this javascript to the “ViewVideo.js” file:

myapp.ViewVideo.Caption_render = function (element, contentItem) {
    // Write code here.
    // bind to the URL of the video passed to the screen
    contentItem.dataBind(“screen.Video.URL”,
     function (url) {
         // bind to the Poster URL of the image passed to the screen
         contentItem.dataBind(“screen.Video.PosterURL”,
          function (PosterURL) {
              // append this html to the caption custom control
              $(element).append(‘<video width=”400″ height=”225″ controls poster=”‘ + PosterURL + ‘”  > ‘ +
              ‘<source src=”‘ + url + ‘” type=”video/mp4″ />’ +
              ‘</video>’);
          });
     });
};

viewvideo6

 

now, clicking on the tile opens the view video screen:

readytoplay

 

Clicking on the PLAY button plays the video!!!!

playing

Congratulations!

 

 

Posted in Uncategorized | 10 Comments