During my early days I was bit scared writing JavaScript applications since I was so use to C# that I always try to find similar feature or offerings in JavaScript, I must say it took me sometime to really grasp the concepts because there are different ways to do a single task e.g. creating an object. I wanted to apply the basic principles of SOLID and found myself nowhere near to adhering to it.
SOLID:
Single responsibility (SRP)
Open Close (OCP)
Liskov substitution principle (LSP)
Interface segregation principle (ISP)
Dependency inversion principle (DIP)
Well I'm too young in JavaScript still to apply all the principles but have at-least I now able to partially apply SRP, DIP up to a certain extent.
Mostly I've seen in SharePoint Hosted apps developers writing a big fat JavaScript file which contains all the business logic and it becomes hard to read and debug (really I mean it).
Modular programming helps a lot here but to achieve one must need to understand modules (class) and how they can be defined in JavaScript, splitting your logic in modules certainly helps you to achieve SRP but that still doesn't solves the root problem as you are still loading the entire module at once which doesn't gives you much benefit in terms of performance. Well loading individual modules can be loaded but you need a solution, and the answer to that is Require.JS, it's an AMD (Asynchronous Module Definition implementation)
Well if you are SharePoint developer then you'll argue that why we need another framework for loading individual module which is already provided by SharePoint via SP.SOD (Script on Demand).
A quick comparison between both approaches
Require JS | SOD (Script On Demand) |
Highly Configurable | No Configurations |
Support to External Libraries | No Exports for External Libraries |
Inject, Exports, Shim | No exports or injection support (natively loads the plugin) |
Cleaner Dependency construct and resolution | Complex construct to write dependencies within external libraries |
I won't go in detail of how to setup require.js the documentation is itself self-explanatory trust me It took me a while to get this up but now it's a piece of cake !!!
I'll talk about some important part of the configuration which is required so that a SharePoint developer can leverage functionality and get benefitted from best of both worlds
The key aspect in require.js is configuration which is very powerful important attribute
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define(function () { | |
//debugger; | |
var rconfig = { | |
baseUrl: '../Scripts/app', | |
deps: | |
[ | |
'spruntime_js','sp_js' | |
], | |
paths: { | |
// follow app directory structure convention | |
// SharePoint Runtime and Core | |
// Register any SharePoint Library if it is required to be processed first | |
spruntime_js: window.location.origin + "/_layouts/15/sp.runtime", | |
sp_js: window.location.origin + "/_layouts/15/sp", | |
// Require JS Plugins, | |
domReady: 'lib/domReady', // For ensuring the dom is loaded | |
text: 'lib/text', // For Downloading the html templates | |
// Frameworks | |
jqueryan: 'lib/jquery-an', | |
jquery: 'lib/jquery-1.9.1', | |
ko: "lib/knockout-3.1.0", | |
boot: "modules/start", | |
ui_web: "modules/userinfo_web", | |
ui_profile: "modules/userinfo_userprofile" | |
}, | |
map: { | |
// '*' means all modules will get 'jquery-ps' | |
// for their 'jquery' dependency. | |
'*': { 'jquery': 'jqueryan' }, | |
// 'jquery-ps' wants the real jQuery module | |
// though. If this line was not here, there would | |
// be an unresolvable cyclic dependency. | |
'jqueryan': { 'jquery': 'jquery' } | |
}, | |
urlArgs: "bust=" + (new Date()).getTime() | |
}; | |
require.config(rconfig); | |
require(['boot']); | |
}); |
Above is my simple configuration for require.js
Please focus on deps and paths property
#1 Paths : The SharePoint JSOM runtime and main js to be loaded.
#2 Deps : Deps setting causes the runtime and sp.js file to be loaded before any module is loaded, thus making required core and necessary classes available for developer to program.
The Module
I've created two module which fetches the current user display name from web and user profile// Module for Web
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var userInfoWebModule = function (jQuery) { | |
// Define user class/module | |
var userModule = function () { | |
function _getUserName() { | |
var context = SP.ClientContext.get_current(); | |
var user = context.get_web().get_currentUser(); | |
context.load(user); | |
context.executeQueryAsync(onGetUserNameSuccess, onGetUserNameFail); | |
function onGetUserNameSuccess() { | |
var _msg = "Display name from Web : " + user.get_title(); | |
jQuery('#ui_web').text(_msg); | |
}; | |
// This function is executed if the above call fails | |
function onGetUserNameFail(sender, args) { | |
alert('Failed to get user name. Error:' + args.get_message()); | |
}; | |
}; | |
return { | |
getUserName : _getUserName | |
}; | |
}; | |
return userModule(); | |
}; | |
define(['jquery'], userInfoWebModule); |
// Module for User Profile
User Profile Code : http://www.vrdmn.com/2013/02/sharepoint-2013-working-with-user.html @vrdmn
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var userProfileModule = function (jQuery) { | |
// Define user class/module | |
var userProfileInfoModule = function () { | |
function _getUserName() { | |
var userProfileProperties; | |
//Get Current Context | |
var clientContext = new SP.ClientContext.get_current(); | |
//Get Instance of People Manager Class | |
var peopleManager = new SP.UserProfiles.PeopleManager(clientContext); | |
//Get properties of the current user | |
userProfileProperties = peopleManager.getMyProperties() | |
clientContext.load(userProfileProperties); | |
//Execute the Query. | |
clientContext.executeQueryAsync(onSuccess, onFail); | |
function onSuccess() { | |
var _msg = "Display name from User Profile Properties : " + userProfileProperties.get_displayName(); | |
jQuery("#ui_profile").text(_msg) | |
}; | |
function onFail(sender, args) { | |
alert("Error: " + args.get_message()); | |
}; | |
} | |
return { | |
getUserName: _getUserName | |
}; | |
}; | |
return userProfileInfoModule(); | |
}; | |
define(['jquery'], userProfileModule); |
The Start-up Module
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var startModule = function (jQuery,userInfoWeb,userInfoProfile) { | |
function ShowUserName() { | |
userInfoWeb.getUserName(); | |
userInfoProfile.getUserName(); | |
}; | |
SP.SOD.loadMultiple(['sp.js', 'userprofile'], ShowUserName); | |
}; | |
define(['jquery', 'ui_web', 'ui_profile'], startModule); | |
SP.SOD.loadMultiple(['sp.js', 'userprofile'], ShowUserName);
#1 The responsibility of ensuring that sp.js and sp.userprofile.js has been loaded is delegated to native SharePoint SOD module, the trick is to use SP.SOD.loadMultiple() which loads multiple modules and triggers the final function to be loaded.
Note: Here you can see the difference in the way require.js works and SOD works, require.js injects the dependencies as a parameters and SOD doesn't which gives more clear understanding of what to expect.
I've already published the sample at github: https://github.com/akhileshnirapure/sp_hosted_app_requirejs
Feel free to fork and if you have any question the reach me at Twitter: @AkhileshN
No comments:
Post a Comment