var driver = new FirefoxDriver();
// select an option on a dropdown list by its text
new SelectElement(driver.FindElement(By.Id("lstStatus"))).SelectByText("Active");
// another example
driver.FindElements(By.CssSelector("#dvSelectListItems li")).Where(elem => elem.Text.Trim() == "some text").FirstOrDefault().Click();
// get text from a table in its second row and second column
driver.FindElement(By.CssSelector("#tbSearchResults tr:nth-child(2) td:nth-child(2)")).Text
// check whether an element is displayed
driver.FindElement(By.CssSelector("div.accordionContent")).Displayed == false
// fill an input field with text
driver.FindElement(By.Id("Name")).SendKeys("Fullname");
// get a td element that has a particular title attribute value
driver.FindElement(By.CssSelector("td[title='a title']"));
// trick to do hover on a menu
Actions actions = new Actions(driver);
var profileMenu = driver.FindElement(By.LinkText("Profiles"));
actions.MoveToElement(profileMenu);
actions.Click();
actions.Perform();
// wait for maximum ten seconds until an element is displayed
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => d.FindElement(By.Id("divDashContainer")).Displayed);
// navigate to an url
driver.Navigate().GoToUrl("some url");
Friday, 8 August 2014
Some Selenium Code Examples
Below are some Selenium code examples:
Labels:
Selenium
Monday, 21 July 2014
TFS 2013 - Limiting Users List in User DropDown
Below is an example of how to limit users in a user selection dropdown:
<field name="Assigned To" refname="System.AssignedTo" type="String" syncnamechanges="true" reportable="dimension">
<!-- show users that are part of the team only -->
<validuser group="[Project]\Contributors" />
<!-- below is another way to do that
<allowedvalues expanditems="true">
<listitem value="[Project]\Contributors" />
</allowedvalues> -->
<default from="currentuser" /> <!-- this one to have the logged on user to be automatically selected by default -->
</field>
Labels:
TFS
Thursday, 17 July 2014
Modifying and Creating Work Item Type in TFS 2013
To modify a work item type in TFS 2013, we need to these steps:
- export the work item type xml file to our machines
- modify that file
- then import back the modified xml file to the TFS server
To create a new work item type, we need to:
- create a new work item type xml file
- import the new xml file to TFS server
We use Visual Studio Command Prompt to do this.
To export work item type, run this command:
To import work item type, run this command:
To delete work item type:
To rename work item type:
To export process configuration:
To import process configuration:
Reference:
MSDN: Import, export, and manage work item types [witadmin]
- export the work item type xml file to our machines
- modify that file
- then import back the modified xml file to the TFS server
To create a new work item type, we need to:
- create a new work item type xml file
- import the new xml file to TFS server
We use Visual Studio Command Prompt to do this.
To export work item type, run this command:
witadmin exportwitd /collection:[team project collection URI] /p:[project_name] /n:[work item type name] /f:[destination file]examples:
C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE>witadmin exportwitd /collection:"http://myTFSserver/tfs/DefaultCollection" /p:MyProject /n:Bug /f:C:\Users\me\Desktop\bug.xml C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE>witadmin exportwitd /collection:"http://myTFSserver/tfs/DefaultCollection" /p:MyProject /n:"Product Backlog Item" /f:c:\Users\me\Desktop\pib.xml
To import work item type, run this command:
witadmin importwitd /collection:[team project collection URI] /p:[project_name] /f:[source file]example:
C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE>witadmin importwitd /collection:"http://myTFSserver/tfs/DefaultCollection" /p:MyProject /f:c:\Users\me\Desktop\task.xml
To delete work item type:
witadmin destroywitd /collection:[team project collection URI] /p:[project_name] /n:[work item type name]example:
witadmin destroywitd /collection:"http://myTFSserver/tfs/DefaultCollection" /p:"My Project" /n:"Impediment"
To rename work item type:
witadmin renamewitd /collection:[team project collection URI] /p:[project_name] /n:[existing work item type name] /new:[new work item type name]example:
C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE>witadmin renamewitd /collection:"http://myTFSserver/tfs/DefaultCollection" /"My Project" /n:"Product Backlog Item" /new:"Enhancement" Are you sure you want to rename the work item type Product Backlog Item to the new name of Enhancement? (Yes/No) Yes The work item type was renamed.
To export process configuration:
witadmin exportprocessconfig /collection:[team project collection URI] /p:[project_name] /f:[destination file]example:
C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE>witadmin exportprocessconfig /collection:"http://myTFSserver/tfs/DefaultCollection" /p:MyProject /f:c:/Users\me\Desktop\ProcessConfiguration.xml
To import process configuration:
witadmin importprocessconfig /collection:[team project collection URI] /p:[project_name] /f:[source file]
C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE>witadmin importprocessconfig /collection:"http://myTFSserver/tfs/DefaultCollection" /p:MyProject /f:c:/Users\me\Desktop\ProcessConfiguration.xml
Reference:
MSDN: Import, export, and manage work item types [witadmin]
Labels:
TFS
Thursday, 26 June 2014
Watching Attribute Value with $observe
Below is an example of how to watch for an attribute's value and do something every time it has changed. To do this we would use $observe function.
See the example in Plunker
<!-- origin markup in the view -->
enter a colour: <input class="observe-test" data-ng-model="inputValue" title="{{inputValue}}"/>
<br />
value typed: <span></span>
// directive codes
myApp.directive('observeTest', function () {
return {
restrict: 'C',
link: function (scope, element, attrs) {
attrs.$observe('title', function (newValue) {
attrs.$set('style', 'color:' + newValue);
element.parent().find('span').text(newValue);
})
}
}
});
See the example in Plunker
Labels:
AngularJS,
JavaScript
Monday, 23 June 2014
More Advanced AngularJS Directive
Isolate Scope
scope option is useful for creating directive's internal variables or functions from outer values or functions.
See the example in Plunker
In this example we use '=' to pass the value of an outer variable specified in an attribute in the origin markup. I.e., data: "=aValue" will create an internal variable called 'data' and get the value of the outer variable (an AngularJs $scope variable) specified in the 'a-value' attribute in the origin markup. The attribute name referred to follows the same matching pattern as the matching pattern of a directive name (i.e, 'aValue' matches 'a-value' in the origin markup).
'@' is used if we want to copy the literal value (copy the text) of an attribute in the origin markup. If we want to pass a value of an AngularJs variable then we need to use {{ . . . }} to get the value first before passing it to the directive. I.e., <scopetest the-value="{{controllerData}}" />
'&' is used to pass a function of an attribute in the origin markup.
If the attribute name being referred to is the same as the internal variable name then when defining the internal variable, we don't need to specify the attribute name after the symbol. E.g., aValue: "="; is the same as aValue: "=aValue";
When we are using isolate scope, external variables will not be available inside the directive. Only the variables that are declared inside scope: { . . . } will be available.
Transclude
This option is used to pass the matched origin markup and its inner content into a directive template. The parent-to-be element in the template needs to be decorated with ng-transclude attribute. All the inner content of the parent-to-be element will be overwritten with the passed markup. The directive also needs to set transclude: true to activate this option.
See the example in Plunker
Priority and Terminal
priority option is useful for directives (more than one) that are defined in a single DOM element. The option is used to determine the order of how they are going to be applied, especially for their link and compile functions. The compile and pre-link functions are executed from the greatest number while post-link functions are executed from the smallest number.
If not defined, the priority default value is 0. If the directives have same priority values then it seems that they are executed in alphabetical order.
If terminal option is used then directives that have lower priorities will be disregarded.
See the example in Plunker
Require
This option is used to pass other directive's controller into the link function of the directive where the option is specified. The other directive could be a sibling directive or a directive in one of parent elements.
The require option can take a single string or an array of strings of directive names to be found.
The string name can be:
- not prefixed - to find a sibling directive
- prefixed with '?' - return 'null' if the intended sibling directive is not found
- prefixed with '^' - find the directive in one of parent elements
- prefixed with '^?' - return 'null' if the intended directive is not found in any parent elements
See the example in Plunker
scope option is useful for creating directive's internal variables or functions from outer values or functions.
<!-- origin markup in the view -->
<scopetest a-value="controllerData" the-value="{{controllerData}}" show-value="showPopup()"></scopetest>
// these codes are inside the controller
$scope.controllerData = 'Oii';
$scope.showPopup = function () {
alert($scope.controllerData);
}
// directive codes
myApp.directive('scopetest', function () { // would work as well if I had used myApp.directive('showValue' . . . restrict: 'A'
return {
restrict: 'E',
template: "<p>controllerData: {{controllerData}}</p> <p>data: {{data}}</p> <p>theData: {{theData}}</p> <p>sameData: {{sameData}}</p> <button type='button' ng-click='showData()'>Click Me!</button>",
scope: {
data: "=aValue",
theData: "@theValue",
sameData: "&aValue", // doesn't work. Seems only work for method.
showData: "&showValue"
}
}
})
See the example in Plunker
In this example we use '=' to pass the value of an outer variable specified in an attribute in the origin markup. I.e., data: "=aValue" will create an internal variable called 'data' and get the value of the outer variable (an AngularJs $scope variable) specified in the 'a-value' attribute in the origin markup. The attribute name referred to follows the same matching pattern as the matching pattern of a directive name (i.e, 'aValue' matches 'a-value' in the origin markup).
'@' is used if we want to copy the literal value (copy the text) of an attribute in the origin markup. If we want to pass a value of an AngularJs variable then we need to use {{ . . . }} to get the value first before passing it to the directive. I.e., <scopetest the-value="{{controllerData}}" />
'&' is used to pass a function of an attribute in the origin markup.
If the attribute name being referred to is the same as the internal variable name then when defining the internal variable, we don't need to specify the attribute name after the symbol. E.g., aValue: "="; is the same as aValue: "=aValue";
When we are using isolate scope, external variables will not be available inside the directive. Only the variables that are declared inside scope: { . . . } will be available.
Transclude
This option is used to pass the matched origin markup and its inner content into a directive template. The parent-to-be element in the template needs to be decorated with ng-transclude attribute. All the inner content of the parent-to-be element will be overwritten with the passed markup. The directive also needs to set transclude: true to activate this option.
<!-- origin markup in the view -->
<trancsludetest>
<div>
This is a content from origin markup.
<br />
Try to print a value: {{value}}
</div>
</trancsludetest>
// code inside controller $scope.value = 'controller value';
// directive codes
myApp.directive('transcludetest', function () {
return {
restrict: 'E',
transclude: true,
template: '<div class="fancy-class" style="border: 2px solid black; padding: 2px"><div class="another--class" style="border: 1px dashed blue" ng-transclude>content inside this div will be ignored</div></div>',
link: function (scope, element) {
scope.value = 'directive value';
},
//scope: {} // if this is used then only external values are considered
};
})
See the example in Plunker
Priority and Terminal
priority option is useful for directives (more than one) that are defined in a single DOM element. The option is used to determine the order of how they are going to be applied, especially for their link and compile functions. The compile and pre-link functions are executed from the greatest number while post-link functions are executed from the smallest number.
If not defined, the priority default value is 0. If the directives have same priority values then it seems that they are executed in alphabetical order.
If terminal option is used then directives that have lower priorities will be disregarded.
<!-- origin markup in the view --> <div second-priority first-priority></div>
// directive codes
myApp.directive('secondPriority', function () {
return {
restrict: 'A',
link: {
pre: function (scope, element, attrs) {
alert('preLink - two');
element.append("<br/>preLink - two");
},
post: function (scope, element, attrs) {
alert('postLink - two');
element.append("<br/>postLink - two");
}
},
priority: 1
}
})
.directive('firstPriority', function () {
return {
restrict: 'A',
link: {
pre: function (scope, element, attrs) {
alert('preLink - one');
element.append("preLink - one");
},
post: function (scope, element, attrs) {
alert('postLink - one');
element.append("<br/>postLink - one");
}
},
priority: 2,
//terminal: true
}
});
See the example in Plunker
Require
This option is used to pass other directive's controller into the link function of the directive where the option is specified. The other directive could be a sibling directive or a directive in one of parent elements.
The require option can take a single string or an array of strings of directive names to be found.
The string name can be:
- not prefixed - to find a sibling directive
- prefixed with '?' - return 'null' if the intended sibling directive is not found
- prefixed with '^' - find the directive in one of parent elements
- prefixed with '^?' - return 'null' if the intended directive is not found in any parent elements
<!-- origin markup in the view --> <div parent-directive> <div> <div> <div sibling-directive require-test></div> </div> </div> </div>
// directive codes
myApp.directive('parentDirective', function () {
return {
restrict: 'A',
controller: function ($scope) {
this.parentData = "<br />parentDirective data.. ";
this.parentFunction = function(param) {
return param + "<br /> - Hi there, this is parentDirective";
};
}
};
})
.directive('siblingDirective', function () {
return {
restrict: 'A',
require: '^parentDirective',
link: function (scope, element, attrs, controller)
{
element.append(controller.parentData);
var test = controller.parentFunction('<br />Hello this is siblingDirective');
element.append(test);
},
controller: function ($scope) {
this.siblingFunction = function(param) {
return param + "<br /> - Hi there, this is your sibling directive";
};
}
};
})
.directive('requireTest', function () {
return {
restrict: 'A',
require: ['?^parentDirective','siblingDirective'],
link: function (scope, element, attrs, controller)
{
element.append(controller[0].parentData);
var test = controller[0].parentFunction('<br />Hello this is requireTest');
element.append(test);
test = controller[1].siblingFunction('<br />Hello this is requireTest');
element.append(test);
}
};
});
See the example in Plunker
Labels:
AngularJS,
JavaScript
Thursday, 15 May 2014
Basic AngularJS Directive
Below is a basic example of using AngularJS directive:
- A - attribute
- C - class, i.e. <div class="my-directive"/>
- E - element, i.e. <my-directive/>
- M - comment, i.e. <!--directive:my-directive-->
The values can also be combined.
template property is used to specify the markup to be added as part of the directive. The markup will be added to the existing container or replace it depending on the replace property value.
Note from the example that we can pass data that is specific to the directive or data from the controller.
If the additional markup is much then we should use templateUrl property instead.
link is used to manipulate scope variables and the directive markup (after combined with the additional markup inside template or templateUrl property if used). It accepts three required and one optional parameters:
- scope - this is the AngularJS scope object
- element - the origin element that is matched, if it is replaced then this is the new element from the template
According to AngularJS documentation, the element is actually a jqLite (a light version of jQuery library from AngularJS) wrapped element. The library contains core functionalities of jQuery and has almost identical API.
However, important to note that if jQuery library is loaded prior to AngularJS libraries then the element will be jQuery wrapped instead so it will have full jQuery functionalities.
- attrs - attributes of the origin element that is matched, if it is replaced then the attributes of the new element from the template
- controller [optional] - to pass the directive's controller instance to be accessible inside the function
On the next post, we will see more advanced examples of AngularJS directive.
References:
AngularJS Developer Guide - Directives
GitHub - AngularJS - Understanding Directives
<!--HTML markup--> <div my-directive></div>
//JavaScript codes
var myApp = angular.module('myApp', []);
myApp.directive('myDirective', function () {
return {
restrict: 'A', // possible values: A, C, E, M
template: "{{controllerData}} <mark>This is my custom directive. '{{directiveData}}'</mark>",
link: function (scope, element, attrs) {
//execute a function then assign a value
scope.directiveData = "Good morning!";
// can change any scope value in the outside as well
// scope.controllerData = "test";
element.on('mouseover', function () {
element.css('font-size', '18px');
},
replace: false // remove the outer container
};
});
restrict property is used to determine which part of origin HTML markup to be matched. The possible values are: - A - attribute
- C - class, i.e. <div class="my-directive"/>
- E - element, i.e. <my-directive/>
- M - comment, i.e. <!--directive:my-directive-->
The values can also be combined.
template property is used to specify the markup to be added as part of the directive. The markup will be added to the existing container or replace it depending on the replace property value.
Note from the example that we can pass data that is specific to the directive or data from the controller.
If the additional markup is much then we should use templateUrl property instead.
link is used to manipulate scope variables and the directive markup (after combined with the additional markup inside template or templateUrl property if used). It accepts three required and one optional parameters:
- scope - this is the AngularJS scope object
- element - the origin element that is matched, if it is replaced then this is the new element from the template
According to AngularJS documentation, the element is actually a jqLite (a light version of jQuery library from AngularJS) wrapped element. The library contains core functionalities of jQuery and has almost identical API.
However, important to note that if jQuery library is loaded prior to AngularJS libraries then the element will be jQuery wrapped instead so it will have full jQuery functionalities.
- attrs - attributes of the origin element that is matched, if it is replaced then the attributes of the new element from the template
- controller [optional] - to pass the directive's controller instance to be accessible inside the function
On the next post, we will see more advanced examples of AngularJS directive.
References:
AngularJS Developer Guide - Directives
GitHub - AngularJS - Understanding Directives
Labels:
AngularJS,
JavaScript
Friday, 25 April 2014
Passing Objects in JSON through ViewBag
Below is an example of how to pass a collection of objects in JSON format through ViewBag.
A different way to do this is by passing the collection directly through ViewBag
List<Student> studentsList = new List<Student>(); studentsList = GetStudents(); ViewBag.Students = System.Web.Helpers.Json.Encode(studentsList);We use System.Web.Helpers.Json.Encode() function to do the formatting and on the view, we just need to render the passed content like this:
@Html.Raw((String)ViewBag.Students)
A different way to do this is by passing the collection directly through ViewBag
ViewBag.Students = studentsList;then we do the formatting on the view
@Html.Raw(Json.Encode((IList<Student>)ViewBag.Students))However this way is less efficient than the earlier one.
Labels:
MVC
Wednesday, 23 April 2014
Building Single Page Application with AngularJS and Web API - Part 2
On this post, we will continue building our single page application with AngularJS and Web API. For the first part of this topic, please see my previous post.
Update Functionality
Now we want to add update user functionality. First, we add a new route in our AngularJS routing configuration:
Other types of parameter can be used as well in a routing path:
- ':name*' is used to store all values from the path of the parameter up to the next matching string path. For example; if we set a route like '/edit/:user*/end' then when we have a url '/edit/1/type/admin/end', :user will have value '1/type/admin'
- 'name?', we can use '?' to specify that this is an optional parameter
Then add logic for updating user in our WebAPI controller:
Also add this in our AngularJS user service:
Then in our AngularJS user controller:
Finally, we add this link to the view:
Delete Functionality
Our delete function does not require a routing because it does not have its on view. The delete function will be executed on the main view.
We specify our Web API method:
Then in our AngularJS user service, we invoke the Web API method with $resource delete method.
We add these codes as well to our controller:
Finally add this delete link to the view:
The complete source codes can be downloaded from here.
Update Functionality
Now we want to add update user functionality. First, we add a new route in our AngularJS routing configuration:
. . .
.when('/edit/:userId', {
templateUrl: 'Partials/create-edit.html',
controller: 'UserCtrl'
}).
. . .
Note that we use a parameter ':userId' in the path. This is used to get a value from the url and will be stored into $routeParams object. A new property called 'userId' will be created in $routeParams object (i.e. $routeParams.userId). Other types of parameter can be used as well in a routing path:
- ':name*' is used to store all values from the path of the parameter up to the next matching string path. For example; if we set a route like '/edit/:user*/end' then when we have a url '/edit/1/type/admin/end', :user will have value '1/type/admin'
- 'name?', we can use '?' to specify that this is an optional parameter
Then add logic for updating user in our WebAPI controller:
// PUT api/UserRegistration/5
public async Task<IHttpActionResult> PutUser(int id, User user)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != user.UserId)
{
return BadRequest();
}
db.Entry(user).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!UserExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
Also add this in our AngularJS user service:
. . .
getById: function (id) {
//return $resource('/api/UserRegistration/' + id).get();
return $resource('/api/UserRegistration/:userId', { userId: '@userId' }).get({ userId: id }); // this is another way to build the string to be passed
},
updateUser: function (user) {
return $resource('/api/UserRegistration/' + user.UserId, {},
{
customUpdate: { method: 'PUT', isArray:false }
}
).customUpdate(user);
},
. . .
Notice that in updateUser function, we need to specify a custom action to invoke our Web API method. By default AngularJS $resource service has these default methods:{ 'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'} };
Each method will invoke a call with the specified HTTP method. We need to specify an additional one with HTTP PUT method to invoke the Web API method. isArray is true if we expect the call to return an array of objects.Then in our AngularJS user controller:
. . .
userService.getById($routeParams.userId).$promise.then(
//success
function (data) {
$scope.user = data;
},
//error
function (response) {
//console.log(response.status)
$scope.error = true;
})
. . .
userService.updateUser(user).$promise.then(
//success
function () { $location.url('/'); },
//error
function () { $scope.error = true }
);
. . .
Finally, we add this link to the view:
<td><a data-ng-href="#edit/{{user.UserId}}">Edit</a></td>
Delete Functionality
Our delete function does not require a routing because it does not have its on view. The delete function will be executed on the main view.
We specify our Web API method:
// DELETE api/UserRegistration/5
[ResponseType(typeof(User))]
public async Task<IHttpActionResult> DeleteUser(int id)
{
User user = await db.Users.FindAsync(id);
if (user == null)
{
return NotFound();
}
db.Users.Remove(user);
await db.SaveChangesAsync();
return Ok(user);
}
Then in our AngularJS user service, we invoke the Web API method with $resource delete method.
. . .
removeUser: function (userId) {
return $resource('/api/UserRegistration/' + userId).delete();
}
. . .
We add these codes as well to our controller:
. . .
$scope.remove = function (index) {
if (confirm('Are you sure to delete this user?')) {
//console.log(index);
var userId = $scope.users[index].UserId;
//console.log(userId);
userService.removeUser(userId).$promise.then(
//success
function () { $scope.users.splice(index, 1); },
//error
function () { $scope.error = true }
);
}
}
. . .
Finally add this delete link to the view:
<td><a href="" data-ng-click="remove($index)">Delete</a></td>Notice that we use $index property of ng-repeat in the view and pass it to the controller function to be able to know which particular record is being clicked. Then we can get any property's value of the record.
The complete source codes can be downloaded from here.
Labels:
AngularJS,
JavaScript,
Web API
Monday, 24 March 2014
Building Single Page Application with AngularJS and Web API - Part 1
On this post, we will see the first part of building a simple single page CRUD application using AngularJs and Web API. The application will manage user registrations data. Complete source codes can be downloaded from here.
First we configure AngularJS routing for our views. See this post to learn more about AngularJS routing.
Create Functionality
Create the partial html page for creating a user.
Then create our Web API method (this one is scaffoled by Visual Studio when creating a new controller):
Now, we create our AngularJS custom service to call the Web API method:
Finally we create our AngularJS controller:
Note that $promise is also used to determine what action to take if the service call is successful and if it has error. $promise is part of $resource service. If it is successful, $promise will return data object (similar to the one we passed to the service call). If it fails, it will return a HTTP response object. It might be useful to see the headers and status properties of the response object.
$scope.error variable is used to show an error message if an error has occurred. This variable is referred to by ng-show in our partial view.
Listing Functionality
We have already implemented the create user functionality, now let us do the listing function.
First, create the partial view.
Then the Web API method:
Add our user service with this method:
Then add the following codes in our controller:
In the coming post, we will see the edit and delete functionality.
First we configure AngularJS routing for our views. See this post to learn more about AngularJS routing.
userApp.config(['$routeProvider',
function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'Partials/home.html', // this is the partial view for listing page
controller: 'UserCtrl'
})
.when('/create', {
templateUrl: 'Partials/create-edit.html', // this is the partial view for creating user page
controller: 'UserCtrl'
})
otherwise({
redirectTo: '/'
});
}]);
Create Functionality
Create the partial html page for creating a user.
<div> <span style="color:red" data-ng-show="error">An error has occured.</span> </div> <div> <label>First Name</label> <input type="text" data-ng-model="user.Firstname" /> </div> <div> <label>Last Name</label> <input type="text" data-ng-model="user.Lastname" /> </div> <div> <label>Organisation</label> <input type="text" data-ng-model="user.Organisation" /> </div> <div> <label>Position</label> <input type="text" data-ng-model="user.Position" /> </div> <div> <label>Email</label> <input type="text" data-ng-model="user.Email" /> </div> <button data-ng-click="submit(user)">Save</button>Note that there are some ng-model attributes on the input fields to instruct AngularJS to bind the value. The attributes were prefixed with 'data-' to be HTML5 compliant. ng-click attribute is also used to tell AngularJS to execute a function.
Then create our Web API method (this one is scaffoled by Visual Studio when creating a new controller):
// POST api/UserRegistration
[ResponseType(typeof(User))]
public async Task<IHttpActionResult> PostUser(User user)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Users.Add(user);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { id = user.UserId }, user);
}
Now, we create our AngularJS custom service to call the Web API method:
userApp.factory('userService', ['$resource', function ($resource) {
return {
createUser: function (user) {
return $resource('/api/UserRegistration').save(user);
}
}
}]);
Here AngularJS $resource service is used to interact with the Web API method. $resource service is useful for interacting with RESTful based services such as Web API. It is simpler to use than $http service. To use this service, ngResource module will need to be included as well in our application. In this JavaScript function, we call the save() method of the $resource service, passing user object as the argument. The service will invoke a HTTP POST call to the specified url.Finally we create our AngularJS controller:
userApp.controller('UserCtrl', ['$scope', '$location', '$routeParams', 'userService', function ($scope, $location, $routeParams, userService) {
$scope.submit = function (user) {
$scope.error = false;
userService.createUser(user).$promise.then(
//success
function (data) { $location.url('/'); },
//error
function (response) {
//console.log(response);
//console.log(response.status);
$scope.error = true }
);
}
}]);
Here we create a submit function that calls the user service that we have just created. The submit function is called by the save button in our partial view through ng-click attribute.Note that $promise is also used to determine what action to take if the service call is successful and if it has error. $promise is part of $resource service. If it is successful, $promise will return data object (similar to the one we passed to the service call). If it fails, it will return a HTTP response object. It might be useful to see the headers and status properties of the response object.
$scope.error variable is used to show an error message if an error has occurred. This variable is referred to by ng-show in our partial view.
Listing Functionality
We have already implemented the create user functionality, now let us do the listing function.
First, create the partial view.
<div>
<span style="color:red" data-ng-show="error">An error has occured.</span>
</div>
<table>
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Email</th>
</tr>
</thead>
<tbody ng-repeat="user in users">
<tr>
<td>{{ user.Firstname }}</td>
<td>{{ user.Lastname }}</td>
<td>{{ user.Email }}</td>
</tr>
</tbody>
</table>
Then the Web API method:
// GET api/UserRegistration
public IQueryable<User> GetUsers()
{
return db.Users;
}
Add our user service with this method:
getAllUsers: function () {
return $resource('/api/UserRegistration').query();
}
Here we use the $resource service's query() method that will invoke a HTTP GET call.Then add the following codes in our controller:
$scope.init = function () {
// Get all users
userService.getAllUsers().$promise.then(
//success
function (data) {
$scope.users = data;
},
//error
function (response) {
//console.log(response);
//console.log(response.status);
$scope.error = true;
}
);
}
In the coming post, we will see the edit and delete functionality.
Labels:
AngularJS,
JavaScript,
Web API
Thursday, 6 March 2014
Validating Model with Data Annotations in Unit Test
We could use the classes inside System.ComponentModel.DataAnnotations to help us in testing model with data annotations.
First, include the assembly in the test project reference libraries.
Then we can use this method of Validator class to do the validation, along with a ValidationContext instance and a collection of ValidationResult type as parameters.
Below is a full example:
First, include the assembly in the test project reference libraries.
Then we can use this method of Validator class to do the validation, along with a ValidationContext instance and a collection of ValidationResult type as parameters.
public static bool TryValidateObject( Object instance, ValidationContext validationContext, ICollection<ValidationResult> validationResults, bool validateAllProperties )We need to pass a validation context instance, a collection to hold the result of each failed validation and a boolean value to indicate whether to validate all properties that are decorated with data annotations or only properties that are decorated with [Required] attribute only. To create a new instance of validation context, we could simply pass the object to be validated into ValidationContext() constructor method:
ValidationContext context = new ValidationContext(object_to_be_validated);
Below is a full example:
List<ValidationResult> validationResults = null;
var itemSellingDto = new ItemSellingDto() { InvoiceId = 0, ItemId = 1, ItemName = "", ItemSellingId = 0, Price = 0, Quantity = 10 };
var validationContext = new ValidationContext(itemSellingDto);
var isValid = Validator.TryValidateObject(itemSellingDto, validationContext, validationResults, validateAllProperties: true);
isValid.should_be(false);
validationResults.Any(vr => vr.ErrorMessage == "Price must be bigger than 0").should_be(true);
Saturday, 1 March 2014
TransactionScope and SaveChanges in Entity Framework
TransactionScope class in .Net is great but if not used properly can cause table locks for long time and suffer application performance.
When using it with Entity Framework, only use TransactionScope when operation cannot be done within one SaveChanges() method or involves more than one data context.
Let's see the following codes. Imagine for some reasons, two data contexts are used.
To enable tracing transactions, go to 'Events Selection' tab, click 'Show all events' then scroll to almost the end, expand 'Transactions' and tick the ones starting with 'TM: ...'
What should have been done is like the following:
Secondly, if there is only one data context needs to be updated, TransactionScope is not needed. Calling SaveChanges() method alone is enough and will create a transaction in database and execute any changes that have been made to the objects within the context.
For more information about TransactionScope, please see my previous article.
When using it with Entity Framework, only use TransactionScope when operation cannot be done within one SaveChanges() method or involves more than one data context.
Let's see the following codes. Imagine for some reasons, two data contexts are used.
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
// some codes that do not involve database
// some queries
var student = contextOne.Students.Where( . . . );
var schoolList = contextTwo.Schools;
// more queries and validations
// check if student is allowed to move out ...
// check if student is allowed to move in ...
// update student
student.School = newSchool;
// update some data in school context
. . .
contextOne.SaveChanges();
contextTwo.SaveChanges();
scope.Complete();
}
When we check SQL Profiler with tracing transactions enabled, we can see that Begin Transaction is executed immediately before the first database related operation. In this case is before the first data context querying a student (line #6). The transaction is wrapped up after the two data contexts are updated. This is a long time of locking and far beyond the necessary.
To enable tracing transactions, go to 'Events Selection' tab, click 'Show all events' then scroll to almost the end, expand 'Transactions' and tick the ones starting with 'TM: ...'
What should have been done is like the following:
// some codes that do not involve database
// some queries
var student = contextOne.Students.Where( . . . );
var schoolList = contextTwo.Schools;
// more queries and validations
// check if student is allowed to move out ...
// check if student is allowed to move in ...
// update student
student.School = newSchool;
// update some data in school context
. . .
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
contextOne.SaveChanges();
contextTwo.SaveChanges();
scope.Complete();
}
You can add try catch as well around the codes and discard the changes when there is an error.
Secondly, if there is only one data context needs to be updated, TransactionScope is not needed. Calling SaveChanges() method alone is enough and will create a transaction in database and execute any changes that have been made to the objects within the context.
For more information about TransactionScope, please see my previous article.
Labels:
Entity Framework,
SQL
Wednesday, 19 February 2014
Routing in AngularJs
On this post we will see the basic of how to create Single Page Application with AngularJs.
First we need to add reference to AngularJs routing library.
Then we add dependency to ngRoute module in our application module.
Next, use $routeProvider service that will be available after we include the new module to configure the routes that we have. The basic syntax is as follow:
Below is an example of a routes configuration:
Finally we use ng-view directive to apply the configuration in our main view. For example:
Below is an example of the complete main view:
If we want to use HTML5 url mode that is supported by recent browsers, on myApp.config(...) method add a dependency to $locationProvider service then add this line inside the method:
First we need to add reference to AngularJs routing library.
<script src="Scripts/angular.min.js"></script> <script src="Scripts/angular-route.min.js"></script>
Then we add dependency to ngRoute module in our application module.
var myApp = angular.module('myApp', ['ngRoute']);
Next, use $routeProvider service that will be available after we include the new module to configure the routes that we have. The basic syntax is as follow:
$routeProvider.when('desired_path'), {
templateUrl: 'location_of_the_partial_view_file',
controller: 'controller_name_for_this_partial_view'
}
The path can contain route parameter(s) which is specified with colon (:) and could end with a star (*). Also we could have an optional parameter with a question mark (?). For more information, please see $routeProvider documentation.Below is an example of a routes configuration:
myApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/pageOne', {
templateUrl: 'partials/page-one.html',
controller: 'PageOneCtrl'
})
.when('/pageTwo', {
templateUrl: 'partials/page-two.html',
controller: 'PageTwoCtrl'
})
.otherwise({
redirectTo: '/'
});
} ]);
Finally we use ng-view directive to apply the configuration in our main view. For example:
<div data-ng-view></div>
Below is an example of the complete main view:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" data-ng-app="myApp">
<head>
<title></title>
<script src="http://code.angularjs.org/1.2.9/angular.min.js" type="text/javascript"></script>
<script src="http://code.angularjs.org/1.2.9/angular-route.min.js" type="text/javascript"></script>
<script src="Scripts/app.js" type="text/javascript"></script>
<script src="Scripts/Home/page-one-controller.js" type="text/javascript"></script>
<script src="Scripts/Home/page-two-controller.js" type="text/javascript"></script>
</head>
<body>
<a href="#">Home</a> | <a href="#pageOne">Page One</a> | <a href="#pageTwo">Page Two</a>
<div data-ng-view></div>
</body>
</html>
If we want to use HTML5 url mode that is supported by recent browsers, on myApp.config(...) method add a dependency to $locationProvider service then add this line inside the method:
$locationProvider.html5Mode(true)
Labels:
AngularJS,
JavaScript
Tuesday, 18 February 2014
Example of Using SQL Script Directly in Entity Framework
Below is a code example of how to use direct SQL script command and normal entity operation in Entity Framework 5. TransactionScope is used to cover the operations to do all if both are successful or nothing at all:
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
context.ExecuteStoreCommand("UPDATE Class Set Number = 20");
context.Student.Add(new Student{ StudentId = 1, Firstname = "first", Lastname = "last" });
context.SaveChanges();
scope.Complete();
}
Labels:
Entity Framework
Friday, 7 February 2014
Getting Started with AngularJS
On this post we will see a simple example of an AngularJS application.
First we need to reference the library on our application. We could copy the files into our project or use the Angular CDN http://code.angularjs.org/
Then create a module. Using module is not required but it is useful to have better code organisation and for reusability.
Next create a controller:
We have also used an inline dependency injection to provide $scope service to the controller. To inject one or more components using inline annotation, we just need to put square brackets around the function, specify the components before the function and put the referred components in the same order in the function's arguments. For example:
Then we create our view:
If you notice I have put the first code snippet on this post (for creating the application module) inside app.js file and the controller codes inside greeting-controller.js file.
Further reading:
Code Organization in Large AngularJS and JavaScript Applications
AngularJS Dependency Injection
First we need to reference the library on our application. We could copy the files into our project or use the Angular CDN http://code.angularjs.org/
Then create a module. Using module is not required but it is useful to have better code organisation and for reusability.
angular.module('myApp', []);
The square brackets on the second argument is for specifying any dependencies required. At the moment we don't have any.Next create a controller:
angular.module('myApp').controller('GreetingCtrl', ['$scope', function ($scope) {
$scope.greeting = 'Hola!';
$scope.echoThis = function (value) {
var processedWord = "";
if (value) {
processedWord = 'Echo ' + value;
}
return processedWord;
};
$scope.print = function (value) {
$scope.result = $scope.word + value + " is printed";
};
} ]);
Note that we use angular.module('myApp') to retrieve our module that we have created. If we have used angular.module('myApp',[]) instead, this would have recreated 'myApp' module again thus overwrited the previous module declaration. Use angular.module('myApp').controller() to define a controller inside a module.We have also used an inline dependency injection to provide $scope service to the controller. To inject one or more components using inline annotation, we just need to put square brackets around the function, specify the components before the function and put the referred components in the same order in the function's arguments. For example:
['dep1', 'dep2', 'depN', function(dep1, renamedDep2, depN){ ... }]
Note also that the argument names can be different than the specified component names as long as they have the same order. $scope is a built-in Angular service that in simple term can be said similar to a global container object that has properties and/or functional properties (have methods attached). $scope properties will be available on the views. Line 1 shows a normal property while line 4 and 12 show functional properties.Then we create our view:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" data-ng-app="myApp">
<head>
<title></title>
<script src="http://code.angularjs.org/1.2.9/angular.min.js" type="text/javascript"></script>
<script src="Scripts/app.js" type="text/javascript"></script>
<script src="Scripts/Home/greeting-controller.js" type="text/javascript"></script>
</head>
<body>
<span>{{ "Hello" + " World"}}</span>
<div data-ng-controller="GreetingCtrl">
{{ greeting }}
<br /><br />
<input data-ng-model="word" /> {{ echoThis(word) }}
<br />
<br />
<button data-ng-click="print(' some text ')">Print</button>
{{ result }}
</div>
</body>
</html>
We have some attributes (which they called directives) in our view; ng-app is used to bootstrap AngularJs and the value is used to specify the root module to be used in the view, ng-controller to specify which controller to use for that particular section of the view, ng-model to bind input value and ng-click to execute a function when the element is clicked.If you notice I have put the first code snippet on this post (for creating the application module) inside app.js file and the controller codes inside greeting-controller.js file.
Further reading:
Code Organization in Large AngularJS and JavaScript Applications
AngularJS Dependency Injection
Labels:
AngularJS,
JavaScript
Friday, 17 January 2014
Checking Debug Mode on an MVC View
To check whether our project is in debug/release mode on a particular view, we would not be able to use #if DEBUG #endif on our view because a view is not compiled. To do this, we need to run the logic from the back end. Below is an example of an extension method to check that:
Then on our view, we could call the method:
public static bool IsDebug(this HtmlHelper htmlHelper)
{
#if DEBUG
return true;
#else
return false;
#endif
}
Then on our view, we could call the method:
@if (Html.IsDebug())
{
@Html.Raw("<span style='color: red'>DEBUGGING MODE IS ON</span>");
}
Labels:
.NET
Friday, 10 January 2014
Some Notes about TransactionScope in .NET
Basic usage of TransactionScope is as follow:
By default, the isolation level used is Serializable and timeout is 1 minute.
According to this post on MSDN Blogs, the default isolation level is likely to introduce deadlock issues. The recommended isolation level is Read Committed.
If our codes are using more than one database (distributed transaction) then they will be much slower compare to only using one database (local transaction).
The maximum timeout of TransactionScope when setting this up via code (Timeout property) is 10 minutes. If we set the property to have a value larger than this, it would fall back to 10 minutes. If we want longer timeout value, we will need to set this in machine.config file. However that will affect other applications on the server.
Below is how to set isolation level and timeout in codes:
If we want to change the timeout in machine.config:
When using SQL server, we also need to concern about the timeout of running query/command or stored procedure. The default timeout is 30 seconds. We can extend the timeout by setting CommandTimeout property.
using (var scope = new TransactionScope())
{
// some codes here . . .
scope.Complete();
}
By default, the isolation level used is Serializable and timeout is 1 minute.
According to this post on MSDN Blogs, the default isolation level is likely to introduce deadlock issues. The recommended isolation level is Read Committed.
If our codes are using more than one database (distributed transaction) then they will be much slower compare to only using one database (local transaction).
The maximum timeout of TransactionScope when setting this up via code (Timeout property) is 10 minutes. If we set the property to have a value larger than this, it would fall back to 10 minutes. If we want longer timeout value, we will need to set this in machine.config file. However that will affect other applications on the server.
Below is how to set isolation level and timeout in codes:
var transactionOptions = new TransactionOptions(); transactionOptions.IsolationLevel = IsolationLevel.ReadCommitted; transactionOptions.Timeout = TransactionManager.MaximumTimeout; var transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions);
If we want to change the timeout in machine.config:
<system.transactions> <machineSettings maxTimeout="00:30:00" /> </system.transactions>
When using SQL server, we also need to concern about the timeout of running query/command or stored procedure. The default timeout is 30 seconds. We can extend the timeout by setting CommandTimeout property.
Labels:
.NET
Tuesday, 24 December 2013
Enabling Automatic Code First Migration
This post is describing how to set automatic Entity Framework Code Fist migration, for the manual migration please see my previous post.
Automatic Code First migration feature is useful during development phase when database has not gone into production environment yet.
If you haven't got an EF migration Configuration.cs file then you can run this command on Package Manager Console:
If you have already got the file, make sure that AutomaticMigrationsEnabled property setting is set to true in the constructor.
Secondly, ensure that MigrateDatabaseToLatestVersion initialisation option is set on the project startup file (for example; inside global.asax)
Configuration: this is the Configuration file discussed earlier. You would need to make the class to be public if you put the initialiser inside other project.
Also if we want the automatic migration to allow data loss (for example; allowing column to be removed) then AutomaticMigrationDataLossAllowed property would need to be set to true.
So the constructor will have these settings:
Automatic Code First migration feature is useful during development phase when database has not gone into production environment yet.
If you haven't got an EF migration Configuration.cs file then you can run this command on Package Manager Console:
Enable-Migrations –EnableAutomaticMigrationsThis will add a folder called 'Migrations' in the project and a file called Configuration.cs with this setting in the constructor method:
AutomaticMigrationsEnabled = true;
If you have already got the file, make sure that AutomaticMigrationsEnabled property setting is set to true in the constructor.
Secondly, ensure that MigrateDatabaseToLatestVersion initialisation option is set on the project startup file (for example; inside global.asax)
Database.SetInitializer(new MigrateDatabaseToLatestVersion<DatabaseContext, Configuration>());DatabaseContext: your database context class name
Configuration: this is the Configuration file discussed earlier. You would need to make the class to be public if you put the initialiser inside other project.
Also if we want the automatic migration to allow data loss (for example; allowing column to be removed) then AutomaticMigrationDataLossAllowed property would need to be set to true.
So the constructor will have these settings:
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
Labels:
Code First,
Entity Framework
Monday, 23 December 2013
Code First Migration
Let say we are using Entity Framework Code First for our project and have a class below:

The table has one record initially.
To enable the migration feature, type enable-migrations on Package Manager Console. Some messages will be displayed when the command has finished running.

[timestamp]_InitialCreate.cs is created because the database has already exists when the first time we access the database context. If the database was still empty then only Configuration.cs file would be added.
There are two main commands for the migration feature:
- add-migration - add migration codes in the code layer (under 'Migrations' folder)
- update-database - update the database according to the migration codes written in the code layer
Now let's try to change our model to add a new property:

Now try update-database command to apply the changes to the database:
Then check __MigrationHistory table again. As we can see, a new record is created in the table with Id value the same as the name of the newly generated file.

If necessary, we could customise the codes in the file generated by the add-migration command.
update-database command also has a few parameters that could be useful. We will see briefly TargetMigration, SourceMigration and Script parameters.
To upgrade/downgrade the database to a specific state, use -TargetMigration parameter. For example:
To get the changes in script only without applying those to database, use -Script parameter:
Starting from EF6, we could use the generated scripts to update from any previous state to the one specified as the target. The scripts have logic to check the states based on entries in __MigrationsHistory table.
Reference:
http://msdn.microsoft.com/en-us/data/jj591621.aspx
public class Student
{
public int StudentId { get; set; }
public string Name { get; set; }
}
When we use the database context for the first time, the database will be created with a table called __MigrationHistory.
The table has one record initially.
To enable the migration feature, type enable-migrations on Package Manager Console. Some messages will be displayed when the command has finished running.
PM> enable-migrations Checking if the context targets an existing database... Detected database created with a database initializer. Scaffolded migration '201312050353336_InitialCreate' corresponding to existing database. To use an automatic migration instead, delete the Migrations folder and re-run Enable-Migrations specifying the -EnableAutomaticMigrations parameter. Code First Migrations enabled for project CodeFirstMigrationTest.A folder called 'Migrations' with two files are created.

[timestamp]_InitialCreate.cs is created because the database has already exists when the first time we access the database context. If the database was still empty then only Configuration.cs file would be added.
There are two main commands for the migration feature:
- add-migration - add migration codes in the code layer (under 'Migrations' folder)
- update-database - update the database according to the migration codes written in the code layer
Now let's try to change our model to add a new property:
. . .
public DateTime DOB { get; set; }
. . .
Then run add-migration command to add the change:PM> add-migration addDOB Scaffolding migration 'addDOB'. The Designer Code for this migration file includes a snapshot of your current Code First model. This snapshot is used to calculate the changes to your model when you scaffold the next migration. If you make additional changes to your model that you want to include in this migration, then you can re-scaffold it by running 'Add-Migration addDOB' again.[timestamp]_addDOB.cs file is created.

Now try update-database command to apply the changes to the database:
PM> update-database Specify the '-Verbose' flag to view the SQL statements being applied to the target database. Applying explicit migrations: [201312081946270_addDOB]. Applying explicit migration: 201312081946270_addDOB. Running Seed method.
Then check __MigrationHistory table again. As we can see, a new record is created in the table with Id value the same as the name of the newly generated file.

If necessary, we could customise the codes in the file generated by the add-migration command.
update-database command also has a few parameters that could be useful. We will see briefly TargetMigration, SourceMigration and Script parameters.
To upgrade/downgrade the database to a specific state, use -TargetMigration parameter. For example:
Update-Database –TargetMigration: addDOBTo roll back to empty database, use $InitialDatabase:
Update-Database –TargetMigration: $InitialDatabase
To get the changes in script only without applying those to database, use -Script parameter:
Update-Database -Script -SourceMigration: [initialState] -TargetMigration: [targetState]If -SourceMigration is not specified then it will use the current database state. If -TargetMigration is not specified then the latest state will be assumed. For example, the command below will generate all migration scripts from empty database up to the addDOB migration state.
Update-Database -Script -SourceMigration: $InitialDatabase -TargetMigration: addDOB
Starting from EF6, we could use the generated scripts to update from any previous state to the one specified as the target. The scripts have logic to check the states based on entries in __MigrationsHistory table.
Reference:
http://msdn.microsoft.com/en-us/data/jj591621.aspx
Labels:
Code First,
Entity Framework
Friday, 22 November 2013
Using Text Description in Enumeration Type
If we would like to have text description for an enum, we could use Description attribute like below:
Then to get the description text we could use something like the codes below. Usually we would put this in a helper class.
Finally the usage:
Reference:
http://blog.spontaneouspublicity.com/associating-strings-with-enums-in-c
public enum MyEnum
{
[Description("Value One")]
One,
[Description("Second Value")]
Two
};
Then to get the description text we could use something like the codes below. Usually we would put this in a helper class.
public static string GetEnumDescription(Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
return attributes[0].Description;
else
return value.ToString();
}
Finally the usage:
Helper.GetEnumDescription(MyEnum.Two);
Reference:
http://blog.spontaneouspublicity.com/associating-strings-with-enums-in-c
Labels:
.NET
Friday, 15 November 2013
Using Editor Template for Rendering DropDownList
Below is an example of how to have a dropdownlist when editing a model that has an integer type property used for foreign key property. I.e.; I have a model that has this property:
For more details about Editor Template or Display Template, you could see my previous post.
int ItemId { get; set; }
Then I want every time an instance of this model type is being edited, the field is shown as a dropdownlist. To do this, inside 'EditorTemplates' folder under the global 'Views\Shared' or a view's specific folder, I create a partial view named 'Item.cshtml' like below:@Html.DropDownListFor(model => ViewData.TemplateInfo.FormattedModelValue, ((IEnumerable<Model.Item>)ViewBag.Items).Select(option => new SelectListItem {
Text = option.Type.Name,
Value = option.ItemId.ToString(),
Selected = (Model != null) && (option.ItemId == (int)ViewData.TemplateInfo.FormattedModelValue)
}), "Please select")
Note that, I use ViewData.TemplateInfo.FormattedModelValue to get the value. I could also write like this by declaring the model to use:@model int
@Html.DropDownListFor(model => model, ((IEnumerable<Model.Item>)ViewBag.Items).Select(option => new SelectListItem {
Text = option.Type.Name,
Value = option.ItemId.ToString(),
Selected = option.ItemId == Model
}), "Please select")
Then to render the custom template on my edit view page, I do:@Html.EditorFor(model => model.ItemId, "Item")
For more details about Editor Template or Display Template, you could see my previous post.
Labels:
MVC
Subscribe to:
Posts (Atom)
