Silverlight to Angular – 2 (Data Binding)

One of the main features in Silverlight is binding. There are two ways you do data binding. ‘One way’ or ‘two way’ data binding. There is one more ‘onetime’, I am skipping it on purpose here. Let’s first see what they are and when do you use them and then see its associated implementation in both Silverlight and Angular.JS

One-way: As the name suggests, as the data in data model changes, the changes are reflected in the View. When it is a one way data binding, user is not allowed to change the value in the view. It is meant to be showing the data coming from the model only. The read only controls are the excellent candidates for this kind of binding. This eliminates the overhead of two way binding.

image

Problem: Display number of times a button is clicked using one-way binding.

Silverlight:

View: For our problem, we need three controls.

  1. A text block with text specifying the ‘Number of clicks:’
  2. A text block which displays number of clicks itself.
  3. A button to click to modify the data.
1 <TextBlock Grid.Row="0" Grid.Column="0">Number of Clicks: </TextBlock> 2 <TextBlock Grid.Row="0" Text="{Binding NumberOfClicks, Mode=OneWay}" Grid.Column="1"/> 3 <Button Grid.Column="1" Grid.Row="1" Command="{Binding ClickMe}" CommandParameter="">Click me</Button>

ViewModel: We have a public property called ‘NumberOfClicks’ which is what bound to our text block to show number of times the button clicked. The second one is IActionCommand to trigger click event for the button. Every time button clicked, we increment the counter and raise the property changed event so that view can pick up the modified value.

1 [ExportAsViewModel(typeof(MainPageViewModel))] 2 public class MainPageViewModel:BaseViewModel 3 { 4 public IActionCommand<string> ClickMe { get; private set; } 5 private int _numberOfClicks = 0; 6 public int NumberOfClicks 7 { 8 get { return _numberOfClicks; } 9 set { _numberOfClicks = value; RaisePropertyChanged(()=>NumberOfClicks); } 10 } 11 12 public MainPageViewModel() 13 { 14 ClickMe = new ActionCommand<string>(p=> NumberOfClicks = NumberOfClicks+1); 15 } 16 }

Angular:

In Angular, to achieve one way binding, i.e., $scope -> view, all you do is to use ng-bind directive instead of ng-model. Alternate solution is to use the default {{variable}} notation, which also behaves same way as the ng-bind. I prefer {{variable}} approach. Here is an example which demonstrate one way binding using ng-bind.

View:

1 <!DOCTYPE html> 2 <html ng-app> 3 <head> 4 <title></title> 5 </head> 6 <body> 7 <div ng-controller="Controller"> 8 Number of Clicks:{{NumberOfClicks}} 9 <button ng-click="onClick()">Click Me</button> 10 </div> 11 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script> 12 <script src="Scripts/Controller.js"></script> 13 </body> 14 </html>

Line (8) is the one way binding. Even though ng-bind was not specified, it is the default binding. So you do not explicitly specify the ng-bind and I prefer to stay with {{}} approach.

ViewModel/Controller:

1 function Controller($scope) { 2 $scope.NumberOfClicks = 5; 3 $scope.onClick = function() { 4 $scope.NumberOfClicks = $scope.NumberOfClicks + 1; 5 }; 6 }

[Note: When you run the code, if the number is not incrementing then, one possibility is that, you might not have ‘()’ next to the onClick function in the HTML. Been there done that.]

Two-way: This is the standard and most used data binding. In this type of binding, when a data in data model changes, the changes reflected in View. In the same way, user is allowed to change the data in view and the change pushed to Model. In my opinion this is what made Silverlight so awesome and now it carries to Angular.

image

Problem: Get name from user and provide a greeting to the user. Allow user to add ‘!’ to the end of the name using a button.

Silverlight: For our problem we need three controls

View:

  1. A text block which display ‘Enter Name’.
  2. A text box to get user name.
  3. A text block to display ‘Hello {name}’.
  4. A button to add ‘!’ char to the name, which should modify both the (2) and (3) control values.
1 <TextBlock>Name: </TextBlock> 2 <TextBox Grid.Column="1" Text="{Binding Name, Mode=TwoWay}"></TextBox> 3 <Button Command="{Binding ClickMe}" CommandParameter="" Grid.Column="2">Add !</Button> 4 <TextBlock Grid.Row="1" Text="{Binding HelloName}"></TextBlock>

If you notice line (2) the text box binding ‘Name’ is set to ‘TwoWay’, which means, user is allowed to enter data which will change the viewmodel, but if the data changes in the model, it get reflected back on the view as well.  When you run the code, you will notice, when user enters ‘Unni’ in the name and press the button, it not only display ‘Hello Unni!’ in the text block in line (4) but also changes the text box (line 2) to ‘Unni!’. In this example, line (4) is still one way, so when data changes in the model, the change will get reflected in the view.’

ViewModel:

1 [ExportAsViewModel(typeof(MainPageViewModel))] 2 public class MainPageViewModel:BaseViewModel 3 { 4 private string _name; 5 public string Name 6 { 7 get { return _name; } 8 set 9 { 10 _name = value; 11 RaisePropertyChanged(() => Name); 12 } 13 } 14 public IActionCommand<string> ClickMe { get; private set; } 15 16 private string _helloName; 17 18 public string HelloName 19 { 20 get { return _helloName; } 21 set 22 { 23 _helloName = value; 24 RaisePropertyChanged(() => HelloName); 25 } 26 } 27 28 public MainPageViewModel() 29 { 30 ClickMe = new ActionCommand<string>(Clicked); 31 } 32 33 public void Clicked(string obj) 34 { 35 if (!Name.EndsWith("!")) 36 Name += "!"; 37 HelloName = "Hello " + Name; 38 } 39 }

[Note: If you have trouble with the code, make sure, all the properties have RaisePropertyChanged event in it. Also make sure the button click command name matches the command in the view model. The last part make sure all the exports attributes are marked properly]

Angular:

By default, if you do not specify ng- in the element, then one way binding is assumed. It means, whenever you use {{variable}}, it is considered one way binding. Anytime data change happens in the controller, view gets the modified data. $scope -> view. On the other hand if you specify ‘ng-model’ attribute in the element, it is considered two way binding. It means, user changes will be pushed to model and any model changes will be pushed to view. $scope <-> model.

View:

1 <!DOCTYPE html> 2 <html ng-app> 3 <head> 4 <title></title> 5 </head> 6 <body> 7 <div ng-controller="controller"> 8 Name: <input ng-model="Name"/> 9 <button ng-click="onClick()">Click Me</button> 10 <br />{{HelloName}} 11 </div> 12 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script> 13 <script src="scripts/controller.js"></script> 14 </body> 15 </html>

Line (8) is a two way binding to a variable ‘Name’ in the $Scope. It means, when user enters a value in the input, the user entered value get pushed to the $scope variable Name. On the other hand, line (10), when ever there is a value change to ‘HelloName’ variable in $scope, it get pushed to the view.

ViewModel/Controller: (Going forward, I am going to refer this as Controller instead of ViewModel).

1 function controller($scope) { 2 $scope.Name = ""; 3 $scope.HelloName = ""; 4 5 $scope.onClick = function () { 6 debugger; 7 if ($scope.Name[$scope.Name.length - 1] !== "!") 8 $scope.Name = $scope.Name + "!"; 9 $scope.HelloName = 'Hello ' + $scope.Name; 10 }; 11 }

Initially when the HTML is rendered in the browser, both Name and HelloName will have empty string. When the user enters a value, say ‘Unni’ in the input box and click on ‘Click Me’ button, controller executes ‘onClick’ function and sets the Name to ‘Unni!’ (note ‘!’ at the end) and make ‘HelloName’ as ‘Hello Unni!’. So both the variables ‘Name’ and ‘HelloName’ changed in the model so the changes pushed back to view. Now if you notice, your input changed to Unni! Instead of Unni, thus making variable ‘Name’ two way binding. While HelloName pushed to view with new value making it one way binding.

So the bottom line is, if you use ‘ng-model’, it is two way binding otherwise it is one way binding.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s