11/15/15

SharePoint App List Lookup Delete Behavior

A SharePoint list can have a field type lookup which is a reference to another list. As an example, we have a client and project lists. The project list has a client lookup field which as reference uses the client list. As in any application, we need referential integrity, and we need to prevent the deletion of a reference/parent record by adding a constraint on the lookup field. (see sample project download at the end)


Add a Constraint to a List

In order to add a constraint to a lookup field, we need to open the schema.xml file under the list definition. Unfortunately for us, Visual Studio does not provide all the possible attributes for a list definition, so a manual change to the xml file is required. The schema.xml contains the metadata that defines the list and all the fields. We need to find the field that we need (in our case client) and add the following attributes:

RelationshipDeleteBehavior="Restrict"
Prevent deletes of the reference or parent record when a dependency exists.
Indexed="TRUE"
This field needs to be indexed in order to support a constraint. This field can also be updated from the properties tab.

Attribute Changes:



Features Deployment

Note*: When building SharePoint JavaScript apps using Visual Studio, we need to be familiar of how the features deployment works and how it can affect a list field lookup or relationship constraint.

Now that we have our lists definition and constraint, we need to make sure that dependencies are deployed first. During the deployment of the application, the deployment does not fail if a dependency is not defined first. In our example, the project list depends on the client list to be deployed first. If we deploy the project list first, the client relationship constraint will not be applied to the list, and the parent records could be deleted leaving orphan records. 

In order to prevent this deployment problem, we need to make sure that the features are deployed in the correct order. In our example (see image below), the Application feature (contains the web application artifacts) is defined before our lists. We then define the client and project lists.



By defining the client feature lists (with the client list definition and instance) before the project feature, we make sure that our constraint will be deployed properly. This helps us make sure that on the server data integrity is enforced. The order of the features can be rearranged by opening the package file under the package folder.  This opens an interface that allows us to move features up or down.



Now that we have our list definitions, we could move forward with building a SharePoint AngularJS SPA that shows how this constraint is actually working.  We will do this in another blog entry.

A sample of this project can be found here:  Github


Thanks

11/8/15

AngularJS Minimized File Unknown Provider

We have a working AngularJS single page application (SPA), but the moment we minimized the JS files, the app breaks with an Unknown Provider error.

When facing this problem, we need to look at how we are injecting dependencies into our modules because when minimizing the files, the parameters name will be changed and Angular would not be able to resolve the dependency.

Implicit (bad) Injection Approach

We start by looking at a common way of injecting dependencies which can lead into a problem.
    var app = angular.module('app', []);     
    app.config(appConfig);
    app.run(initApp);
    app.controller('appCtrl', appCtrl);
    
    function appConfig($myProvider) {
        $myProvider.appConfig();
    }

    function initApp(myService) {
        myService.initApp();
    }

    function appCtrl($scope) {
        $scope.appCtrl = 'hi ozkary';
    }

In this example, we are defining a module that has a configuration, app initialization and controller functions. When we define those functions, we are only setting the function names, but the function implementation has a parameter which indicates what needs to be injected. The problem with this approach is that when this code is minimized, it looks as follows:

function i(n){n.appConfig()}
function r(n){n.initApp()}
function u(n){n.appCtrl="hi ozkary"}
var t=n.module("app",[]);
t.config(i);
t.run(r);
t.controller("appCtrl",u)

As we can see, the parameters are renamed to “n” at which point there is probably no dependency with such name, and Angular is not able to find it. This is why we get the ugly Unknown Provider error.

Explicit Injection Declaration

To address this problem, we need to change the way we declare the dependencies using an array as parameter. With this approach, our code changes to this:

    var app = angular.module('app', []);
    app.config(['$myProvider', appConfig]);
    app.run(['myService', initApp]);
    app.controller('appCtrl', ['$scope', appCtrl]);
   
    function appConfig($myProvider) {
        $myProvider.appConfig();
    }

    function initApp(myService) {
        myService.initApp();
    }

    function appCtrl($scope) {
        $scope.appCtrl = 'hi ozkary';
    }

The advantage of this approach that is that the array values are not mangled by the minimization process, and this allows Angular to find and inject the dependency. The minimized version of our code now looks like this:

function i(n){n.appConfig()}
function r(n){n.initApp()}
function u(n){n.appCtrl="hi ozkary"}
var t=n.module("app",[]);
t.config(["$myProvider",i]);
t.run(["myService",r]);
t.controller("appCtrl",["$scope",u])

Yes, the dependency name is kept, and Angular should be able to resolve it. We can also notice that the parameter “n” is still used in the function, but what is important is the name declared on the array.
Moving forward, we should explicitly define our dependencies as minimization of resources is now a must standard.

Hope it helps.

10/24/15

SharePoint 2013 API OData For Lookup Fields

When using SharePoint data lists as the backend for an application, we can leverage the use of REST and OData to fetch the information from a data list. When the list contains lookup fields, a normal GET operation on the list only returns the id from the lookup field which may not be enough for some cases. 

In the case that more information is needed from the lookup or reference field, we can leverage the OData $expand parameter. This parameter is used to expand the related data so that in addition of the id other information also becomes available. As an example, we can look at a simple Project data list which has a lookup field named Client. This field uses the Client list as reference.

Project List Fields (internal names)
Client List Fields (internal names)
id
Id
Name
Name
Client  (lookup to Client)


How to fetch the data:

Note: my.sharepoint.com should be replaced with a specific domain name.


In this URL string, we are using the getbytitle API which allows us to query a list by name. The items route suffix indicates to fetch all the items with no explicit filters (where clause) and all the available fields. The resulting data contains the fields Id, Name, ClientId (where is this coming from?) and other fields that are created by the SharePoint platform.  For our purposes, our focus is only on our custom fields (Name, ClientId)


We can notice that there is a ClientId field, but the field that we defined is named Client. SharePoint by default returns the lookup field id using this format: field name + id. In this case, the name is ClientId which as we can guess is the id of the client record.

What about also getting the client name?

In the case that more lookup data is required as in the case of client name, we must use a couple of OData parameters to expand on the selection of the look up fields: $select, $expand

Parameter
Description
$select
Use this parameter to control what fields should be selected.
$expand
Use this field to indicate what lookup field should be expanded to get additional information from that field. Field to expand must be in the $select parameter.


As an example, our request can be changed to this: (no domain name listed)

_api/web/lists/getbytitle('project')/items?$select=Client/Name,ClientId,Name,Id&$expand=Client

URL Segments:

URL Segment
Description
_api/web/lists/getbytitle('project')/items
This is the API and route to query a list by name and retrieve the items (records)
$select=Client/Name,ClientId,Name,Id
OData request parameter to select fields by name

$expand=Client
OData request parameter to indicate what look up field should be expanded to get additional information.

When using the $expand query parameter, the $select query parameter must also be used, and the field to be expanded must be explicitly declared as shown on our example above. The result of this request returns the project name and id as well as client name and client id.  We should note the format used to get the client name, Client/Name. This format is what indicates that we want the Name field from the Client list.

Sample Result:

A sample of the response is shown below. The results array contains each record, for which we can find the selected fields as well as the client property (object) with the name property (client name).

Expanded Results:


As shown above, the USE of OData request parameters are very important when there is a need to get related/lookup data with one request.


Originally Posted on ozkary.com

10/17/15

SharePoint Choice Field Options with AngularJS $resource

A SharePoint data list can have a field of type = Choice which is used to provide the options for a dropdown control. When building SharePoint apps with AngularJS, we need to build the dropdown control with the options that are defined by the field’s metadata. Let’s see how we can go about to get this information.

In order to fetch the options from the Choice field from a given data list, we can use the REST APIs with OData operations. To show how this can be done using the AngularJS $resource service, we can take a look at an example in which we have a Task data list with a status field (type = Choice) with the following options:

Status (Choice) field options
Pending
In-Progress
Complete

We start by building an AngularJS service which uses the $resource service for our API calls. Note that we could also use the $http service as well.  Our service defines the $resource operation as follows: (note the relative path and the use of the getbytitle API route to get list information)

 var apiUrl = "../_api/web/lists/getbytitle('task')/:route";
       
        var api = $resource(apiUrl, { route: '@route' }, {
            //Other methods and routes
            status: { method: 'GET',params: {route:'fields'},isArray:false}        
        })

When making the call to get the data list status information, we need to send on the request an OData parameter to tell the API that we are interested in getting the information from a particular field. In our case, we need the metadata from the Status field which includes the choices that are available. Note that the Choice field does not provide a text/value pair. It only provides the text part of the options. This must be used for the value/text properties of the drop-down control. When updating the model and consequentially making a Create/Update API calls, we must persist the text value as this is the expected data (constraint) on the data list.

Our service and the Status method implementation to get the field's metadata looks like this:

function svcTask($q, $resource) {
        var apiUrl = "../_api/web/lists/getbytitle('task')/:route";
       
        var api = $resource(apiUrl, { route: '@route' }, {
            //Other methods and routes
            status: { method: 'GET', params: {route:'fields'}, isArray:false}        
        })
      
        var service = {
            /*
              name:status
              gets the options from a choice field type (sharepoint)
            */
            status: function () {
                var deferred = $q.defer();
 
 //gets the field by title
                var params = { '$filter': "Title eq 'Status'" };
               
api.status(params, function (data) {
                    statusOk(data);
                },
                    function (err) {
                        deferred.reject(err);
                    }
                );

                function statusOk(data) {
                    var result = data.value[0].Choices;
                    var list = [];
                    for (var idx = 0; idx < result.length; idx++) {
                        var item = { code: result[idx], name: result[idx] };
                        list.push(item);
                    }

                    service.statusList = list;
                    deferred.resolve(list);
                }
                return deferred.promise;
            }          
        }

        return service;
    }

A few notes about this code:
  • The getbytitle API is used to query a list metadata as well as data.
  • The Fields route segment is used to tell the API that we are interested in requesting field metadata information. This is where the options are defined.
  • The $filter OData parameter is used to tell the API which field we need to query by filtering on the title property.
  • The choices property is contained on the response Value property and has a results array.



The request is made by using the OData $filter request parameter. If the response is OK, the statusOk function is called. Otherwise an error is return by calling deferred.reject(err).

In the statusOk function, the code iterates thru the list and builds a JSON array that contains the value/text pairs. This is the model for our dropdown. Since there is a bit of data mapping/transformation that we need to do, and we want to encapsulate this away from the controller, the Status method returns a deferred promised. This allows us to handle the asynchronous request and perform the data mapping yet still keeping the asynchronous behavior. We then return the transformed data by calling deferred.resolve(list). 

The controller can now consume this service by calling the status method as shown below:

    function ctrlTask(svcTask) {

        var ctrl = this;
        ctrl.statusOptions = [];   //model for the status dropdown

        //load the status options waiting on a promise
        svcTask.status().then(function (data) {
            ctrl.statusOptions = data;
        },
            function (err) {
                $log.error(err);

            }
        );

    }

The ctrl.statusOptions is the model that holds the options for our dropdown on the view.

Finally, this is how the module is defined with both service and controller:

(function (angular) {
    'use strict';
    var appName = 'app';
    var app = angular.module(appName,['ngRoute', 'ngResource']);
    app.factory('svcTask', ['$q', '$resource', svcTask]);
    app.controller('ctrlTask', ['svcTask', ctrlTask]);


    function svcTask($q, $resource) {
        //...           
    }

    function ctrlTask(svcTask) {
        //...
    }


})(angular);


I hope this tip helps you build SharePoint Apps with AngularJS.

Origunally Posted on ozkary.com

9/20/15

AngularJS HTML5 Date Range Controller


When dealing with date input fields, we often rely on the use of JavaScripts date pickers to control how the user can enter the data.  These date pickers in most cases take away from the native experience especially on mobile devices.  

With the support of HTML5 in modern devices, we could eliminate the use of JavaScript date pickers and instead make use of the native date type support (more information here).  As an example,  Let’s take a look at how we can build a date range simple app with AngularJS and HTML5. 


Widget:





In this implementation of a date range app, we bind our dateRange model properties (startDate, endDate) to two input fields (from,to). The idea is that based on the start date selection, we define the constraints for our end date. In this example, our constraints are set to a future date and no more than seven days.

To enable the native date picker control, we set both input fields to “type=date”. This enables the device to render a native control for the date selection as well as the limit on what can be typed in the control.

As we change the start date, the on-change event is raised.  In the handler for this event, we set the min and max HTML5 properties which help us on set the edge input constraints for the end date field. In our case, we set the min to one day more than the start date and max to seven days more.  This is done by binding the model properties (minDate,maxDate) to the input field properties (min,max) respectively. We should also note that we must use the ISO date format (yyyy-MM-dd) otherwise our constraints would not be enforced.


The result of settings these edge constraints are reflected on the date picker as only the dates with-in the constraints are enabled. All other dates are disabled (grayed-out) and can’t be selected.



As devices adapt the HTML5 standards, our apps can leverage their native support which enables our apps with a much better user experience.

Thanks for reading.