Skip to content
This repository was archived by the owner on Apr 18, 2020. It is now read-only.

Angular Directive

Jaime Still edited this page Sep 10, 2016 · 2 revisions

For more information on directives, see AngularJS Developer Guide - Directives.

As with the wmiSvc, before we can define the directive logic, the directive function needs to be setup and registered with the angular module. This includes injecting the appropriate services as arguments into the directive function, then aliasing the function names using $inject.

(function () {
    var queryWmi = function (wmiSvc, toastrSvc) {
        return {
            // configure directive here
        };
    };

    queryWmi.$inject = ['wmiSvc', 'toastrSvc'];
    powershellApp.directive('queryWmi', queryWmi);
}());

To start, the directive is restricted to use as an element or an attribute, the directive element will be replaced with the template markup when rendered by angular, and the templateUrl is defined:

restrict: 'EA',
replace: true,
templateUrl: '/Content/templates/query-wmi.html'

There are two ways that you can manage the scope for a directive. The first is to define a controller function, then register the controller function with the directive using the controller property. The second, and the option I've grown to prefer, is to make use of the directive's link property function. There are several arguments that the link function can receive, but the only one that concerns this directive is the scope object. The first thing I do when defining the link function is initialize any of the variables that need to be defined on the scope. Looking at the end of the Angular Directive Template wiki, the following scope variables need to be defined:

model
resultsModel
pagedModel
loading

Note that newProperty does not need to be explicity defined because it is directly bound to the input element and will be created the first time data is input into the element.

link: function (scope) {
    scope.model = wmiSvc.queryModel;
    scope.pagedModel = {
        results: []
    };
    scope.resultsModel = wmiSvc.resultsModel;
    scope.loading = false;
}

After the scope variables are defined, I prefer to write out the local scope functions, which will be:

addProperty()
removeProperty()

The addProperty() function checks the scope.newProperty variable to determine if it is valid. If so, it checks to see if the property is already contained in the array. If either checks fail, a notification is toasted to the client alerting the user of the failed check. Otherwise, the property is pushed to the array and the scope.newProperty variable is cleared.

scope.addProperty = function () {
    if (scope.newProperty !== null && scope.newProperty !== undefined && scope.newProperty !== '') {
        if (scope.model.properties.indexOf(scope.newProperty) === -1) {
            scope.model.properties.push(scope.newProperty);
            scope.newProperty = '';
        } else {
            toastrSvc.alertWarning('The specified property has already been added', 'Invalid Property Value');
        }
    } else {
        toastrSvc.alertWarning('Property must have a value', 'Invalid Property Value');
    }
}

The removeProperty() function receives the property value corresponding to the remove button that is clicked on the interface. The index of the property compared to the properties array is assigned to an index variable. If the index is greater than -1, the property is spliced from the array. Otherwise, an alert is toasted to the user alerting that the property was not found in the array.

scope.removeProperty = function (property) {
    var index = scope.model.properties.indexOf(property);

    if (index > -1) {
        scope.model.properties.splice(index, 1);
    } else {
        toastrSvc.alertWarning('The specified property was not found in the property collection', 'Invalid Property Value');
    }
}

After the local scope functions are defined, the testQuery() function that calls the wmiSvc.queryWmi() function needs to be defined. Before that's done, however, I like to write a validation function that ensures that the model sent to the service function is valid.

function validate(model) {
    if (model.query === null || model.query === undefined || model.query === '') {
        toastrSvc.alertWarning('Query must have a value', 'Invalid Query Configuration');
        return false;
    }

    if (model.properties === null || model.properties === undefined || model.properties.length < 1) {
        toastrSvc.alertWarning('You must specify at least one property', 'Invalid Query Configuration');
        return false;
    }

    if (model.computername === null || model.computername === undefined || model.computername === '') {
        toastrSvc.alertWarning('You must specify the name of the computer to query', 'Invalid Query Configuration');
        return false;
    }

    if (model.wmiNamespace === null || model.wmiNamespace === undefined || model.wmiNamespace === '') {
        toastrSvc.alertWarning('You must specify the WMI Namespace', 'Invalid Query Configuration');
        return false;
    }

    if (model.username === null || model.username === undefined || model.username === '') {
        toastrSvc.alertWarning('Username is required for remote connections', 'Invalid Query Configuration');
        return false;
    }

    if (model.password === null || model.password === undefined || model.password === '') {
        toastrSvc.alertWarning('Password is required for remote connection', 'Invalid QueryConfiguration');
        return false;
    }

    return true;
}

With the validation function defined, the service function can now be defined. If scope.model is valid, the loading variable is set to true, any results contained in scope.resultsModel are cleared, and the service function is invoked. finally is used to set loading back to false regardless of whether the function call succeeded or failed.

scope.testQuery = function () {
    if (validate(scope.model) {
        scope.loading = true;
        scope.resultsModel.results = [];
        wmiSvc.queryWmi(scope.model).finally(function () {
            scope.loading = false;
        });
    }
}
Clone this wiki locally