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.