Thursday, June 11, 2015

Beginners for AngularJS


A Google employee, started to work with AngularJS in 2009. The idea turned out very well, and the project is now officially supported by Google and AngularJS version 1.0 was released in 2012.

AngularJS is a JavaScript framework. It is a library written in JavaScript. AngularJS extends HTML attributes with Directives. AngularJS expressions can be written inside HTML

AngularJS Extends HTML

AngularJS extends HTML with ng-directives (AngularJS directives are HTML attributes with an ng prefix. ) AngularJS lets you extend HTML with new attributes called Directives.


  • The ng-app directive defines the root element of an AngularJS application(defines an AngularJS application).
               It initializes an AngularJS application. will auto-bootstrap (automatically initialize) the application when a web page is loaded.
               It tells AngularJS that the <div> element is the "owner" of an AngularJS application.

              Technically possible to have several applications per page, only one ng-app directive will be automatically instantiated and initialized by the Angular framework. we need to bootstrap the modules to have multiple ng-app within the same page.

  • The ng-model directive binds the value of HTML controls (input, select, textarea) to application data.

              It binds the value of the input field to the application variable name.
              The ng-model directive can also:
                      Provide type validation for application data (number, email, required).
                      Provide status for application data (invalid, dirty, touched, error).
                      Provide CSS classes for HTML elements.
                      Bind HTML elements to HTML forms.


  • The ng-bind directive binds application data to the HTML view.

              It binds the innerHTML of the <p> element to the application variable name.
  • The ng-init directive initialize AngularJS application variables. It initializes application data and defines initial values for an AngularJS application. Normally, we'll not use ng-init. We'll use a controller or module instead.

  • The ng-controller directive defines the application controller. 
              AngularJS applications are controlled by controllers. It control the data of AngularJS applications.

             A controller is a JavaScript Object, created by a standard JavaScript object constructor.
<div ng-app="myApp" ng-controller="myCtrl">

First Name: <input type="text" ng-model="firstName"><br>
Last Name: <input type="text" ng-model="lastName"><br>
<br>
Full Name: {{firstName + " " + lastName}}

</div>

<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
    $scope.firstName = "John";
    $scope.lastName = "Doe";
});

</script>

In the above example,  ng-app="myApp"  creates AngularJs application and the application runs inside the <div>. The ng-controller="myCtrl" defines a controller and myCtrl function is a JavaScript function. It invoke the controller with a $scope object. $scope is the application object. The controller creates two properties (variables) in the scope (firstName and lastName). The ng-model directives bind the input fields to the controller properties (firstName and lastName).

It is common to store controllers in external files:
<script src="{YourControllerFileName}.js"></script>

Controller Methods

The example above demonstrated a controller object with two properties: lastName and firstName.
A controller can also have methods (variables as functions):

<div ng-app="myApp" ng-controller="personCtrl">

First Name: <input type="text" ng-model="firstName"><br>
Last Name: <input type="text" ng-model="lastName"><br>
<br>
Full Name: {{fullName()}}

</div>

<script>
var app = angular.module('myApp', []);
app.controller('personCtrl', function($scope) {
    $scope.firstName = "John";
    $scope.lastName = "Doe";
    $scope.fullName = function() {
        return $scope.firstName + " " + $scope.lastName;

    }
});
</script>


  • The ng-repeat directive clones HTML elements once for each item in a collection (in an array).



Example:
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<body>

<div ng-app="" ng-init="firstName='John'">
  <p>Name: <input type="text" ng-model="name"></p>
  <p ng-bind="name"></p>
OR
  <p>{{name}}</p>

    <p>The name is <span ng-bind="firstName"></span></p>
OR
    <p>The name is {{firstName}}</p>
</div>

</body>
</html>



AngularJS modules define applications:
var app = angular.module('myApp', []);

AngularJS controllers control applications:
app.controller('myCtrl', function($scope) {
    $scope.firstName= "John";
    $scope.lastName= "Doe";
});

AngularJS expressions are written inside double braces: {{ expression }}.
AngularJS expressions binds data to HTML the same way as the ng-bind directive.
Example:
<p>The name is <span ng-bind="firstName"></span></p>
OR
    <p>The name is {{firstName}}</p>

If we remove the ng-app directive, HTML will display the expression as it is, without solving it.

we can bind data to HTML in two ways:
<div ng-app="" ng-init="firstName='John';lastName='Doe'">

<p>The name is {{ firstName + " " + lastName }}</p>

</div>

OR

<div ng-app="" ng-init="firstName='John';lastName='Doe'">

<p>The name is <span ng-bind="firstName + ' ' + lastName"></span></p>

</div>



AngularJS Objects

AngularJS objects are like JavaScript objects:
<div ng-app="" ng-init="person={firstName:'John',lastName:'Doe'}">

<p>The name is {{ person.lastName }}</p>

</div>

AngularJS Arrays

AngularJS arrays are like JavaScript arrays:
<div ng-app="" ng-init="points=[1,15,19,2,40]">

<p>The third result is {{ points[2] }}</p>

</div>


In the above example, the {{ firstName }} expression  is an AngularJS data binding expression.
{{ firstName }} is synchronized with ng-model="firstName", binds the value of the firstName input field to the application firstName variable name.

In the below example two text fields are synchronized with two ng-model directives:

<div ng-app="" ng-init="quantity=1;price=5">

Quantity: <input type="number" ng-model="quantity">
Costs:    <input type="number" ng-model="price">

Total in dollar: {{ quantity * price }}

</div>


The ng-repeat directive repeats an HTML element and it used on an array of objects.
<div ng-app="" ng-init="names=[
{name:'Jani',country:'Norway'},
{name:'Hege',country:'Sweden'},
{name:'Kai',country:'Denmark'}]"
>

<ul>
  <li ng-repeat="x in names">
    {{ x.name + ', ' + x.country }}
  </li>
</ul>

</div>

AngularJS Filters

        Filters can be added to expressions and directives using a pipe character.
        AngularJS filters can be used to transform data. A filter can be added to an expression with a pipe character (|).

Filter in expression
The uppercase filter format strings to upper case:

<div ng-app="myApp" ng-init="lastName ='John'">
        <p>The name is {{ lastName | uppercase }}</p>
</div>


The lowercase filter format strings to lower case:
<div ng-app="myApp" ng-init="lastName ='John'">
        <p>The name is {{ lastName | lowercase }}</p>
</div>

The currency filter formats a number as currency:
<div ng-app="myApp" ng-controller="costCtrl">
        <input type="number" ng-model="quantity">
        <input type="number" ng-model="price">
     <p>Total = {{ (quantity * price) | currency }}</p>
</div>

Filter in directives
        A filter can be added to a directive with a pipe character (|).

The orderBy filter orders an array by an expression:
<div ng-app="myApp" ng-controller="namesCtrl">

<ul>
  <li ng-repeat="x in names | orderBy:'country'">
    {{ x.name + ', ' + x.country }}
  </li>
</ul>
<div>

The filter filter selects a subset of an array:
<div ng-app="myApp" ng-controller="namesCtrl">
<p><input type="text" ng-model="test"></p>
<ul>
  <li ng-repeat="x in names | filter:test | orderBy:'country'">
    {{ (x.name | uppercase) + ', ' + x.country }}
  </li>
</ul>
</div>

AngularJS AJAX - $http


        AngularJS $http is a core service for reading data from web servers.

        $http.get(url) is the function to use for reading server data.


<div ng-app="myApp" ng-controller="customersCtrl"> 
<ul>
  <li ng-repeat="x in names">
    {{ x.Name + ', ' + x.Country }}
  </li>
</ul>

</div>

<script>
var app = angular.module('myApp', []);
app.controller('customersCtrl', function($scope, $http) {
    $http.get("http://www.w3schools.com/angular/customers.php")
    .success(function(response) {$scope.names = response.records;});
});
</script>

        In the above example, $http is an XMLHttpRequest object for requesting external data.
$http.get() reads JSON data from http://www.w3schools.com/angular/customers.php. If success, the controller creates a property (names) in the scope, with JSON data from the server.


Display the Table Index ($index)

To display the table index, add a <td> with $index: 
<table>
  <tr ng-repeat="x in names">
    <td>{{ $index + 1 }}</td>
    <td>{{ x.Name }}</td>
    <td>{{ x.Country }}</td>
  </tr>
</table>


Using $even and $odd


To identify the even and odd rows in  a table.


<table>
<tr ng-repeat="x in names">
<td ng-if="$odd" style="background-color:#f1f1f1">{{ x.Name }}</td>
<td ng-if="$even">{{ x.Name }}</td>
<td ng-if="$odd" style="background-color:#f1f1f1">{{ x.Country }}</td>
<td ng-if="$even">{{ x.Country }}</td>
</tr>
</table>

AngularJS HTML DOM

The ng-disabled directive binds AngularJS application data to the disabled attribute of HTML elements.
<div ng-app="myApp" ng-init="mySwitch=true">
        
<p>
            <button ng-disabled="mySwitch">Click Me!</button>
        </p>
        <p>
           <input type="checkbox" ng-model="mySwitch">Button
        </p>
         <p>
             {{ mySwitch }}
         </p>


</div>



       It binds the application data mySwitch to the HTML button's disabled attribute. The ng-model directive binds the value of the HTML checkbox element to the value of mySwitch.

       If the value of mySwitch evaluates to true, the button will be disabled.

The ng-show directive shows or hides an HTML element. It shows (or hides) an HTML element based on the value of ng-show. It can also be used to set the visibility of a part of an application.

<div ng-app="myApp" ng-init="mySwitch=true">
        
<p>
            <button ng-show="mySwitch">Click Me!</button>
        </p>
        <p>
           <input type="checkbox" ng-model="mySwitch">Button
        </p>
         <p>
             {{ mySwitch }}
         </p>


</div>

        We can use any expression that evaluates to true or false:
<div ng-app="myApp" ng-init="hour=13">
      <p ng-show="hour > 12">I am visible.</p>
</div>


The ng-hide directive hides or shows an HTML element. It can be used to set the visibility of a part of an application.

<div ng-app="myApp">
     <p ng-hide="true">I am not visible.</p>
     <p ng-hide="false">I am visible.</p>
</div>

<div ng-app="myApp" ng-controller="personCtrl">

<button ng-click="toggle()">Toggle</button>

<p ng-hide="myVar">
First Name: <input type="text" ng-model="firstName"><br>
Last Name: <input type="text" ng-model="lastName"><br>
<br>
Full Name: {{firstName + " " + lastName}}
</p>

</div>

<script>
var app = angular.module('myApp', []);
app.controller('personCtrl', function($scope) {
    $scope.firstName = "John",
    $scope.lastName = "Doe"
    $scope.myVar = false;
    $scope.toggle = function() {
        $scope.myVar = !$scope.myVar;
    };
});
</script>

The ng-if directive removes an HTML element.  Instead of changing its visibility via the display css property, it completely removes element. When an element is removed using ng-If its scope is destroyed and a new scope is created when the element is restored.



<label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
Show when checked:
<span ng-if="checked" class="animate-if">
  This is removed when the checkbox is unchecked.
</span>

      By using ng-show/ng-hide, The element is shown or hidden by removing or adding the .ng-hide CSS class onto the element. The .ng-hide CSS class is predefined in AngularJS and sets the display style to none (using an !important flag).

Don't confuse with ng-if, ng-show/ng-hide, ng-if will remove elements from DOM.  I.e anything else attached to those elements will be lost. For example, if we bound a click handler to one of child elements, when ng-if evaluates to false, that element will be removed from DOM and our click handler will not work any more, even after ng-if later evaluates to true and displays the element. You will need to reattach the handler.

ng-show/ng-hide does not remove the elements from DOM. It uses CSS styles to hide/show elements. This way our handlers that were attached to children will not be lost.


The ng-click directive defines an AngularJS click event.

<div ng-app="myApp" ng-controller="myCtrl">
<button ng-click="count = count + 1">Click me!</button>
<p>{{ count }}</p>
</div>

<script>

var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
    $scope.count = 0;
});

</script>



AngularJS Forms

An AngularJS form is a collection of input controls.

<html>
<body>
<div ng-app="myApp" ng-controller="formCtrl">
  <form novalidate>
    First Name:<br>
    <input type="text" ng-model="user.firstName"><br>
    Last Name:<br>
    <input type="text" ng-model="user.lastName">
    <br><br>
    <button ng-click="reset()">RESET</button>
  </form>
  <p>form = {{user}}</p>
  <p>master = {{master}}</p>
</div>

<script>
var app = angular.module('myApp', []);
app.controller('formCtrl', function($scope) {

        //Assign initial values to the master object
    $scope.master = {firstName: "John", lastName: "Doe"};

        //Create reset().
    $scope.reset = function() {
        $scope.user = angular.copy($scope.master);
    };
    $scope.reset();   //Calling reset(), to initialize the user data
});
</script>
</body>
</html>

In the above example, The ng-model directive binds two input elements to the user object in the model.
The formCtrl function sets initial values to the master object, and defines the reset() method. The reset() method sets the user object equal to the master object. The ng-click directive invokes the reset() method, only if the button is clicked The novalidate attribute is new in HTML5. It disables any default browser validation.


$rootScope

The $rootScope is the top-most scope. An app can have only one $rootScope which will be shared among all the components of an app. Hence it acts like a global variable. All other $scopes are children of the $rootScope.


<html>
<body ng-app="myApp">
<div ng-controller="Ctrl1" style="border:2px solid blue; padding:5px">
 Hello {{msg}}!
<br />
 Hello {{name}}!
 (rootScope)
</div>
<br />
<div ng-controller="Ctrl2" style="border:2px solid green; padding:5px">
 Hello {{msg}}!
<br />
 Hey {{myName}}!
<br />
 Hi {{name}}! (rootScope)
 <br/>
 <button ng-click="change()">Change root variable</button>
</div>
<br/>
<script src= "angular.min.js"></script>
<script>
 var app = angular.module('myApp', []);

 app.controller('Ctrl1', function ($scope, $rootScope) {
 $scope.msg = 'World';
 $rootScope.name = 'AngularJS';
 });

 app.controller('Ctrl2', function ($scope, $rootScope) {
  
 $scope.msg = 'Dot Net Tricks';
 $scope.myName = $rootScope.name;
 $scope.change = function() {
        $rootScope.name = "(root variable value modified)";
    };
 });
</script>
</body>
</html>

Angular Context

       When the browser receives an event that can be managed by the angular context and the $digest loop will be fired.

The $watch list 

Every time we bind something in the UI angular insert a $watch in a $watch list. Imagine the $watch as something that is able to detect changes in the model it is watching

Exaple-1:
     User: <input type="text" ng-model="user" />
     Password: <input type="password" ng-model="pass" />

    Here, we have $scope.user, which is bound to the first input, and we have $scope.pass, which is bound to the second one. Doing this we add two $watch to the $watch list.

Example-2:

app.controller('MainCtrl', function($scope) {
  $scope.foo = "Foo";
  $scope.world = "World";
});

Hello, {{ World }}

   Here, even though we have two things attached to the $scope, only one is bound. So in this case we only created one $watch.


Example-3:

app.controller('MainCtrl', function($scope) {
  $scope.people = [...];
});


<ul>
  <li ng-repeat="person in people">
      {{person.name}} - {{person.age}}
  </li>
</ul>

How many $watch are created here? Two for each person (for name and age) in people plus one for the ng-repeat. If we have 10 people in the list it will be (2 * 10) + 1, I.e 21 $watch's.

Everything that is bound in our UI using directives creates a $watch.When our template is loaded, Also known as in the linking phase, the compiler will look for every directive and creates all the $watch that are needed.

we already know that every binding we set has its own $watch to update the DOM when is needed

The $scope.watch() function is used to observe changes in a variable on the $scope. It accepts three parameters : expression, listener and equality object

Example -4:

app.controller('MainCtrl', function($scope) {
  $scope.name = "Angular";

  $scope.updated = -1;

  $scope.$watch('name', function() {
    $scope.updated++;
  });
});

<body ng-controller="MainCtrl">
  <input ng-model="name" />
  Name updated: {{updated}} times.
</body>

    Here the first parameter can be a string or a function. In this case it is just a string with the name of what we want to $watch, in this case, $scope.name. The second parameter is what is going to happen when $watch says that our watched expression has changed. The first thing we have to know is that when the controller is executed and finds the $watch, it will immediately fire.

    we have initialized the $scope.updated to -1, the $watch will run once when it is processed and it will put the $scope.updated to 0.

Example-5:


app.controller('MainCtrl', function($scope) {
  $scope.name = "Angular";

  $scope.updated = 0;

  $scope.$watch('name', function(newValue, oldValue) {
    if (newValue === oldValue) { return; } // AKA first run
    $scope.updated++;
  });
});
<body ng-controller="MainCtrl">
  <input ng-model="name" />
  Name updated: {{updated}} times.

</body>

     The second parameter of $watch receives two parameters. The new value and the old value. We can use them to skip the first run that every $watch does. Normally you don’t need to skip the first run,

Example-6:

app.controller('MainCtrl', function($scope) {
  $scope.user = { name: "Fox" };

  $scope.updated = 0;

  $scope.$watch('user', function(newValue, oldValue) {
    if (newValue === oldValue) { return; }
    $scope.updated++;
  });
});

<body ng-controller="MainCtrl">
  <input ng-model="user.name" />
  Name updated: {{updated}} times.
</body>

We want to $watch any changes in our $scope.user object. Same as before but using an object instead of a primitive.

      Here It doesn’t work. Because the $watch by default compares the reference of the objects. In example 4 and 5, every time we modify $scope.name it will create a new primitive, so the $watch will fire because the reference of the object is new and that is our change. In this new case, since we are watching $scope.user and then we are changing $scope.user.name, the reference of $scope.user is never changing because we are creating a new $scope.user.name every time we change the input, but the $scope.user will be always the same.

Example-7:
app.controller('MainCtrl', function($scope) {
  $scope.user = { name: "Fox" };

  $scope.updated = 0;

  $scope.$watch('user', function(newValue, oldValue) {
    if (newValue === oldValue) { return; }
    $scope.updated++;
  }, true);
});

<body ng-controller="MainCtrl">
  <input ng-model="user.name" />
  Name updated: {{updated}} times.
</body>

     Now it'll work, we added a third parameter to the $watch which is a bool to indicate that we want to compare the value of the objects instead of the reference. And since the value of $scope.user is changing when we update the $scope.user.name the $watch will fire appropriately.

$digest loop

       When the browser receives an event that can be managed by the angular context and the $digest loop will be fired.
       This loop is made from two smaller loops. One processes the $evalAsync queue and the other one processes the $watch list, 

     The $scope.$digest() function iterates through all the watches in the $scope object, and its child $scope objects (if it has any - isolated scopes). 

Hey $watch, what is your value?    //Watch-1
It is 9
Alright, has it changed?                 ///Watch-1
No, sir.
(nothing happens with this one, so it moves to the next)
You, what is your value?             //Watch-2
It is Foo.
Has it changed?                           //Watch-2 
Yes, it was Bar.
(good, we have a DOM to be updated)
This continues until every $watch has been checked.

     The above checking is called dirty-checking. Now that all the $watch have been checked there is something else to ask: Is there any $watch that has been updated? If there is at least one of them that has changed, the loop will fire again until all of the $watch report no changes. This is to ensure that every model is clean. Have in mind that if the loop runs more than 10 times, it will throw an exception to prevent infinite loops.

     When the $digest loop finishes, the DOM makes the changes.

Example-4:
app.controller('MainCtrl', function() {
  $scope.name = "Foo";

  $scope.changeFoo = function() {
      $scope.name = "Bar";
  }
});

{{ name }}

<button ng-click="changeFoo()">Change the name</button>

    Here we have only one $watch because ng-click doesn’t create any watches (the function is not going to change :P).

    1. We press the button.
    2. The browser receives an event which will enter the angular context (I will explain why, later in this article).
    3. The $digest loop will run and will ask every $watch for changes.
    4. Since the $watch which was watching for changes in $scope.name reports a change, if will force another $digest loop.
    5. The new loop reports nothing.
    6. The browser gets the control back and it will update the DOM reflecting the new value of $scope.name
 
    The important thing here is that EVERY event that enters the angular context will run a $digest loop. That means that every time we write a letter in an input, the loop will run checking every $watch in this page.


Example -1:
<html> <head> <title>AngularJS Digest</title> <script src="lib/angular.js"></script> <script> var myapp = angular.module("myapp", []); var myController = myapp.controller("myController", function ($scope) { $scope.datetime = new Date(); $scope.updateTime = function () { $scope.datetime = new Date(); }
// This block of code updating model outside of the Angular. so, we are using $scope.$digest() to refresh.
document.getElementById("updateTimeButton").addEventListener('click', function () {
console.log("update time clicked"); $scope.datetime = new Date(); console.log($scope.datetime); }); }); </script> </head> <body ng-app="myapp" ng-controller="myController"> <button ng-click="updateTime()">Update time - ng-click</button> <button id="updateTimeButton">Update time</button> <br /> {{datetime | date:'yyyy-MM-dd HH:mm:ss'}} </body> </html>

    When you will click on second button, the data binding is not updated. Since $scope.$digest() is not called after the second button's event listener is executed. In this way on clicking the second button the time will be updated in the $scope.data.time variable, but the new time will never displayed.

<script type="text/javascript">
document.getElementById("updateTimeButton").addEventListener('click', function () {
 console.log("update time clicked");
 $scope.datetime = new Date();

 //to update $scope
 $scope.$digest();
 console.log($scope.datetime);
 });</script>



To fix this issue you need to add a $scope.$digest() call to the second button event listener


$apply

When we do change in any model outside of the Angular context (like browser DOM events, setTimeout, XHR or third party libraries), then we need to inform Angular of the changes by calling $apply() manually. When the $apply() function call finishes AngularJS calls $digest() internally, so all data bindings are updated.

  <script>  
 document.getElementById("updateTimeButton").addEventListener('click', function () {
 $scope.$apply(function () {
 console.log("update time clicked");
 $scope.datetime = new Date();
 
 console.log($scope.datetime);
 });
 });
 </script>

In the above $digest example, instead of calling $digest() function inside the button listener function we can used the $apply() function like this

The main differance between the $digest and $apply:
     1. $digest() is faster than $apply(), since $apply() triggers watchers on the entire scope chain while $digest() triggers watchers on the current scope and its children(if it has).
     2. When error occurs in one of the watchers, $digest() can not handled errors via $exceptionHandler service, In this case you have to handle exception yourself. While $apply() uses try catch block internally to handle errors and if error occurs in one of the watchers then it passes errors to $exceptionHandler service.





Wednesday, June 10, 2015

Multiple Applications (Modules) and Controllers in AngularJS

I have just started learning Angular JS and I am stuck with the following problem.

I have created 2 ng-app and 2 ng_controller's.

There are associated views for each controller. The first View renders fine but second is not rendering. There are no errors.

<html>
<script src= "angular.min.js"></script>
<body>

<p>Try to change the names.</p>

<div  id="div1" ng-app="myApp" ng-controller="myCtrl" ng-init="firstName1='John'">

First Name: <input type="text" ng-model="firstName"><br>
Last Name: <input type="text" ng-model="lastName"><br>
<br>
Full Name: {{firstName + " " + lastName}}
<p>Name: <input type="text" ng-model="name"></p>
<p ng-bind="name"></p>
<p>"ng-bind" used - The name is <span ng-bind="firstName1"></span></p>
<p>"{{firstName1}}" used - The name is {{firstName1}}</p>
</div>
<div id="div2" ng-app="namesApp" ng-controller="namesCtrl">
<ul>
<li ng-repeat="x in names">
{{ x.name + ', ' + x.country }}
</li>
</ul>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.firstName= "John";
$scope.lastName= "Doe";
});

angular.module('namesApp', []).controller('namesCtrl', function($scope) {
$scope.names = [
{name:'Jani',country:'Norway'},
{name:'Hege',country:'Sweden'},
{name:'Kai',country:'Denmark'}
];
});
</script>
</body>
</html>

Solution:

        Technically possible to have several applications per page, only one ng-app directive will be automatically instantiated and initialized by the Angular framework. we need to bootstrap the modules to have multiple ng-app within the same page.

To overcome this issue by using the angular.bootstrap() directly.

place below line of code before the </script> tag:
angular.bootstrap(document.getElementById('div2'),['namesApp']);


Wednesday, May 27, 2015

Alternative to use String.Split function in BizTalk Orchstration

My requirement is to get the 6th ISA segment value.

The var_ISASegment contains this value "ISA*00*          *00*          *01*FIRST *01*98765LCSAPT*090318*1420*U*00401*000442082*0*T*^~"

Solution:
We will have to use an expression shape and a loop and Expression shape


Place this below condition in the loop
var_counter < 8


 create 3 variables (var_PrvPosition, var_curntPosition, var_counter) with int data type.  

  • var_PrvPosition used to store the startup position.  
  • var_curntPosition used to store  index of our delimiter 
  • var_counter used to store the segment number.


Place below code in the expression shape
var_PrvPosition ++;
var_curntPosition = var_ISASegment.IndexOf("*",var_PrvPosition);
               
var_counter++;
if (counter == 7)
{
  Var_SixthSegment = var_ISASegment.Substring(var_PrvPosition, var_curntPosition – var_PrvPosition));
}

var_PrvPosition = indexCurrentPosition;


Tuesday, April 7, 2015

Debatching(Splitting) XML Message in Orchestration using Custom Pipeline with specified BatchSize



I have used custom pipeline component to debatch it which uses XML disassembler.

“Disassemble” is the second stage of the receive pipeline which is primarily used to disassemble incoming messages, validate schema and promote properties. To disassemble (break) messages it uses an envelope schema and it breaks the message record-wise. There is no provision to break a set of records (batches).


Sample Code:

1. Create a class library project.
2. Add reference to below assemblies
         a). Microsoft.XLANGs.BaseTypes (C:\Program Files (x86)\Microsoft BizTalk Server 2010\Microsoft.XLANGs.BaseTypes.dll)
         b). Microsoft.BizTalk.Pipeline (C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies\Microsoft.BizTalk.Pipeline.dll)
3. Rename default class1.cls to XMLDebatch.cs.
4. Use following code




[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
    [ComponentCategory(CategoryTypes.CATID_DisassemblingParser)]
    [System.Runtime.InteropServices.Guid("6cf0de2b-fc7a-4a8d-913b-9d48d59ca584")]
    public class XMLDebatch : IBaseComponent, IDisassemblerComponent, IComponentUI, IPersistPropertyBag
    {
        #region Private Variables

        private ResourceManager resourceManager = null;

        //Used to hold disassembled messages
        private System.Collections.Queue qOutputMsgs = null;
        private string systemPropertiesNamespace = null;
        private string filePropertiesNamespace = null;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="XMLDebatch"/> class.
        /// </summary>
        public XMLDebatch()
        {
            resourceManager = new ResourceManager("{YourClassLibrary}.XMLDebatch", Assembly.GetExecutingAssembly());
            qOutputMsgs = new System.Collections.Queue();
            systemPropertiesNamespace = @"http://schemas.microsoft.com/BizTalk/2003/system-properties";
            filePropertiesNamespace = "http://schemas.microsoft.com/BizTalk/2003/file-properties";

        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the size of the batch.
        /// </summary>
        /// <value>
        /// The size of the batch.
        /// </value>
        [DisplayName("BatchSize")]
        [DefaultValue(50)]
        public int BatchSize
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the batch root element x path.
        /// </summary>
        /// <value>
        /// The batch root element x path.
        /// </value>
        [DisplayName("BatchRootElementXPath")]
        public string BatchRootElementXPath
        {
            get;
            set;
        }

        #endregion

        #region IBaseComponent

        /// <summary>
        /// Description of the Component
        /// </summary>
        public string Description
        {
            get { return resourceManager.GetString("COMPONENTDESCRIPTION", CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Name of the Component
        /// </summary>
        public string Name
        {
            get { return resourceManager.GetString("COMPONENTNAME", CultureInfo.InvariantCulture); }
        }

        /// <summary>
        /// Version of the Component
        /// </summary>
        public string Version
        {
            get { return resourceManager.GetString("COMPONENTVERSION", CultureInfo.InvariantCulture); }
        }

        #endregion

        #region IPersistPropertyBag

        /// <summary>
        /// GetClassID of component for usage from unmanaged code.
        /// </summary>
        /// <param name="classID"></param>
        public void GetClassID(out Guid classID)
        {
            classID = new Guid("0713faf8-748c-44d3-a1b1-8a80e3b168fc");
        }

        /// <summary>
        /// InitNew
        /// </summary>
        public void InitNew()
        {
        }

        /// <summary>
        /// Loads configuration properties for the component
        /// </summary>
        /// <param name="propertyBag"></param>
        /// <param name="errorLog"></param>
        public void Load(IPropertyBag propertyBag, int errorLog)
        {
            try
            {
                BatchSize = ReadPropertyBag<int>(propertyBag, "BatchSize");
                BatchRootElementXPath = ReadPropertyBag<string>(propertyBag, "BatchRootElementXPath");
            }
            catch (NullReferenceException ex)
            {
                System.Diagnostics.EventLog.WriteEntry("Error in reading property bag", ex.Message);
                throw ex;
            }
        }

        /// <summary>
        /// Saves the current component configuration into the property bag
        /// </summary>
        /// <param name="pb">Configuration property bag</param>
        /// <param name="fClearDirty">not used</param>
        /// <param name="fSaveAllProperties">not used</param>
        public virtual void Save(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, bool fClearDirty, bool fSaveAllProperties)
        {
            WritePropertyBag(pb, "BatchSize", this.BatchSize);
            WritePropertyBag(pb, "BatchRootElementXPath", this.BatchRootElementXPath);
        }

        #endregion

        #region IComponentUI

        /// <summary>
        /// Component icon to use in BizTalk Editor
        /// </summary>
        [System.ComponentModel.Browsable(false)]
        public IntPtr Icon
        {
            get
            {
                return ((System.Drawing.Bitmap)(this.resourceManager.GetObject("COMPONENTICON", System.Globalization.CultureInfo.InvariantCulture))).GetHicon();
            }
        }

        /// <summary>
        /// The Validate method is called by the BizTalk Editor during the build of a BizTalk project.
        /// </summary>
        /// <param name="projectSystem"></param>
        /// <returns></returns>
        public System.Collections.IEnumerator Validate(object projectSystem)
        {
            return null;
        }

        #endregion

        #region IDisassemblerComponent

        /// <summary>
        /// Disassembles the specified p context.
        /// </summary>
        /// <param name="pContext">The p context.</param>
        /// <param name="pInMsg">The pInMSG.</param>
        /// <exception cref="System.ApplicationException">
        /// Error in reading original message:  + ex.Message
        /// or
        /// Error in writing outgoing messages:  + ex.Message
        /// </exception>
        public void Disassemble(IPipelineContext pContext, Microsoft.BizTalk.Message.Interop.IBaseMessage pInMsg)
        {
            XPathNavigator xPathNavigator = null;
            XmlNamespaceManager namespaceManager = null;
            XPathNodeIterator nodeIterator = null;
            string receivedFileName = pInMsg.Context.Read("ReceivedFileName", filePropertiesNamespace).ToString();
            string fileName = System.IO.Path.GetFileNameWithoutExtension(receivedFileName);
            long enrollmentsCount = 0;
            bool isOutstandingChunks = true;

            try
            {
                //fetch original message
                Stream originalMessageStream = pInMsg.BodyPart.GetOriginalDataStream();
                XPathDocument document = new XPathDocument(originalMessageStream);
                xPathNavigator = document.CreateNavigator();
                namespaceManager = XmlDocumentHelper.GetNameSpacesFromMessage(document);
                enrollmentsCount = long.Parse(xPathNavigator.Evaluate(string.Concat("count(", BatchRootElementXPath, "/*)"), namespaceManager).ToString());
            }

            catch (Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("XMLDebatch", string.Concat("Error in reading original message: ", ex.Message));
                throw new ApplicationException(string.Concat("Error in reading original message: ", ex.Message));
            }

         
            try
            {
                long startPosition = 1;
                long FinalPosition = BatchSize;
                string[] nodes = null;
                string finalChunkString = null;
                XmlDocument chunkDoc = null;
                XmlNode batchRootElement = null;
                int batchCount = 0;
                string batchId = Guid.NewGuid().ToString();
                XmlDocument templateDoc = new XmlDocument();

                templateDoc.LoadXml(xPathNavigator.OuterXml);
                batchRootElement = templateDoc.SelectSingleNode(BatchRootElementXPath, namespaceManager);
                batchRootElement.InnerXml = string.Empty;   //Clear children.

                while (isOutstandingChunks)
                {
                    chunkDoc = templateDoc;
                    batchRootElement = chunkDoc.SelectSingleNode(BatchRootElementXPath, namespaceManager);
                    string namespaceURI = chunkDoc.DocumentElement.NamespaceURI;
                    string rootElement = chunkDoc.DocumentElement.LocalName;


                    nodeIterator = xPathNavigator.Select(string.Concat(BatchRootElementXPath, string.Format("/*[position() >= {0} and position() <= {1}]", startPosition, FinalPosition)), namespaceManager);
                    nodes = nodeIterator.Cast<XPathNavigator>().Select(item => item.OuterXml).ToArray();
                    finalChunkString = string.Join(string.Empty, nodes);

                    batchRootElement.InnerXml = finalChunkString;

                    batchCount++;
                    CreateOutgoingMessage(pContext, chunkDoc.OuterXml, namespaceURI, rootElement, pInMsg, batchCount, batchId);
                    startPosition = FinalPosition + 1;
                    if (FinalPosition >= enrollmentsCount)
                    {
                        isOutstandingChunks = false;
                    }
                    else
                    {
                        FinalPosition = FinalPosition + BatchSize;
                        if (FinalPosition > enrollmentsCount)
                        {
                            FinalPosition = enrollmentsCount;
                        }
                    }

                }

                batchRootElement.InnerXml = string.Empty;

            }
            catch (Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("XMLDebatch", string.Concat("Error in writing outgoing messages: ", ex.Message));
                throw new ApplicationException("Error in writing outgoing messages: " + ex.Message);
            }
        }

        /// <summary>
        /// Gets the next message.
        /// </summary>
        /// <param name="pContext">The pContext.</param>
        /// <returns></returns>
        public Microsoft.BizTalk.Message.Interop.IBaseMessage GetNext(IPipelineContext pContext)
        {
            IBaseMessage returnMessage = null;

            if (qOutputMsgs.Count > 0)
            {
                returnMessage = (IBaseMessage)qOutputMsgs.Dequeue();
            }

            return returnMessage;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Reads the property bag.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyBag">The property bag.</param>
        /// <param name="propName">Name of the property.</param>
        /// <returns></returns>
        private T ReadPropertyBag<T>(IPropertyBag propertyBag, string propName)
        {
            T returnValue = default(T);
            object val = null;
            try
            {
                propertyBag.Read(propName, out val, 0);
                val = val ?? default(T);
                returnValue = (T)Convert.ChangeType(val, typeof(T));
            }
            catch (System.ArgumentException)
            {
                return returnValue;
            }
            catch (System.Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("XMLDebatch", string.Concat("Error in reading into property bag", ex.Message));
                throw new ApplicationException(string.Concat("Error in reading into property bag: ", ex.Message));
            }
            return returnValue;
        }

        /// <summary>
        /// Writes the property bag.
        /// </summary>
        /// <param name="propertyBag">The property bag.</param>
        /// <param name="propName">Name of the property.</param>
        /// <param name="val">The value.</param>
        private void WritePropertyBag(IPropertyBag propertyBag, string propName, object val)
        {
            try
            {
                propertyBag.Write(propName, ref val);
            }
            catch (Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("Error in writing into property bag", ex.Message);
                throw new ApplicationException(string.Concat("Error in writing into property bag: ", ex.Message));
            }
        }

        /// <summary>
        /// Queue outgoing messages
        /// </summary>
        private void CreateOutgoingMessage(IPipelineContext pContext, String messageString, string namespaceURI, string rootElement, IBaseMessage pInMsg, int batchIndex, string batchId)
        {

            IBaseMessage outMsg = null;
            try
            {
                string receivedFileName = pInMsg.Context.Read("ReceivedFileName", filePropertiesNamespace).ToString();
                string fileName = System.IO.Path.GetFileNameWithoutExtension(receivedFileName);
                string[] fileNameAttr = fileName.Split(new char[] { '.' });

                fileName = fileNameAttr[fileNameAttr.Length - 1];
                receivedFileName = Path.GetDirectoryName(receivedFileName);
                //create outgoing message
                outMsg = pContext.GetMessageFactory().CreateMessage();
                outMsg.AddPart("Body", pContext.GetMessageFactory().CreateMessagePart(), true);
                outMsg.Context = PipelineUtil.CloneMessageContext(pInMsg.Context);
                outMsg.Context.Promote("MessageType", systemPropertiesNamespace, string.Concat(namespaceURI, "#", rootElement));
                outMsg.Context.Promote("ReceivedFileName", filePropertiesNamespace, string.Concat(receivedFileName, "\\", fileName, ".", batchId, ".Chunk.", batchIndex.ToString("00"), ".xml"));
                byte[] bufferOoutgoingMessage = System.Text.ASCIIEncoding.ASCII.GetBytes(messageString);
                outMsg.BodyPart.Data = new MemoryStream(bufferOoutgoingMessage);
                qOutputMsgs.Enqueue(outMsg);

            }
            catch (Exception ex)
            {
                System.Diagnostics.EventLog.WriteEntry("XMLDebatch", string.Concat("Error in queening outgoing messages:", ex.Message));
                throw new ApplicationException(string.Concat("Error in queening outgoing messages: ", ex.Message));
            }
        }

        #endregion

    }


Create new XmlDocumentHelper class and use the below code.

public class XmlDocumentHelper
    {
        #region Private Variables

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="XmlDocumentHelper"/> class.
        /// </summary>
        public XmlDocumentHelper()
        {
        }

        #endregion

        #region Private Methods

        #endregion

        #region Public Methods

        /// <summary>
        /// Constructs the empty message.
        /// </summary>
        /// <param name="objType">Type of the object.</param>
        /// <param name="rootNodeName">Name of the root node.</param>
        /// <returns></returns>
        public static XmlDocument ConstructEmptyMessage(Type objType, string rootNodeName = "")
        {
            Type MessageSchemaType = objType;

            try
            {
                DocumentSpec DocumentSpecification = null;
                if (string.IsNullOrEmpty(rootNodeName))
                {
                    DocumentSpecification = new DocumentSpec(MessageSchemaType.FullName, MessageSchemaType.Assembly.FullName);
                }
                else
                {
                    DocumentSpecification = new DocumentSpec(string.Concat(MessageSchemaType.FullName, "+", rootNodeName), MessageSchemaType.Assembly.FullName);
                }

                XmlDocument SchemaInstance = new XmlDocument();
                using (StreamReader InstanceStreamReader = new StreamReader(DocumentSpecification.GetDocSchema().CreateXmlInstance()))
                {
                    SchemaInstance.Load(InstanceStreamReader);
                }

                return SchemaInstance;

            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        /// <summary>
        /// Gets the name spaces from message.
        /// </summary>
        /// <param name="doc">The document.</param>
        /// <returns></returns>
        public static XmlNamespaceManager GetNameSpacesFromMessage(XPathDocument xDocument)
        {
            XmlNamespaceManager namespaceManager = new XmlNamespaceManager(new NameTable());
            XPathNavigator nav = xDocument.CreateNavigator();
            XPathNodeIterator nodes = (XPathNodeIterator)nav.Evaluate("//namespace::*");

            while (nodes.MoveNext())
            {
                namespaceManager.AddNamespace(nodes.Current.Name, nodes.Current.Value);
            }


            return namespaceManager;
        }

        /// <summary>
        /// Sets the node value.
        /// </summary>
        /// <param name="doc">The document.</param>
        /// <param name="xpath">The xpath.</param>
        /// <param name="value">The value.</param>
        /// <param name="namespaceManager">The namespace manager.</param>
        public static void SetNodeValue(XmlDocument doc, string xpath, string value, XmlNamespaceManager namespaceManager)
        {
            if (doc != null && !string.IsNullOrEmpty(xpath))
            {
                string nodeValue = string.Empty;
                XmlNode xpathNode = doc.SelectSingleNode(xpath, namespaceManager);
                if (xpathNode != null)
                {
                    xpathNode.InnerText = value;
                }
            }
        }

        #endregion
    }