Written on 8/28/2014 in Web development
After this, there wasn't anything too complicated or frustating1 left to do.
Nancy modules and views
I set up 2 nancy modules, an API to communicate JSON and a regular module for routing the views:
public class ApiModule : NancyModule
{
// Repository does nothing fancy, uses azure storage api for .net to get and post 'veryPairs'
private readonly ThePoetRepository _repo;
public ApiModule()
{
_repo = new ThePoetRepository();
Get["/api/very-pair/list/{input}"] = _ => { return GetPossibleVeryPairs(_.input); };
Get["/api/very-pair/single/{veryWord}"] = _ => { return GetVeryPair(_.veryWord); };
Post["/api/very-pair"] = _ => { return PostVeryPair(); };
}
private dynamic GetVeryPair(string veryWord)
{
return Negotiate
.WithStatusCode(HttpStatusCode.OK)
.WithModel(_repo.GetVeryPair(veryWord));
}
private dynamic GetPossibleVeryPairs(string input)
{
return Negotiate
.WithStatusCode(HttpStatusCode.OK)
.WithModel(_repo.GetVeryPairsByInput(input));
}
private dynamic PostVeryPair()
{
var veryPair = new VeryPairViewModel();
this.BindTo(veryPair);
_repo.SubmitSuggestion(veryPair.VeryWord, veryPair.BetterWord);
return Negotiate
.WithStatusCode(HttpStatusCode.OK);
}
}
public class GetModule : NancyModule
{
public GetModule()
{
Get["/"] = _ => { return View["index.cshtml"]; };
Get["/about"] = _ => { return View["about.cshtml"]; };
Get["/suggest"] = _ => { return View["suggest.cshtml"]; };
}
}
And the views:
index.cshtml:
@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase
@{
Layout = "_Layout.cshtml";
ViewBag.Title = "The poet";
}
<div ng-controller="indexController">
<h1>The poet</h1>
<p>Avoid saying:</p>
<p id="very-text">Very <autocomplete ng-model="veryWordInput" data="veryWords" on-type="updateVeryWords" on-select="getBetterWord" attr-placeholder="tired"></autocomplete></p>
<p>Rather say:</p>
<p id="better-text">{{betterWord}}</p>
</div>
suggest.cshtml:
@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase
@{
Layout = "_Layout.cshtml";
ViewBag.Title = "The poet - Suggest a combination";
}
<div ng-controller="suggestController">
<h1>The poet</h1>
<h2>Suggest a combination</h2>
<p>Avoid saying:</p>
<p id="very-text">Very <input type="text" placeholder="tired" ng-model="veryWordInput" /></p>
<p>Rather say:</p>
<p id="better-text"><input type="text" placeholder="Exhausted" ng-model="betterWordInput" /></p>
<p><button ng-click="addSuggestion()">Submit</button></p>
</div>
AngularJS controllers and service
Again, nothing complicated here, I have 2 controllers for the main page and the suggestion page, a service for making HTTP calls and a directive for autocompleting a text field.
Controllers:
indexController:
ThePoet.controller('indexController', ['$scope', 'AppHttpService', function ($scope, AppHttpService) {
$scope.veryWordInput;
$scope.veryWords = [];
$scope.betterWord = "exhausted";
$scope.updateVeryWords = function (tempInput) {
if (tempInput && tempInput.length > 0) {
AppHttpService.get("/api/very-pair/list/" + tempInput)
.success(function (data) {
while ($scope.veryWords.length > 0) {
$scope.veryWords.pop();
}
for (var i = 0; i < data.length; i++) {
$scope.veryWords.push(data[i].veryWord);
}
for (var i = 0; i < data.length; i++) {
if (data[i].veryWord === tempInput) {
$scope.getBetterWord(tempInput);
}
}
});
}
};
$scope.getBetterWord = function (tempInput) {
AppHttpService.get("/api/very-pair/single/" + tempInput)
.success(function (data) {
$scope.betterWord = data.betterWord;
});
};
}]);
suggestController:
ThePoet.controller('suggestController', ['$scope', 'AppHttpService', function ($scope, AppHttpService) {
$scope.veryWordInput;
$scope.betterWordInput;
$scope.feedbackType;
$scope.feedbackMessage;
$scope.addSuggestion = function () {
if ($scope.veryWordInput && $scope.betterWordInput && $scope.veryWordInput.length > 3 && $scope.betterWordInput.length > 3) {
AppHttpService.post("/api/very-pair", { veryWord: $scope.veryWordInput, betterWord: $scope.betterWordInput })
.success(function () {
$scope.feedbackType = "success";
$scope.feedbackMessage = "Your suggestion was added!";
$scope.veryWordInput = "";
$scope.betterWordInput = "";
}).error(function () {
$scope.feedbackType = "error";
$scope.feedbackMessage = "Snap, Something went wrong! You could try again, but I can't promise it will work.. :(";
});
} else {
$scope.feedbackType = "error";
$scope.feedbackMessage = "The suggestion was not added because it is too short";
}
};
}]);
Service:
ThePoet.service('AppHttpService', ['$http', function ($http) {
this.basePath = "/ThePoet"
this.loading = { counter: 0 };
this.get = function (url) {
var loading = this.loading;
loading.counter++;
var promise = $http.get(this.basePath + url, {
contentType: 'application/json; charset=utf-8',
dataType: 'json'
});
promise
.success(function () {
loading.counter--;
})
.error(function () {
loading.counter--;
});
return promise;
};
this.post = function (url, data) {
var loading = this.loading;
loading.counter++;
var promise = $http.post(this.basePath + url, data, {
contentType: 'application/json; charset=utf-8',
dataType: 'json'
});
promise
.success(function () {
loading.counter--;
})
.error(function () {
loading.counter--;
});
return promise;
};
}]);
Autocomplete: Autocomplete
Deploying to an existing azure website
I deployed the website on the existing website for my blog. So I created a virtual directory using the project properties. I changed the url's to reflect the correct path to '/ThePoet/...' and added the configuration for SquishIt. Then make sure you add a virtual directory to the azure website as described here. In the publish profile, you have to change the following: