Ng-repeat | orderby 'field name'
$scope.sortorder
You can use a drop down list
So user
Ng-repeat | orderby 'field name'
$scope.sortorder
You can use a drop down list
So user
// Wrap an already existing instance
PrivateObject accessor = new PrivateObject( objectInstanceToBeWrapped );
// Retrieve a private field
MyReturnType accessiblePrivateField = (MyReturnType) accessor.GetField( "privateFieldName" );
// Call a private method
accessor.Invoke( "PrivateMethodName", new Object[] {/* ... */} );
stackoverflow.com/questions/21919962/angular-share-data-between-controllers
http://stackoverflow.com/questions/21919962/angular-share-data-between-controllers
Url rewrite module
http://blogs.msdn.com/b/webdev/archive/2013/09/20/understanding-security-features-in-spa-template.aspx
http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
https://bitbucket.org/david.antaramian/so-21662778-spa-authentication-example/overview
http://blogs.msdn.com/b/webdev/archive/2013/09/20/understanding-security-features-in-spa-template.aspx
http://www.codeproject.com/script/Membership/View.aspx?mid=10859808
http://blog.pluralsight.com/angularjs-step-by-step-controllers
Angularjs service created by factory
Directive
Form can be embedded
Note: Make sure you have checked all the dependencies for this lab before running the setup.
Note: Each exercise is accompanied by a starting solution located in the Begin folder of the exercise that allows you to follow each exercise independently of the others. Please be aware that the code snippets that are added during an exercise are missing from these starting solutions and may not work until you have completed the exercise. Inside the source code for an exercise, you will also find an End folder containing a Visual Studio solution with the code that results from completing the steps in the corresponding exercise. You can use these solutions as guidance if you need additional help as you work through this hands-on lab.
Note: When you first start Visual Studio, you must select one of the predefined settings collections. Each predefined collection is designed to match a particular development style and determines window layouts, editor behavior, IntelliSense code snippets, and dialog box options. The procedures in this lab describe the actions necessary to accomplish a given task in Visual Studio when using the General Development Settings collection. If you choose a different settings collection for your development environment, there may be differences in the steps that you should take into account.
Note: By adding these files, you are adding the data model, the Entity Framework's database context and the database initializer for the Geek Quiz application.Entity Framework (EF) is an object-relational mapper (ORM) that enables you to create data access applications by programming with a conceptual application model instead of programming directly using a relational storage schema. You can learn more about Entity Framework here.
The following is a description of the classes you just added:
- TriviaOption: represents a single option associated with a quiz question
- TriviaQuestion: represents a quiz question and exposes the associated options through the Options property
- TriviaAnswer: represents the option selected by the user in response to a quiz question
- TriviaContext: represents the Entity Framework's database context of the Geek Quiz application. This class derives from DContext and exposes DbSet properties that represent collections of the entities described above.
- TriviaDatabaseInitializer: the implementation of the Entity Framework initializer for the TriviaContext class which inherits from CreateDatabaseIfNotExists. The default behavior of this class is to create the database only if it does not exist, inserting the entities specified in the Seed method.
using GeekQuiz.Models;
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
System.Data.Entity.Database.SetInitializer(new TriviaDatabaseInitializer());
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
namespace GeekQuiz.Controllers
{
[Authorize]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
...
}
}
Note: The Authorize filter checks to see if the user is authenticated. If the user is not authenticated, it returns HTTP status code 401 (Unauthorized) without invoking the action. You can apply the filter globally, at the controller level, or at the level of individual actions.
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - Geek Quiz</title>
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
</head>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
@Html.ActionLink("Geek Quiz", "Index", "Home", null, new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>@Html.ActionLink("Play", "Index", "Home")</li>
</ul>
@Html.Partial("_LoginPartial")
</div>
</div>
</div>
<div class="container body-content">
@RenderBody()
<hr />
<footer>
<p>© @DateTime.Now.Year - Geek Quiz</p>
</footer>
</div>
using Newtonsoft.Json.Serialization;
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Use camel case for JSON data.
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Note: The CamelCasePropertyNamesContractResolver automatically converts property names to camel case, which is the general convention for property names in JavaScript.
Note: ASP.NET Scaffolding is a code generation framework for ASP.NET Web applications. Visual Studio 2013 includes pre-installed code generators for MVC and Web API projects. You should use scaffolding in your project when you want to quickly add code that interacts with data models in order to reduce the amount of time required to develop standard data operations.
The scaffolding process also ensures that all the required dependencies are installed in the project. For example, if you start with an empty ASP.NET project and then use scaffolding to add a Web API controller, the required Web API NuGet packages and references are added to your project automatically.
using System.Data.Entity;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Description;
using GeekQuiz.Models;
public class TriviaController : ApiController
{
private TriviaContext db = new TriviaContext();
protected override void Dispose(bool disposing)
{
if (disposing)
{
this.db.Dispose();
}
base.Dispose(disposing);
}
}
Note: The Dispose method of TriviaController invokes the Dispose method of the TriviaContext instance, which ensures that all the resources used by the context object are released when the TriviaContext instance is disposed or garbage-collected. This includes closing all database connections opened by Entity Framework.
private async Task<TriviaQuestion> NextQuestionAsync(string userId)
{
var lastQuestionId = await this.db.TriviaAnswers
.Where(a => a.UserId == userId)
.GroupBy(a => a.QuestionId)
.Select(g => new { QuestionId = g.Key, Count = g.Count() })
.OrderByDescending(q => new { q.Count, QuestionId = q.QuestionId })
.Select(q => q.QuestionId)
.FirstOrDefaultAsync();
var questionsCount = await this.db.TriviaQuestions.CountAsync();
var nextQuestionId = (lastQuestionId % questionsCount) + 1;
return await this.db.TriviaQuestions.FindAsync(CancellationToken.None, nextQuestionId);
}
// GET api/Trivia
[ResponseType(typeof(TriviaQuestion))]
public async Task<IHttpActionResult> Get()
{
var userId = User.Identity.Name;
TriviaQuestion nextQuestion = await this.NextQuestionAsync(userId);
if (nextQuestion == null)
{
return this.NotFound();
}
return this.Ok(nextQuestion);
}
private async Task<bool> StoreAsync(TriviaAnswer answer)
{
this.db.TriviaAnswers.Add(answer);
await this.db.SaveChangesAsync();
var selectedOption = await this.db.TriviaOptions.FirstOrDefaultAsync(o => o.Id == answer.OptionId
&& o.QuestionId == answer.QuestionId);
return selectedOption.IsCorrect;
}
// POST api/Trivia
[ResponseType(typeof(TriviaAnswer))]
public async Task<IHttpActionResult> Post(TriviaAnswer answer)
{
if (!ModelState.IsValid)
{
return this.BadRequest(this.ModelState);
}
answer.UserId = User.Identity.Name;
var isCorrect = await this.StoreAsync(answer);
return this.Ok<bool>(isCorrect);
}
[Authorize]
public class TriviaController : ApiController
{
...
}
Note: Make sure that Internet Explorer is selected in the Start button located on the Visual Studio toolbar.
Note: When the application starts, the default MVC route is triggered, which by default is mapped to the Index action of the HomeController class. Since HomeController is restricted to authenticated users (remember that you decorated that class with the Authorize attribute in Exercise 1) and there is no user authenticated yet, the application redirects the original request to the log in page.
Note: Once the download finishes, you will be prompted to make an action with the downloaded file. Leave the dialog box open in order to be able to watch the response content through the Developers Tool window.
Note: For more information about AngularJS, refer to http://angularjs.org/.
Install-Package AngularJS.Core
angular.module('QuizApp', [])
.controller('QuizCtrl', function ($scope, $http) {
$scope.answered = false;
$scope.title = "loading question...";
$scope.options = [];
$scope.correctAnswer = false;
$scope.working = false;
$scope.answer = function () {
return $scope.correctAnswer ? 'correct' : 'incorrect';
};
});
Note: The constructor function of the QuizCtrl controller expects an injectable parameter named $scope. The initial state of the scope should be set up in the constructor function by attaching properties to the $scope object. The properties contain the view model, and will be accessible to the template when the controller is registered.
The QuizCtrl controller is defined inside a module named QuizApp. Modules are units of work that let you break your application into separate components. The main advantages of using modules is that the code is easier to understand and facilitates unit testing, reusability and maintainability.
.controller('QuizCtrl', function ($scope, $http) {
...
$scope.nextQuestion = function () {
$scope.working = true;
$scope.answered = false;
$scope.title = "loading question...";
$scope.options = [];
$http.get("/api/trivia").success(function (data, status, headers, config) {
$scope.options = data.options;
$scope.title = data.title;
$scope.answered = false;
$scope.working = false;
}).error(function (data, status, headers, config) {
$scope.title = "Oops... something went wrong";
$scope.working = false;
});
};
};
Note: This function retrieves the next question from the Trivia Web API created in the previous exercise and attaches the question data to the $scope object.
.controller('QuizCtrl', function ($scope, $http) {
...
$scope.sendAnswer = function (option) {
$scope.working = true;
$scope.answered = true;
$http.post('/api/trivia', { 'questionId': option.questionId, 'optionId': option.id }).success(function (data, status, headers, config) {
$scope.correctAnswer = (data === "true");
$scope.working = false;
}).error(function (data, status, headers, config) {
$scope.title = "Oops... something went wrong";
$scope.working = false;
});
};
};
Note: This function sends the answer selected by the user to the Trivia Web API and stores the result –i.e. if the answer is correct or not– in the $scope object.
The nextQuestion and sendAnswer functions from above use the AngularJS $http object to abstract the communication with the Web API via the XMLHttpRequest JavaScript object from the browser. AngularJS supports another service that brings a higher level of abstraction to perform CRUD operations against a resource through RESTful APIs. The AngularJS $resource object has action methods which provide high-level behaviors without the need to interact with the $http object. Consider using the $resource object in scenarios that requires the CRUD model (fore information, see the $resource documentation).
@{
ViewBag.Title = "Play";
}
<div id="bodyContainer" ng-app="QuizApp">
<section id="content">
<div class="container" >
<div class="row">
<div class="flip-container text-center col-md-12" ng-controller="QuizCtrl" ng-init="nextQuestion()">
<div class="back" ng-class="{flip: answered, correct: correctAnswer, incorrect:!correctAnswer}">
<p class="lead">{{answer()}}</p>
<p>
<button class="btn btn-info btn-lg next option" ng-click="nextQuestion()" ng-disabled="working">Next Question</button>
</p>
</div>
<div class="front" ng-class="{flip: answered}">
<p class="lead">{{title}}</p>
<div class="row text-center">
<button class="btn btn-info btn-lg option" ng-repeat="option in options" ng-click="sendAnswer(option)" ng-disabled="working">{{option.title}}</button>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
@section scripts {
@Scripts.Render("~/Scripts/angular.js")
@Scripts.Render("~/Scripts/app/quiz-controller.js")
}
Note: The AngularJS template is a declarative specification that uses information from the model and the controller to transform static markup into the dynamic view that the user sees in the browser. The following are examples of AngularJS elements and element attributes that can be used in a template:
- The ng-app directive tells AngularJS the DOM element that represents the root element of the application.
- The ng-controller directive attaches a controller to the DOM at the point where the directive is declared.
- The curly brace notation {{ }} denotes bindings to the scope properties defined in the controller.
- The ng-click directive is used to invoke the functions defined in the scope in response to user clicks.
.validation-summary-valid {
display: none;
}
/* Geek Quiz styles */
.flip-container .back,
.flip-container .front {
border: 5px solid #00bcf2;
padding-bottom: 30px;
padding-top: 30px;
}
#content {
position:relative;
background:#fff;
padding:50px 0 0 0;
}
.option {
width:140px;
margin: 5px;
}
div.correct p {
color: green;
}
div.incorrect p {
color: red;
}
.btn {
border-radius: 0;
}
.flip-container div.front, .flip-container div.back.flip {
display: block;
}
.flip-container div.front.flip, .flip-container div.back {
display: none;
}
Note: If you are using the solution from the previous exercise, you can log in with the user account you created before.
/* flip transformation */
.flip-container div.front {
-moz-transform: perspective(2000px) rotateY(0deg);
-webkit-transform: perspective(2000px) rotateY(0deg);
-o-transform: perspective(2000px) rotateY(0deg);
transform: perspective(2000px) rotateY(0deg);
}
.flip-container div.front.flip {
-moz-transform: perspective(2000px) rotateY(179.9deg);
-webkit-transform: perspective(2000px) rotateY(179.9deg);
-o-transform: perspective(2000px) rotateY(179.9deg);
transform: perspective(2000px) rotateY(179.9deg);
}
.flip-container div.back {
-moz-transform: perspective(2000px) rotateY(-180deg);
-webkit-transform: perspective(2000px) rotateY(-180deg);
-o-transform: perspective(2000px) rotateY(-180deg);
transform: perspective(2000px) rotateY(-180deg);
}
.flip-container div.back.flip {
-moz-transform: perspective(2000px) rotateY(0deg);
-webkit-transform: perspective(2000px) rotateY(0deg);
-ms-transform: perspective(2000px) rotateY(0);
-o-transform: perspective(2000px) rotateY(0);
transform: perspective(2000px) rotateY(0);
}
/* hide back of pane during flip */
.front, .back {
-moz-backface-visibility: hidden;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/bootstrap.css",
"~/Content/site.css",
"~/Content/Flip.css"));
http://weblogs.asp.net/dwahlin/using-an-angularjs-factory-to-interact-with-a-restful-service
http://forums.asp.net/t/369778.aspx?How+to+set+a+DataColumn+s+DataType+
Customer.Columns.Add(New DataColumn("CustomerCatagoryID", GetType(Integer)))