After I installed: http://msdn.microsoft.com/en-us/vstudio/dn722381.aspx.

Visual Studio 2013 would keep on crashing when opening a Multi-Device Hybrid Apps project.

I found the fix at: http://stackoverflow.com/questions/24404132/visual-studio-crashes-every-time-a-project-is-opened

 

Go to Tools > Options > Extensions and Updates > Add Web Essentials Nightly builds:

image

 

Then go to Tools > Extensions and Updates… (install the latest Web Essentials build)

image

 

I also updated to the latest version of the Windows Phone emulator, but I don’t think that is necessary.

 

 

Running a cordova AngularJS app from Visual Studio 2013 in the ripple:

 

image

 

Running app in the Android emulator from Visual Studio 2013:

 

Listing all available AVD’s

C:\Users\Roel\AppData\Local\Android\android-sdk\tools>android list targets

Creating a AVD

C:\Users\Roel\AppData\Local\Android\android-sdk\tools>android create avd -n rli_
_4__android_4.4.2 -t 4

 

image

 

Note: first time launch of the emulator might take a while in my case (2 minutes staring at a black screen with android logo on it Winking smile)

Tags:

 

In this post all will be extracting a data service from the code created in the previous posts and I will be adding some client side validation by using the breeze directive: data-z-validate.

 

Note

All code can be found at:

https://github.com/roelvanlisdonk/Research/tree/master/Research/Research.UI.Web/Client/Features/AngularJS_and_Breeze/Part4

 

image

 

Just by adding the data-z-validate attribute to the input elements in the grid, breeze will automatically show client side validation errors, by default an asterisk “*” is shown for required fields and a message is shown, when the user clears a required field.

image

 

In this case I also added a maxlength data annotation to the lastName field:

image

 

Model

namespace Research.UI.Web.Server.Model
{
    using System.ComponentModel.DataAnnotations;

    public class Employee
    {
        [Key]
        public int Id { get; set; }
        [Required]
        [MaxLength(20)]
        public string FirstName { get; set; }
        [Required]
        [MaxLength(50)]
        public string LastName { get; set; }
        [MaxLength(20)]
        public string PhoneNumber { get; set; }
    }
}

Note

I encountered a problem with the data-z-validate directive, this was caused by added the directive dynamically in a custom directive created by me, see: http://stackoverflow.com/questions/22849402/angular-directive-with-a-template-does-not-work-with-z-validate but this was fixed by altering my custom directive.

 

The custom directive with fix

// Angular directive [spaField] responsible for generating grid cell controls.
spa.app.directive('spaField', ['$compile', function ($compile)
{
    var directive = {
        restrict: 'A', /* Restrict this directive to attributes.  */
        replace: true, /* The given element will be replaced in the link function. */
        link: function ($scope, element, attrs)
        {
            // The data-z-validate directive will append a [span class="z-decorator"] to the following [input] element, by using the jquery "append" function.
            var html = '<input ng-disabled="{{vm.isKeyField(prop)}}" type="text" data-ng-model="entity[prop.name]" data-z-validate>';

            if ($scope.prop.relatedNavigationProperty) {
                var relatedTableName = $scope.prop.relatedNavigationProperty.nameOnServer;
                html = '<select kendo-combo-box ng-model="entity[prop.name]" ng-options="record.id as record.firstName for record in vm[\'' + relatedTableName + '\']"></select>';
            } else if ($scope.prop.dataType.name === "DateTime") {
                html = '<input kendo-date-picker k-ng-model="entity[prop.name]" k-format="\'dd-MMM-yyyy\'" />';
            } else if ($scope.prop.dataType.name === "Boolean")
            {
                html = '<input kendo-mobile-switch k-on-label="\'YES\'" k-off-label="\'NO\'" />';
            }

            // Apply scope to the created html fragment.
            var compiled = $compile(html)($scope);

            // Get the [<span class="z-decorator"] appended to the input element by the z-validate directive.
            var span = compiled[0].parentNode.children[1];

            // The following 2 lines will only add the input element to the DOM and not the [span class="z-decorator"], that is added by the z-validate directive.
            element.replaceWith(compiled);
            element = compiled;

            // Add the [span class="z-decorator"] to the current parent element of the input element.
            element.parent().append(span);
            element.parent().append(span);
        }
    };
    
    return directive;
}]);

Tags: , ,

 

Using Kendo UI mobile switch in AngularJS generated grid.

image

 

Using Kendo UI date picker in AngularJS generated grid.

image

 

Using Kendo UI combobox in AngularJS generated grid.

image

 

All code can be found at:

https://github.com/roelvanlisdonk/Research/tree/master/Research/Research.UI.Web/Client/Features/AngularJS_and_Breeze/Part3

 

Some highlights:

 

AngularJS directive

This directive will render the controls in the grid

spa.app.directive('ngField',['$compile', function ($compile) {

    var directive = {
        restrict: 'A', /* restrict this directive to elements */

        link: function ($scope, element, attrs) {
            var html = '<input ng-disabled="{{vm.isKeyField(prop)}}" type="text" ng-model="entity[prop.name]">';

            if ($scope.prop.relatedNavigationProperty) {
                var relatedTableName = $scope.prop.relatedNavigationProperty.nameOnServer;
                html = '<select kendo-combo-box ng-model="entity[prop.name]" ng-options="record.id as record.firstName for record in vm[\'' + relatedTableName + '\']"></select>';
            } else if ($scope.prop.dataType.name === "DateTime") {
                html = '<input kendo-date-picker k-ng-model="entity[prop.name]" k-format="\'dd-MMM-yyyy\'" />';
            } else if ($scope.prop.dataType.name === "Boolean")
            {
                html = '<input kendo-mobile-switch k-on-label="\'YES\'" k-off-label="\'NO\'" />';
            }

            var compiled = $compile(html)($scope);
            element.replaceWith(compiled);
            element = compiled;
        }
    };
    
    return directive;
}]);

 

HTML

The HTML shows a splash page with the text “Loading data…” by using the ng-cloak AngularJS directive.

By putting all external resources, like “link” and “script” tags at the bottom of the page and inlining the minimal CSS to show the splash page, the splash page is immediately shown to the user, while the AngularJS app is booted and the data is loaded.

<!DOCTYPE html>
<html data-ng-app="app">
<head>
    <title data-ng-bind="title">Angular and Breeze</title>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />

    <!-- All external resources are moved to the bottom of the page, to allow direct rendering of the [splash screen]. -->
    <!-- Only the styling for the [loader / splash screen] is included in the head. -->
    <style type=text/css>
        html, body {
            -moz-box-sizing: border-box;
            -webkit-box-sizing: border-box;
            box-sizing: border-box; /* Border boxing is used, so the padding, margin and borders are within the width and height of the element. */
            height: 100%;           /* Full screen single page app. */
            margin: 0;              /* Prevent unnecessary white space. */
            max-height: 100%;       /* Full screen single page app. */
            outline: 0;             /* Prevent unnecessary white space. */
            padding: 0;             /* Prevent unnecessary white space. */
        }

        body {
            padding: 20px;
        }

        div.spa-splash {
            display: none;
        }

        div.spa-splash, div.spa-splash > div {
            -moz-box-sizing: border-box;
            -webkit-box-sizing: border-box;
            box-sizing: border-box; /* Border boxing is used, so the padding, margin and borders are within the width and height of the element. */
            height: 100%;           /* Full screen single page app. */
            max-height: 100%;       /* Full screen single page app. */
            padding: 20px;            
        }

        div.spa-splash > div {
            border: 1px solid rgb(212, 212, 212);
            padding: 10px;
        }

        [ng-cloak].spa-splash {
          display: block !important;
        }

        [ng-cloak] {
            display: none;
        }
    </style>
</head>
<body>
    <div class="spa-splash" ng-cloak><div>Loading data...</div></div>
    <div class="spa-page container" data-ng-controller="admin as vm" ng-cloak>
        <div class="spa-page-sidebar">
            <div>Tables</div>
            <div ng-repeat="entityType in vm.entityTypes"><a class="spa-action-link" ng-click="vm.refresh(entityType)">{{ entityType.shortName }}</a></div>
        </div>
        <div class="spa-page-content">
            <div class="spa-grid-toolbar">
                <a class="spa-action-link" ng-click="vm.save()">save</a> |
                <a class="spa-action-link" ng-click="vm.reset()">reset</a> |
                <a class="spa-action-link" ng-click="vm.create()">create</a>
                <i class="fa fa-exclamation-circle"
                   title="Some data has change. Press save to save the changes to the server!"
                   ng-show="vm.isDirty"></i>
            </div>
            <table class="spa-grid">
                <thead>
                    <tr>
                        <th> </th>
                        <th ng-repeat='(key, prop) in vm.entityDataFields'>{{ prop.name }}</th>
                    </tr>
                </thead>
                <tbody>
                    <tr ng-repeat="entity in vm.entities">
                        <td><a class="spa-action-link" ng-click="vm.delete(entity)">delete</a></td>
                        <td ng-repeat='(key, prop) in vm.entityDataFields'>
                            <div ng-field></div>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>

    <!-- Libraries -->
    <link rel="stylesheet" type="text/css" href="../../../Libraries/FontAwesome/css/font-awesome.min.css" />
    <link rel="stylesheet" type="text/css" href="../../../Libraries/Toastr/toastr.min.css" />

    <!-- App -->
    <link rel="stylesheet" type="text/css" href="app.css" />

    <!-- Libraries -->
    <script type="text/javascript" src="../../../Libraries/jQuery/jquery-2.1.1.js"></script>
    <script type="text/javascript" src="../../../Libraries/Angular/angular.js"></script>

    <!-- Kendo -->
    <link rel="stylesheet" type="text/css" href="//cdn.kendostatic.com/2014.2.625/styles/kendo.common.min.css" />
    <link rel="stylesheet" type="text/css" href="//cdn.kendostatic.com/2014.2.625/styles/kendo.rtl.min.css" />
    <link rel="stylesheet" type="text/css" href="//cdn.kendostatic.com/2014.2.625/styles/kendo.metro.min.css" />
    <link rel="stylesheet" type="text/css" href="//cdn.kendostatic.com/2014.2.625/styles/kendo.metro.mobile.min.css" />
    <script type="text/javascript" src="//cdn.kendostatic.com/2014.2.625/js/kendo.all.min.js"></script>

    <!-- Breeze -->
    <script type="text/javascript" src="../../../Libraries/Breeze/breeze.debug.js"></script>
    <script type="text/javascript" src="../../../Libraries/Breeze/breeze.angular.js"></script>

    <!-- Add toastr which needs jQuery (Breeze does not need jQuery). -->
    <script type="text/javascript" src="../../../Libraries/Toastr/toastr.js"></script>
    <script type="text/javascript" src="../../../Libraries/Moment/moment.js"></script>
    <script type="text/javascript" src="../../../Libraries/Spin/spin.js"></script>

    <!-- Add breeze.savequeuing which needs Q (Breeze does not need Q). -->
    <script type="text/javascript" src="../../../Libraries/Q/q.min.js"></script>
    <script type="text/javascript" src="../../../Libraries/Breeze/breeze.savequeuing.js"></script>

    <!-- App -->
    <script type="text/javascript" src="app.js"></script>
</body>
</html>

Tags: , , ,

Roel van Lisdonk on July 14th, 2014

The native confirm dialog, shown when you use the confirm function in JavaScript can’t be positioned in JavaScript.

A Kendo UI window can be positioned.

 

 

image

 

HTML

<!DOCTYPE html>
<html>
<head>
    <title>Kendo element binding research page.</title>
    <link rel="stylesheet" type="text/css" href="//cdn.kendostatic.com/2014.1.528/styles/kendo.common.min.css" />
    <link rel="stylesheet" type="text/css" href="//cdn.kendostatic.com/2014.1.528/styles/kendo.rtl.min.css" />
    <link rel="stylesheet" type="text/css" href="//cdn.kendostatic.com/2014.1.528/styles/kendo.metro.min.css" />
    <link rel="stylesheet" type="text/css" href="//cdn.kendostatic.com/2014.1.528/styles/kendo.metro.mobile.min.css" />
    <link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css">
    <style>
        /* Resets */
        div, span, i, p, input, select {
            -moz-box-sizing: border-box;
            -webkit-box-sizing: border-box;
            box-sizing: border-box; /* Border boxing is used, so the padding, margin and borders are within the width and height of de element. */
            color: rgba(112, 112, 112, 1);
            font-family: Arial, Helvetica, sans-serif; /* For know use default fonts used on google.com stackoverflow.com, telerik.com etc. */
            font-size: 13px;
            margin: 0; /* Margin zero is used to prevent unnecessary white space. */
            padding: 0; /* Padding zero is used to prevent unnecessary white space. */
        }        

        html, body {
            height: 100%;
            max-height: 100%;
        }

        body {
            padding: 20px;
        }

        .page {
            border: 1px solid rgb(212, 212, 212);
            height: 100%;
            max-height: 100%;
            padding: 10px;
            position: relative;
        }

        #confirm > p {
            padding-bottom: 20px;
        }

        .k-button {
            cursor: pointer;
        }

        .k-window-titlebar, .k-window-titlebar > span {
            cursor: move;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <div class="page">
        <button class="k-button" data-bind="click: onShowClick">Show confirm</button>
        <div id="confirm"></div>
    </div>
    <script type="text/x-kendo-template" id="confirmTemplate">
        <p>#= ConfirmText #</p>
        <button class="k-button" id="yesButton">Yes</button>
        <button class="k-button" id="noButton"> No</button>
    </script>
    <!-- Libraries -->
    <script type="text/javascript" src="//code.jquery.com/jquery-1.9.1.min.js"></script>
    <script type="text/javascript" src="//cdn.kendostatic.com/2014.1.528/js/kendo.all.min.js"></script>
    
    <!-- Custom -->
    <script type="text/javascript" src="app.js"></script>
</body>
</html>

 

Code

var kendo = kendo || {};

kendo.controller = (function () {
    var self = {};
    var _vm = null;
    var _confirm = null;
    var _confirmTemplate = null;

    self.show = function () {
        _confirm =  $("#confirm").kendoWindow({
            title: "Delete record",
            visible: false, // The window will not appear before its .open method is called.
            width: "230px",
            height: "100px",
        }).data("kendoWindow");
        _confirmTemplate = kendo.template($("#confirmTemplate").html());
        _vm = new kendo.data.ObservableObject({
            onShowClick: function () {
                // Dynamically set some window options.
                _confirm.setOptions({
                    position: {
                        top: 200,
                        left: 200
                    }
                });
                _confirm.content(_confirmTemplate({ ConfirmText: "Delete record?"}));
                _confirm.open();

                $("#yesButton").click(function () {

                    // TODO: Delete record.
                    _confirm.close();
                })
                $("#noButton").click(function () {
                    // User cancelled the window.
                    _confirm.close();
                })
            }
        });

        kendo.bind($(".page"), _vm);
    };

    return self;
})();

kendo.controller.show();

Tags: ,

 

Q or $q

If you are using Q or $q you can pas extra data to the then function by using the $q.all function:

function execute()
{
    var firstNames = ['Bo', 'Cris', 'Richard'];
    $q.all({
        firstNames: $q.when(firstNames),
        lastNames: $http.get('/api/lastNames')
    }).then(handleGetLastNamesResult)
}

function handleGetLastNamesResult(data)
{
    var firstNames = data.firstNames;
    var lastNames = data.lastNames;
}

 

jQuery

 

In this example I will use jQuery, but the same pattern works for $q and Q.

You can make an Ajax request in jQuery like, so:

var app = (function ()
{
    var self = {};
        
    self.handleGetResult = function (data) {
        // Do something with the result from the server.
    };

    self.start = function ()
    {
        var promise = $.ajax({
            url: 'https://api.github.com/users/roelvanlisdonk/repos',
            type: 'GET'
        });

        $.when(promise).then(self.handleGetResult);
    };

    self.start();

    return self;
})();

The function passed to the .then function can only contain one parameter and will contain the data returned form the server. If you want to pass the function called by the “.then” function some additional data. You can use the following pattern:

 

var app = (function ()
{
    var self = {};
    
    self.handleGetResult = function (data) {
        var resultHandler = this;
        var additionalData = resultHandler.getAdditionalData();

        // Do something with the result from the server and the additional data.
    };

    self.ResultHandler = function (additionalData, handleResultFunc) {
        var self = this;
        var _additionalData = additionalData;
        var _handleResultFunc = handleResultFunc;

        self.getAdditionalData = function () {
            return _additionalData;
        };

        self.handleResult = function (data) {
            _handleResultFunc.call(self, data);
        };

        return self;
    };

    self.start = function ()
    {
        var promise = $.ajax({
            url: 'https://api.github.com/users/roelvanlisdonk/repos',
            type: 'GET'
        });

        var handler = new self.ResultHandler("Some extra data", self.handleGetResult);
        $.when(promise).then(handler.handleResult);
    };

    self.start();

    return self;
})();

Tags: , ,

 

In my previous post I created a simple [AngularJS – Breeze] edit view. In this post I will add the create, delete, reset and “is dirty” (entity change state tracking) to the simple view.

 

Before create

image

 

Create

The user clicked on the create button, so an “is dirty” icon is shown and a new row is added to the grid.

image

 

Save

When the user fills the fields of the new row and clicks save, the “is dirty” icon will disappear and the id field will automatically be filled by the id generated on the server in the database.

 

image

 

Reset

When the user create, updated and deleted some records and pressed save, the original state of the database can be restored by pressing the reset button.

image

 

 

index.html

<!DOCTYPE html>
<html data-ng-app="app">
<head>
    <title data-ng-bind="title">Angular and Breeze</title>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />

    <!-- Libraries -->
    <link rel="stylesheet" type="text/css" href="../../../Libraries/FontAwesome/css/font-awesome.min.css" />
    <link rel="stylesheet" type="text/css" href="../../../Libraries/Toastr/toastr.min.css" />
    
    <!-- App -->
    <link rel="stylesheet" type="text/css" href="app.css" />
</head>
<body>
    <div class="spa-page" data-ng-controller="admin as vm">
        <div class="spa-grid-toolbar">
            <a class="spa-action-link" ng-click="vm.save()">save</a> |
            <a class="spa-action-link" ng-click="vm.reset()">reset</a> |
            <a class="spa-action-link" ng-click="vm.create()">create</a>
            <i class="fa fa-exclamation-circle" 
               title="Some data has change. Press save to save the changes to the server!" 
               ng-show="vm.isDirty"></i>
        </div>
        <table class="spa-grid">
            <thead>
                <tr>
                    <th> </th>
                    <th ng-repeat='(key, prop) in vm.entityFields'>{{ prop.name }}</th>
                </tr>
            </thead>
            <tbody>
                <tr ng-repeat="entity in vm.entities">
                    <td><a class="spa-action-link" ng-click="vm.delete(entity)">delete</a></td>
                    <td ng-repeat='(key, prop) in vm.entityFields'>
                        <input ng-disabled="{{vm.isReadOnlyField(prop.name)}}" 
                               type='text' 
                               ng-model='entity[prop.name]'>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>

    <!-- Libraries -->
    <script type="text/javascript" src="../../../Libraries/Angular/angular.js"></script>
    <script type="text/javascript" src="../../../Libraries/Breeze/breeze.debug.js"></script>
    <script type="text/javascript" src="../../../Libraries/Breeze/breeze.angular.js"></script>

    <!-- Add toastr which needs jQuery (Breeze does not need jQuery) -->
    <script type="text/javascript" src="../../../Libraries/jQuery/jquery-2.1.1.js"></script>
    <script type="text/javascript" src="../../../Libraries/Toastr/toastr.js"></script>
    
<!-- Add breeze.savequeuing which needs Q (Breeze does not need Q)--> <script type="text/javascript" src="../../../Libraries/Q/q.min.js"></script> <script type="text/javascript" src="../../../Libraries/Breeze/breeze.savequeuing.js"></script> <!-- App --> <script type="text/javascript" src="app.js"></script> </body> </html>

 

 

app.js

// Use namespaces to prevent pollution of the global namespace.
var spa = spa || {};
spa.controllers = spa.controllers || {};

// Angular module [app].
spa.app = (function () {
    'use strict';

    var app = angular.module('app', [
        'breeze.angular' // The breeze service module.
    ]);
})();

// Angular controller [admin].
spa.controllers.admin = (function () {
    'use strict';
    var controllerId = 'admin';
    angular.module('app').controller(controllerId, ['$http', 'breeze', admin]);

    function admin($http, breeze) {
        var entityChangedToken = null;
        var entityTypeName = "Employee";
        var manager = null;

        var vm = this;
        vm.create = function () {
            // Create entity by breeze.
            var entity = manager.createEntity(entityTypeName);
            // Show entity to user.
            vm.entities.push(entity);
        };
        vm.delete = function (entity) {
            // Delete from UI
            vm.entities.pop(entity);

            // Mark for deletion.
            entity.entityAspect.setDeleted();
        };
        vm.entities = [];
        vm.entityFields = null;
        vm.isDirty = false;
        vm.isReadOnlyField = function (name) {
            // Make 'id' fields read-only.
            return (name === 'id');
        };
        vm.reset = function () {
            // Re-seed database and refetch data.
            $http.get('/breeze/breeze/ReSeed').then(getData).then(handleResetResult).catch(showError);
        };
        vm.save = function () {
            manager.saveChanges().then(handleSaveResult).catch(showError);
        };

        function handleStateChange(args) {
            vm.isDirty = true;
        }

        function getData() {
            // Get entities from the server.
            var query = new breeze.EntityQuery().from(entityTypeName);
            manager.executeQuery(query).then(handleGetDataResult).catch(showError);
        }

        function initialize() {
            // Use camel case for entity properties.
            breeze.NamingConvention.camelCase.setAsDefault();

            // Configure and create EntityManager (double breeze is needed, because of .
            manager = new breeze.EntityManager('/breeze/breeze');
            manager.enableSaveQueuing(true);
            registerForStateChange();

            getData();
        }

        function handleGetDataResult(data) {
            // Get entity fields from metadata.
            var entityMetaData = manager.metadataStore.getEntityType(entityTypeName);
            vm.entityFields = entityMetaData.dataProperties;

            // Show the enties from the server.
            vm.entities = data.results;
            vm.isDirty = false;
        }

        function handleResetResult() {
            vm.isDirty = false;
            toastr.info("Database re-seeded.");
        }

        function handleSaveResult() {
            vm.isDirty = false;
            toastr.info("Changes saved to the server.");
        }

        function registerForStateChange() {
            // Make sure to only subscribe once.
            if (entityChangedToken) { return; }

            // Register for state change.
            entityChangedToken = manager.entityChanged.subscribe(handleStateChange);
        }

        function showError(e) {
            // Show xhr error.
            toastr.error(e);
        }

        initialize();
    }
})();

 

app.css

/* Resets */
html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre,abbr, address, cite, code,
del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset,
form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, 
figcaption, figure, footer, header, hgroup, menu, nav, section, summary, time, mark, audio, video {
    border: 0;              /* Prevent unnecessary white space. */
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    box-sizing: border-box; /*  Border boxing is used, so the padding, margin and borders are within 
                                the width and height of the element. */
    margin: 0;              /* Prevent unnecessary white space. */
    outline: 0;             /* Prevent unnecessary white space. */
    padding: 0;             /* Prevent unnecessary white space. */
    font-size: 100%;
    vertical-align: baseline;
}

html, body {
    height: 100%;       /* Full screen single page app. */
    max-height: 100%;   /* Full screen single page app. */
}

body {
    padding: 20px;
}

a {
    cursor: pointer;
}

a.spa-action-link {
    color: #428bca;
    text-decoration: none;
}

a.spa-action-link:hover, a.spa-action-link:focus {
    color: #2a6496;
    text-decoration: underline;
}

div.spa-page {
    border: 1px solid rgb(212, 212, 212);
    height: 100%;       /* Full screen single page app. */
    max-height: 100%;   /* Full screen single page app. */
    padding: 20px;
    position: relative;
}

div.spa-page > a {
    margin-bottom: 10px;
}

div.spa-grid-toolbar i.fa-exclamation-circle {
    margin-left: 10px;
}

.spa-grid input[type="text"] {
    padding-left: 2px;
}

 

BreezeController.cs

namespace Research.UI.Web.Server.Controllers
{
    using Breeze.ContextProvider;
    using Breeze.ContextProvider.EF6;
    using Breeze.WebApi2;
    using Newtonsoft.Json.Linq;
    using Research.UI.Web.Server.Model;
    using System.Data.Entity.Migrations;
    using System.Linq;
    using System.Web.Http;

    [BreezeController]
    public class BreezeController : ApiController
    {
        private readonly EFContextProvider<ResearchDbContext> _contextProvider;

        public BreezeController(): this(null)
        {
        }

        public BreezeController(EFContextProvider<ResearchDbContext> contextProvider)
        {
            _contextProvider = contextProvider ??  new EFContextProvider<ResearchDbContext>();
        }

        [HttpGet]
        public IQueryable<Employee> Employee()
        {
            return _contextProvider.Context.Employees;
        }

        [HttpGet]
        public string Metadata()
        {
            string result = _contextProvider.Metadata();
            return result;
        }
                        
        [HttpGet]
        public void ReSeed()
        {
            // Remove all records from the "Employees" table.
            _contextProvider.Context.Database.ExecuteSqlCommand("truncate table Employees");

            // Run an "Update-Database" EF migrations command, this will update the database schema 
// to the latest state and run the Seed() method.
var configuration = new Research.UI.Web.Migrations.Configuration(); configuration.ContextType = typeof(ResearchDbContext); var migrator = new DbMigrator(configuration); migrator.Update(); } [HttpPost] public SaveResult SaveChanges(JObject saveBundle) { return _contextProvider.SaveChanges(saveBundle); } } }

 

 

For the complete code, see:

https://github.com/roelvanlisdonk/Research/tree/master/Research/Research.UI.Web/Client/Features/AngularJS_and_Breeze/Part2

Tags: , , , ,

 

I wanted a really simple view in AngularJS, that would allow me to edit the data in a database table.

The view should show a grid containing a row for each record in the database.

Each record should show textboxes (html inputs) for all columns in the database, except the “Id” column.

In this way I could edit all data in a database table.

 

Here’s the end result:

 

image

 

When the user clicks on the “save” button, the data is persisted to the database.

 

image

 

 

Breeze.js

I decided to use Breeze.js for the data interaction.

The main trick I used to automatically data bind the Breeze.js entity data properties (fields) to the UI in AngularJS, is getting the names of the entity data properties (database table column names) from the metadata and then using a ng-repeat to loop over the table records and within this ng-repeat an other ng-repeat to loop over de Breeze entity data properties.

 

Getting table column names with Breeze (part of the client side code):

var entityMetaData = manager.metadataStore.getEntityType(“Employee”);

vm.entityFields = entityMetaData.dataProperties;

Automatically data bind, Breeze entity data properties (part of the client side code):

<tbody> <tr ng-repeat="entity in vm.entities"> <td ng-repeat='(key, prop) in vm.entityFields'>
<input ng-disabled="{{vm.isReadOnlyField(prop.name)}}" type='text' ng-model='entity[prop.name]'>
</
td> </tr> </tbody>

Client side code

<!DOCTYPE html>
<html data-ng-app="app">
<head>
    <title data-ng-bind="title">Research page</title>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, 
user-scalable=no" />
<!-- Libraries --> <link rel="stylesheet" type="text/css" href="../../Libraries/Toastr/toastr.min.css" /> <!-- App --> <style> /* Resets */ html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead,
tr, th, td, article, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav,
section, summary, time, mark, audio, video { border: 0; /* Prevent unnecessary white space. */ -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; /* Padding, margin and borders are within the width and height of the element. */ margin: 0; /* Prevent unnecessary white space. */ outline: 0; /* Prevent unnecessary white space. */ padding: 0; /* Prevent unnecessary white space. */ font-size: 100%; vertical-align: baseline; } html, body { height: 100%; /* Full screen single page app. */ max-height: 100%; /* Full screen single page app. */ } body { padding: 20px; } a { cursor: pointer; } a.spa-action-link { color: #428bca; text-decoration: none; } a.spa-action-link:hover, a.spa-action-link:focus { color: #2a6496; text-decoration: underline; } div.spa-page { border: 1px solid rgb(212, 212, 212); height: 100%; /* Full screen single page app. */ max-height: 100%; /* Full screen single page app. */ padding: 20px; position: relative; } div.spa-page > a { margin-bottom: 10px; } </style> </head> <body> <div class="spa-page" data-ng-controller="admin as vm"> <a class="spa-action-link" ng-click="vm.save()">save</a> <table> <thead> <tr> <th ng-repeat='(key, prop) in vm.entityFields'>{{ prop.name }}</th> </tr> </thead> <tbody> <tr ng-repeat="entity in vm.entities"> <td ng-repeat='(key, prop) in vm.entityFields'>
<
input ng-disabled="{{vm.isReadOnlyField(prop.name)}}"
type='text'
ng-model='entity[prop.name]'>
</
td> </tr> </tbody> </table> </div> <!-- Libraries --> <script type="text/javascript" src="../../Libraries/Angular/angular.js"></script> <script type="text/javascript" src="../../Libraries/Breeze/breeze.debug.js"></script> <script type="text/javascript" src="../../Libraries/Breeze/breeze.angular.js"></script> <!-- Add toastr which needs jQuery (Breeze does not need jQuery) --> <script type="text/javascript" src="../../Libraries/jQuery/jquery-2.1.1.js"></script> <script type="text/javascript" src="../../Libraries/Toastr/toastr.js"></script>
<!-- Add breeze.savequeuing which needs Q (Breeze does not need Q)-->
<script type="text/javascript" src="../../Libraries/Q/q.min.js"></script> <script type="text/javascript" src="../../Libraries/Breeze/breeze.savequeuing.js"></script> <!-- App --> <script type="text/javascript"> // Use namespaces to prevent pollution of the global namespace. var spa = spa || {}; spa.controllers = spa.controllers || {}; // Angular module [app]. spa.app = (function () { 'use strict'; var app = angular.module('app', [ 'breeze.angular' // The breeze service module. ]); })(); // Angular controller [admin]. spa.controllers.admin = (function () { 'use strict'; var controllerId = 'admin'; angular.module('app').controller(controllerId, ['breeze', admin]); function admin(breeze) { var manager = null; var entityTypeName = "Employee"; var vm = this; vm.entities = []; vm.entityFields = null; vm.isReadOnlyField = function (name) { // Make 'id' fields read-only. return (name === 'id'); }; vm.save = function () { manager.saveChanges().then(showInfo).catch(showError); }; function initialize() { // Use camel case for entity properties. breeze.NamingConvention.camelCase.setAsDefault(); // Configure and create EntityManager.
// The first breeze is part of the routing, second breeze is the name of the Web Api
// controller).
manager = new breeze.EntityManager('/breeze/breeze'); manager.enableSaveQueuing(true); // Get entities from the server. var query = new breeze.EntityQuery().from(entityTypeName); manager.executeQuery(query).then(handleRefresh).catch(showError); } function handleRefresh(data) { // Get entity fields from metadata. var entityMetaData = manager.metadataStore.getEntityType(entityTypeName); vm.entityFields = entityMetaData.dataProperties; // Show the enties from the server. vm.entities = data.results; } function showError(e) { // Show xhr error. toastr.error(e); } function showInfo() { // Show info message. toastr.info(entityTypeName + " saved!"); } initialize(); } })(); </script> </body> </html>

 

Sever side code

 

Employee.cs

namespace Research.UI.Web.Server.Model
{
    public class Employee
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string PhoneNumber { get; set; }
     }
}

ResearchDbContext.cs

namespace Research.UI.Web.Server.Model
{
    using System.Collections.Generic;
    using System.Data.Entity;

    public class ResearchDbContext : DbContext
    {
        public DbSet<Employee> Employees { get; set; }
    }    
}

BreezeWebApiConfig.cs

using System.Web.Http;

[assembly: WebActivator.PreApplicationStartMethod(
    typeof(Research.UI.Web.App_Start.BreezeWebApiConfig), "RegisterBreezePreStart")]
namespace Research.UI.Web.App_Start {
  ///<summary>
  /// Inserts the Breeze Web API controller route at the front of all Web API routes
  ///</summary>
  ///<remarks>
  /// This class is discovered and run during startup; see
  /// http://blogs.msdn.com/b/davidebb/archive/2010/10/11/light-up-your-nupacks-with-startup-code-and-webactivator.aspx
  ///</remarks>
  public static class BreezeWebApiConfig {

    public static void RegisterBreezePreStart() {
      GlobalConfiguration.Configuration.Routes.MapHttpRoute(
          name: "BreezeApi",
          routeTemplate: "breeze{controller}/{action}"
      );
    }
  }
}

 

BreezeController.cs

namespace Research.UI.Web.Server.Controllers
{
    using Breeze.ContextProvider;
    using Breeze.ContextProvider.EF6;
    using Breeze.WebApi2;
    using Newtonsoft.Json.Linq;
    using Research.UI.Web.Server.Components;
    using Research.UI.Web.Server.Model;
    using System.Linq;
    using System.Web.Http;

    [BreezeController]
    public class BreezeController : ApiController
    {
        private readonly EFContextProvider<ResearchDbContext> _contextProvider;
        public BreezeController(): this(null)
        {
        }

        public BreezeController(EFContextProvider<ResearchDbContext> contextProvider)
        {
            _contextProvider = contextProvider ??  new EFContextProvider<ResearchDbContext>();

         }

        [HttpGet]
        public string Metadata()
        {
            string result = _contextProvider.Metadata();
            return result;
        }

        [HttpGet]
        public IQueryable<Employee> Employee()
        {
            
            return _contextProvider.Context.Employees;
        }
    
        [HttpPost]
        public SaveResult SaveChanges(JObject saveBundle)
        {
            return _contextProvider.SaveChanges(saveBundle);
        }
    }
}

Tags: , , ,

I had installed Visual Studio 2010 and Visual Studio 2012 and installed the NuGet Package Manager, via [Extensions and Updates] and no NuGet Package Manager was showing.

I found the solution add: http://nuget.codeplex.com/workitem/3031

 

Manual downloading the NuGet Package Manager (vsix), closing all visual studio instances and running the *.vsix twice simultaneous, fixed the problem.

Tags:

Roel van Lisdonk on July 7th, 2014

In SSIS 2012 there are two scopes on which parameters can be defined:

- project

- package

 

Projects parameters

If you want to show / edit SSIS project parameters in Visual Studio 2012:

Open the solution explorer and under the project, there’s a folder [Poject.params]:

Clicking on the folder [Poject.params] will show the project parameters.

image

 

Package parameters

If you want to show the packages parameters, open the package and click on the Parameters tab:

image

Tags: ,

Roel van Lisdonk on July 4th, 2014

If you want to show an text in a Kendo UI grid, you could use CSS, as suggested on stackoverflow.com:

http://stackoverflow.com/questions/23476978/display-a-message-within-the-kendo-grid-when-its-empty

 

But if you want to completely hide the gird and show a message, you can use a calculated field:

http://docs.telerik.com/kendo-ui/getting-started/framework/mvvm/observableobject#creating-dependent-methods-also-known-as-calculated-fields

 

image

 

HTML

<!DOCTYPE html>
<html>
<head>
    <title>Kendo element binding research page.</title>
    <link rel="stylesheet" type="text/css" href="//cdn.kendostatic.com/2014.1.528/styles/kendo.common.min.css" />
    <link rel="stylesheet" type="text/css" href="//cdn.kendostatic.com/2014.1.528/styles/kendo.rtl.min.css" />
    <link rel="stylesheet" type="text/css" href="//cdn.kendostatic.com/2014.1.528/styles/kendo.metro.min.css" />
    <link rel="stylesheet" type="text/css" href="//cdn.kendostatic.com/2014.1.528/styles/kendo.metro.mobile.min.css" />
    <style>
        /* Resets */
        div, span, i, p, input, select {
            -moz-box-sizing: border-box;
            -webkit-box-sizing: border-box;
            box-sizing: border-box; /* Border boxing is used, so the padding, margin and borders are within the width and height of de element. */
            color: rgb(112, 112, 112);
            font-family: Arial, Helvetica, sans-serif; /* For know use default fonts used on google.com stackoverflow.com, telerik.com etc. */
            font-size: 13px;
            margin: 0; /* Margin zero is used to prevent unnecessary white space. */
            padding: 0; /* Padding zero is used to prevent unnecessary white space. */
        }        

        html, body {
            height: 100%;
            max-height: 100%;
        }

        body {
            padding: 20px;
        }

        .info {
            border: 1px solid rgb(128, 128, 128);
            box-shadow: rgba(0, 0, 0, 0.298039) 0 2px 6px 0, rgba(0, 0, 0, 0.2) 0 -3px 8px 0;
            padding: 10px;
        }

        .no-data {
            border: 1px solid #dadada;
            padding: 10px;
        }
    </style>
</head>
<body>
    <div id="view">
        <div class="info">            
            <script id="productsRowTemplate" type="text/x-kendo-tmpl">
                <tr data-uid="#: uid#">
                    <td>#: Id#</td>
                    <td>#: Name#</td>
                </tr>
            </script>
            <div id="productsGrid" data-role="grid"
                 data-row-template="productsRowTemplate"
                 data-columns="[
                                    { 'field': 'Id' },
                                    { 'field': 'Name' }
                               ]"
                 data-bind="source: products, invisible: productsIsEmpty"></div>
            <div class="no-data" data-bind="visible: productsIsEmpty">No data.</div>
        </div>
    </div>

    <!-- Libraries -->
    <script type="text/javascript" src="//code.jquery.com/jquery-1.9.1.min.js"></script>
    <script type="text/javascript" src="//cdn.kendostatic.com/2014.1.528/js/kendo.all.min.js"></script>


    <!-- Custom -->
    <script type="text/javascript" src="kendo-controller.js"></script>
</body>
</html>

 

JavaScript

var kendo = kendo || {};

kendo.controller = (function () {
    var self = {};
    var _vm = null;

    self.show = function () {

        _vm = new kendo.data.ObservableObject({
            products: new kendo.data.DataSource({
                data: new kendo.data.ObservableArray([
                    
                ])
            }),
            productsIsEmpty: function () {
                return this.get("products").data().length === 0;
            }
        });

        kendo.bind($("#view"), _vm);
    };

    return self;
})();

kendo.controller.show();

Tags: ,