When I call the showLoading method of a chart the show loading screen appears but on the wrong position. I am not sure which is causing this. I have created a plunk (http://plnkr.co/edit/FdBqoPJZmopNc4s33Jcg) but I could not reproduce the wrong behaviour with the plunk.
$scope.renderColumnChart = function (date) {
var chart = {};
$.each(Highcharts.charts, function (index, c) {
if (c && c.renderTo.id === "column-chart")
chart = c;
});
$scope.columnChartConfig.series = [];
$scope.columnChartConfig.title.text = moment(date).format("DD.MM.YYYY");
$scope.columnChartUrl = SharedService.createUrl(SharedService.createUrlDataByPeriod($scope.dimension, SharedService.getPeriodFromDate($scope.dimension, date), date, 0, 0, 1, [19]));
chart.showLoading(); //TODO show loading wrong place
$q.all([
ColumnChartService2.getData($scope.columnChartUrl, globalConfig.oDataServer + globalConfig.ageGroups)
]).then(function (result) {
$scope.columnChartConfig.series.push({
name: "Personen pro Altersgruppe",
color: Highcharts.getOptions().colors[0],
data: result[0].data
});
//chart.hideLoading();
});
}
regards
Additional Information
Here is the full code of the angular controller and the html template.
Controller
ManagementReporting.controller("CustomerHeadCountController", ['$rootScope', '$scope', '$q', 'globalConfig', 'SharedService', 'MultiLineChartService', 'ColumnChartService2',
function ($rootScope, $scope, $q, globalConfig, SharedService, MultiLineChartService, ColumnChartService2) {
SharedService.cleanUpCharts();
$scope.title = 'Customer Head Count';
var init = function () {
$scope.dimension = SharedService.getGranularity()[0];
var ranges = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
$scope.before = ranges[3];
$scope.after = ranges[5];
$scope.referenceDate = new Date(moment().subtract("month", 0).format("YYYY-MM-DD"));
$scope.period = SharedService.getPeriods($scope.dimension, $scope.referenceDate).period;
};
init();
// TODO add charts
$scope.refresh = function () {
$scope.processCharts();
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////// EVENTS ////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// when route change!!!!
$scope.$on('$routeChangeSuccess', function () {
$rootScope.$broadcast('change.activeSite', 'Anzahl Kunden auf basis verrechneter Einsätze (jeder Kunde in der Dimension einmalig gezählt)');
$scope.processCharts();
});
$scope.processCharts = function () {
$("#modal-processing-report-data").modal("show");
$scope.lineChartConfig = MultiLineChartService.getConfig();
$scope.lineChartConfig.series = [];
$scope.url = SharedService.createUrl(SharedService.createUrlDataByPeriod($scope.dimension, $scope.period.value, $scope.date, $scope.before, $scope.after, 1, [17]));
$scope.urlRegression = SharedService.createUrl(SharedService.createUrlDataByPeriod($scope.dimension, $scope.period.value, $scope.date, $scope.before, $scope.after, 1, [21]));
$scope.columnChartConfig = ColumnChartService2.getConfig();
$scope.columnChartConfig.series = [];
$q.all([
MultiLineChartService.getData($scope.url),
MultiLineChartService.getRegressionData($scope.urlRegression)
]).then(function(result) {
$scope.lineChartConfig.series = result[0].data;
$scope.lineChartConfig.series.push(result[1].data);
$scope.renderColumnChart($scope.date);
var normal = Enumerable.from(result[0].data).where(function(record) {
return record.name === "Normal";
}).toArray()[0];
$scope.average = parseFloat(normal.avg.toFixed(2));
$scope.minimum = normal.min;
$scope.maximum = normal.max;
$("#modal-processing-report-data").modal("hide");
});
}
$scope.$on('multilinechart.click', function (angularEvent, highchartEvent, point) {
var date = new Date(highchartEvent.point.x);
$scope.renderColumnChart(date);
});
$scope.renderColumnChart = function (date) {
var chart = {};
$.each(Highcharts.charts, function (index, c) {
if (c && c.renderTo.id === "column-chart")
chart = c;
});
$scope.columnChartConfig.series = [];
$scope.columnChartConfig.title.text = moment(date).format("DD.MM.YYYY");
$scope.columnChartUrl = SharedService.createUrl(SharedService.createUrlDataByPeriod($scope.dimension, SharedService.getPeriodFromDate($scope.dimension, date), date, 0, 0, 1, [19]));
//chart.showLoading(); //TODO show loading wrong place
$q.all([
ColumnChartService2.getData($scope.columnChartUrl, globalConfig.oDataServer + globalConfig.ageGroups)
]).then(function (result) {
$scope.columnChartConfig.series.push({
name: "Personen pro Altersgruppe",
color: Highcharts.getOptions().colors[0],
data: result[0].data
});
//chart.hideLoading();
});
}
}]);
HTML Template
<div>
<!--
<div class="row">
<div data-ng-include="selectionHeader.url"></div>
</div>
-->
<div class="dateselector"
data-dimension="dimension"
data-before="before"
data-date="date"
data-period="period"
data-after="after"
data-refresh="refresh()">
</div>
<div class="well">
<highchart id="line-chart"
data-config="lineChartConfig"
data-chart="lineChart" />
<div class="text-center">
<span translate>Minimum: </span> {{minimum}}, <span translate>Maximum: </span> {{maximum}}, <span translate>Durchschnitt: </span> {{average}}
</div>
</div>
<div class="well">
<highchart id="column-chart"
data-config="columnChartConfig"
data-chart="columnChart" />
</div>
<!-- Modal Section -->
<div class="modal fade" id="modal-processing-report-data" tabindex="-1" role="dialog" aria-labelledby="modal-processing-report-data-label" aria-hidden="true" data-keyboard="false">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="modal-processing-report-data-label">Processing...</h4>
</div>
<div class="modal-body">
<div class="progress progress-striped active">
<div class="progress-bar" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"></div>
</div>
</div>
</div>
</div>
</div>
</div>
Related
I have a list of cities in a form with check box. when trying cancel form , the list of checkboxes checked are not clearing . but when i uncheck each of the checkbox, it works
api-model.ts
export interface City{
city_Name: string;
}
class-model.ts
export class eModel {
cityList: Array<City>=[];
}
app.html
<div class="row">
<div class="column-3">
<div *ngFor="let data of cityData; let i=index">
<mat-checkbox color="primary" (change)="getCheckboxValues(data, i, $event)" >
{{data.city_Name}}
</mat-checkbox>
</div>
</div>
</div>
<button mat-raised-button class="cancel-button" (click)="Cancel()">Cancel</button>
app.ts
cityData: City[] = [];
ngOnInit(): void {
this.eModel.cityList = [];
loadCityList();
}
loadCityList() {
return this._getAPIservice.getCity().subscribe(data => {
this.cityData = data;
});
}
Cancel(): void {
this.eForm.resetForm({});
loadCityList();
this.eModel.cityList = [];
}
This can be achieved by using element reference variable.
Try this:
<div class="row">
<div class="column-3">
<div *ngFor="let data of cityData; let i=index">
<mat-checkbox #chkboxes color="primary" (change)="getCheckboxValues(data, i, $event)" >
{{data.city_Name}}
</mat-checkbox>
</div>
</div>
</div>
<button mat-raised-button class="cancel-button" (click)="Cancel()">Cancel</button>
In ts:
#ViewChildren('chkboxes') chkboxes: QueryList<any>; // Define this at the top
Cancel(){
this.chkboxes.forEach(x => x.checked = false);
}
I'm going through this learning lab for AngularJS. I can get the example to work as-is (with a minor bug fix).
I'm also trying to learn some good coding practices concerning AngluarJS by referencing this guide. This has resulted in a js file that looks like this:
(function () {
'use strict';
angular.module('QuizApp', []);
angular.module('QuizApp').controller('QuizCtrl', QuizController);
function QuizController($http) {
var vm = this;
vm.answer = answer();
vm.answered = false;
vm.correctAnswer = false;
vm.nextQuestion = nextQuestion();
vm.options = [];
vm.sendAnswer = sendAnswer(option);
vm.title = "loading question...";
vm.working = false;
function answer() {
return vm.correctAnswer ? 'correct' : 'incorrect';
}
function nextQuestion() {
vm.working = true;
vm.answered = false;
vm.title = "loading question...";
vm.options = [];
$http.get("/api/trivia")
.success(function (data, status, headers, config) {
vm.options = data.options;
vm.title = data.title;
vm.answered = false;
vm.working = false;
}).error(function (data, status, headers, config) {
vm.title = "Oops... something went wrong.";
vm.working = false;
});
}
function sendAnswer(option) {
vm.working = true;
vm.answered = true;
$http.post('/api/trivia', { 'questionId': option.questionId, 'optionId': option.id })
.success(function (data, status, headers, config) {
vm.correctAnswer = (data === true);
vm.working = false;
})
.error(function (data, status, headers, config) {
vm.title = "Oops... something went wrong.";
vm.working = false;
});
}
};
})();
However, this code is throwing the following error when the page loads.
ReferenceError: 'option' is undefined
at QuizController (http://localhost:17640/Scripts/app/quiz-controller.js:16:9)
at invoke (http://localhost:17640/Scripts/angular.js:4473:7)
at Anonymous function (http://localhost:17640/Scripts/angular.js:9093:11)
at nodeLinkFn (http://localhost:17640/Scripts/angular.js:8205:13)
at compositeLinkFn (http://localhost:17640/Scripts/angular.js:7637:13)
at compositeLinkFn (http://localhost:17640/Scripts/angular.js:7641:13)
at compositeLinkFn (http://localhost:17640/Scripts/angular.js:7641:13)
at compositeLinkFn (http://localhost:17640/Scripts/angular.js:7641:13)
at compositeLinkFn (http://localhost:17640/Scripts/angular.js:7641:13)
at publicLinkFn (http://localhost:17640/Scripts/angular.js:7512:30)
For whatever reason, it appears to be attempting to execute sendAnswer immediately. Since it's failing, the javascript halts mid-way displaying the angular property name {{title}} on the page instead of rendering the question on the page. I've tried different ways of defining and calling vm.sendAnswer and function sendAnswer, without any luck.
For reference, here is the code to my view (note the ng-repeat on options - this view code works perfectly when following the lab exercise exactly):
<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>
How can I preserve my coding practice (essentially, avoid using $scope in this situation and declare all of my viewmodel properties at the top of the controller) yet get the function to operate properly?
Are you planning to send in
vm.options = [];
vm.sendAnswer = sendAnswer(option);
vm.options into the sendAnswer() function because option is not defined. you won't get an error if you enclose 'option' in a string and pass it into the function.But then you wouldn't be getting the object you want.
add this somewhere at the top var option = {}.
I have this example working now.
For starters, the controller properties that are plugged into functions just need to reference the names of the functions.
vm.answer = answer;
vm.nextQuestion = nextQuestion;
vm.sendAnswer = sendAnswer;
Then we need an alias for our controller on our view so we can reference it more easily.
<div class="flip-container text-center col-md-12" ng-controller="QuizCtrl as quiz" ng-init="quiz.nextQuestion()">
<div class="back" ng-class="{flip: quiz.answered, correct: quiz.correctAnswer, incorrect:!quiz.correctAnswer}">
<p class="lead">{{quiz.answer()}}</p>
<p>
<button class="btn btn-info btn-lg next option" ng-click="quiz.nextQuestion()" ng-disabled="quiz.working">Next Question</button>
</p>
</div>
<div class="front" ng-class="{flip: quiz.answered}">
<p class="lead">{{quiz.title}}</p>
<div class="row text-center">
<button class="btn btn-info btn-lg option" ng-repeat="option in quiz.options" ng-click="quiz.sendAnswer(option)" ng-disabled="quiz.working">{{option.title}}</button>
</div>
</div>
</div>
And that's pretty much it. Just clean up the property definitions in the angular controller and then utilize an alias for the controller on the view. Everything else is pretty much the same code as what's in the question.
Here's the complete code now. Angular controller:
(function () {
'use strict';
angular.module('QuizApp', []);
angular.module('QuizApp').controller('QuizCtrl', QuizController);
function QuizController($http) {
var vm = this;
vm.answer = answer;
vm.answered = false;
vm.correctAnswer = false;
vm.nextQuestion = nextQuestion;
vm.options = [];
vm.sendAnswer = sendAnswer;
vm.title = "loading question...";
vm.working = false;
function answer() {
return vm.correctAnswer ? 'correct' : 'incorrect';
};
function nextQuestion() {
vm.working = true;
vm.answered = false;
vm.title = "loading question...";
vm.options = [];
$http.get("/api/trivia")
.success(function (data, status, headers, config) {
var answerOptions = data.options;
while (answerOptions.length > 0){
var random = Math.floor(Math.random() * answerOptions.length, 0);
alert(random);
vm.options.push(answerOptions[random]);
answerOptions.splice(random, 1);
}
//vm.options = data.options;
vm.title = data.title;
vm.answered = false;
vm.working = false;
}).error(function (data, status, headers, config) {
vm.title = "Oops... something went wrong.";
vm.working = false;
});
};
function sendAnswer(option)
{
vm.working = true;
vm.answered = true;
$http.post('/api/trivia', { 'questionId': option.questionId, 'optionId': option.id }).success(function (data, status, headers, config) {
vm.correctAnswer = (data === true);
}).error(function (data, status, headers, config) {
vm.title = "Oops... something went wrong";
});
vm.working = false;
}
};
})();
MVC Index view:
#{
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 as quiz" ng-init="quiz.nextQuestion()">
<div class="back" ng-class="{flip: quiz.answered, correct: quiz.correctAnswer, incorrect:!quiz.correctAnswer}">
<p class="lead">{{quiz.answer()}}</p>
<p>
<button class="btn btn-info btn-lg next option" ng-click="quiz.nextQuestion()" ng-disabled="quiz.working">Next Question</button>
</p>
</div>
<div class="front" ng-class="{flip: quiz.answered}">
<p class="lead">{{quiz.title}}</p>
<div class="row text-center">
<button class="btn btn-info btn-lg option" ng-repeat="option in quiz.options" ng-click="quiz.sendAnswer(option)" ng-disabled="quiz.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")
}
Application Description:
I am making a simple Ecommerce website(single page product listing) using AngularJS and Rails. It only handles Cash On Delivery Orders. The user adds products and checksout. All this process is done in Angular. The cart is stored in localstorage.When he checksout a modal pops up asking him to choose choose between two shipping methods. Depending on the shipping method he chooses the price which is displayed on the bootstrap modal has to be updated.
Problem Description:
The page flickers(the curly braces appear) when I try to do this. When I reload the whole thing it works properly.
After some research I found that I have to use $compile but I am not sure of how to use it. i read several tutorials but I am not able to figure it out.
Here is my angular code. The two functions I used in bootstrap modal are shippingCharges(), totalPrice(). They are at the end of the angular code.
<script>
var products = angular.module('products', []);
products.controller('ListController', ['$scope', function($scope) {
$scope.categories = JSON.parse('<%= j raw(#categories_hash.to_json) %>');
$scope.activeCategory = null;
$scope.cart = JSON.parse(localStorage.getItem('cart'));
if (!!$scope.cart) {
angular.forEach($scope.cart, function(item_quantity, item_id) {
$scope.categories.forEach(function(category, index1) {
category.products.forEach(function(product, index2) {
if (item_id == product.id) {
product.ordered_quantity = item_quantity;
}
});
});
});
};
$scope.formData = {
shipping: "scheduled"
};
$scope.addProductToCart = function(product_id) {
// event.preventDefault();
var cart = $scope.cart;
if (!cart) {
cart = {}
}
if (!cart[product_id]) {
cart[product_id] = 0;
}
cart[product_id] += 1;
localStorage.setItem('cart', JSON.stringify(cart));
$scope.cart = cart;
};
$scope.increaseQuantity = function(product) {
product.ordered_quantity += 1;
$scope.addProductToCart(product.id);
};
$scope.decreaseQuantity = function(product) {
product.ordered_quantity = product.ordered_quantity - 1;
var cart = $scope.cart;
if (!cart) {
cart = {}
}
cart[product.id] -= 1;
localStorage.setItem('cart', JSON.stringify(cart));
$scope.cart = cart;
};
$scope.removeProductFromCart = function(product_id) {
var cart = $scope.cart;
cart[product_id] = 0;
localStorage.setItem('cart', JSON.stringify(cart));
$scope.cart = cart;
}
$scope.totalPrice = function() {
total = 0;
$scope.categories.forEach(function(category, index) {
category.products.forEach(function(product, index1) {
total += product.price*product.ordered_quantity;
});
});
return total;
};
$scope.toggleCategory = function(category) {
if ($scope.activeCategory == category.category_id) {
$scope.activeCategory = null;
} else {
$scope.activeCategory = category.category_id;
}
};
$scope.shouldShowCategory = function(category) {
return($scope.activeCategory == category.category_id);
};
$scope.shippingCharges = function() {
var cart = $scope.cart;
var shippingcost;
if ($scope.formData.shipping == "scheduled"){
shippingcost = 35;
}else if ($scope.formData.shipping == "unscheduled"){
shippingcost = 75;
}
cart["shipping"]=shippingcost;
localStorage.setItem('cart', JSON.stringify(cart));
return shippingcost;
}
}]);
</script>
Boostrap Modal Code
<div class="modal fade" id="checkoutModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" ng-controller="ListController" ng-cloak >
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">Information for delivery</h4>
</div>
<div class="modal-body checkout-details">
<form id="checkoutForm" class="form-horizontal">
<div id="checkoutLoading" class="progress progress-striped active hidden">
<div class="progress-bar progress-bar-success" style="width: 100%"></div>
</div>
<fieldset>
<legend>Choose a delivery method</legend>
<p>We are making a schedule delivery to <strong><%= delivery_timing[0] %></strong> on <strong><%= delivery_timing[1] %></strong>. If you are not located in the mentioned places please choose an unscheduled delivery</p>
<div class="radio">
<label><input type="radio" name="shipping" value="scheduled" ng-model="formData.shipping" ng-change="setShipping('scheduled')">Scheduled Delivery(Rs. 35)</label>
</div>
<div class="radio">
<label><input type="radio" name="shipping" value="unscheduled" ng-model="formData.shipping" ng-change="setShipping('unscheduled')">Unscheduled Delivery(Rs.75)</label>
</div>
<p class="ng-cloak">Total: {{shippingCharges() + totalPrice()}}</p>
</fieldset>
<fieldset>
<legend>Please provide delivery details:</legend>
<div class="errorMessage alert alert-dismissible alert-danger hidden">
<strong>Oh snap!</strong> Please provide phone number and address.
</div>
<div id="checkoutEmailFormGroup" class="form-group">
<label for="checkoutPhone">Email</label>
<input type="email" class="form-control" id="checkoutEmail" placeholder="me#example.com" >
</div>
<div id="checkoutPhoneFormGroup" class="form-group">
<label for="checkoutPhone">Phone</label>
<input type="phone" class="form-control" id="checkoutPhone" placeholder="+91-9999-999-999" >
</div>
<div id="checkoutAddressFormGroup" class="form-group">
<label for="checkoutAddress">Address</label>
<textarea class="form-control" id="checkoutAddress" placeholder="Plot No
Street Name
City" rows="5"></textarea>
</div>
</fieldset>
</form>
</div>
<div class="modal-footer">
<p class="ng-cloak" >Total cost: {{shippingCharges() + totalPrice()}}</p>
<button type="button" class="btn btn-default" data-dismiss="modal">Go Back</button>
<button id="checkout_trigger" type="button" class="btn btn-brown">Confirm</button>
</div>
</div>
</div>
</div>
Can you please let me know how to compile the code in the Bootstrap modal?
I am developing an asp.net MVC4 web app where I need to show StateList and CountryList. I am having a dropdownlist for Countries and on changing i am getting a partial view to display the corresponding States through an ajax request. The partial view has the dropdownlist for States. But once rendered, the dropdownlist for States is not expanding. Here is the code.
First View:
<div class="row" style="margin-left: 12%">
<div class="col-md-12 control-group">
<label class="col-md-4 control-label" style="margin-top :1%;"><b>Country:</b></label>
<div class="col-md-4" style="">
#Html.DropDownListFor(Function(model) model.SelectedCountryId, Model.CountryList, New With {.style = "width:100px", .type = "text", .id = "country"})
#Html.ValidationMessageFor(Function(model) model.SelectedCountryId)
</div>
</div>
</div>
<div id="stateDiv"></div>
Script:
$(document).ready(function () {
var rootUrl = $('#rootUrl').val();
$('#country').change(function () {
var countryCode = $(this).val();
$.get(rootUrl + 'GetStateList', { countryCodeId: countryCode }, function (data) {
$('#stateDiv').html(data);
}, 'html');
var isUSCAN = false;
if ($(this).val() == 1 || $(this).val() == 2) {
isUSCAN = true;
}
$('#stateSelect').toggle(isUSCAN);
$('#stateText').toggle(!isUSCAN);
var isCAN = $(this).val() == 2;
$('#provinceLabel').toggle(isCAN);
$('#stateLabel').toggle(!isCAN);
}).change();
});
Controller:
Function GetStateList(countryCodeId As Integer) As ActionResult
Return PartialView("PartialStateList", Defendant)
End Function
PartialStateList View
<div class="row" style="margin-left: 12%">
<div class="col-md-12 control-group">
<label id="stateLabel" class="col-md-4 control-label" style="margin-top :1%;"><b>State:</b></label>
<label id="provinceLabel" class="col-md-4 control-label" style="margin-top :1%;"><b>Province:</b></label>
<div class="col-md-4" style="">
#Html.DropDownListFor(Function(model) model.SelectedStateId, Model.StateList, New With {.style = "width:100px", .type = "text", .id = "stateSelect"})
#Html.TextBoxFor(Function(model) model.SelectedStateId, New With {.style = "width:100px", .value = "", .type = "text", .id = "stateText"})
#Html.ValidationMessageFor(Function(model) model.SelectedStateId)
</div>
</div>
</div>
The html that is rendered when i check in browser has the entire list of states, but when i click on the dropdownlist, it is not expanding and showing the list. Please let me know if there is something I am missing.
I am building a SPA per the guidance provided in John Papa's Jumpstart.
When I create the model, it has
modelObservable().entityAspect.entityState.isAdded() = true;
I update the text, dropdown and
modelObservable().entityAspect.entityState.isAdded() = false;
in my Datacontext:
var createProject = function (position) {
return manager.createEntity(entityNames.project,
{
positionId : position.id(),
start : position.start(),
memberId : position.memberId()
});
};
which is called from my add viewModel:
define(['services/datacontext', 'durandal/plugins/router', 'durandal/system', 'durandal/app', 'services/logger', 'services/uiService'],
function (datacontext, router, system, app, logger, ui) {
var model = ko.observable();
var position = ko.observable();
var hourTypes = ko.observableArray([]);
var isSaving = ko.observable(false);
// init
var activate = function (routeData) {
logger.log('Add View Activated', null, 'add', true);
var positionId = parseInt(routeData.id);
initLookups();
return datacontext.getPositionById(positionId, position).then(**createProject**);
};
var initLookups = function () {
logger.log('initLookups', null, 'add', true);
hourTypes(datacontext.lookups.hourTypes);
};
// state
**var createProject = function () {
return model(datacontext.createProject(position()));
}**
var addNewProject = function () {
if (position == undefined || position().id() < 1) {
console.log('callback addNewProject');
setTimeout(function () {
addNewProject();
}, 1000);
} else {
datacontext.addProject(position(), model);
console.log(model().id());
return;
}
}
var **save** = function () {
isSaving(true);
**datacontext.saveChanges()**
.then(goToEditView).fin(complete);
function complete() {
isSaving(false);
}
function goToEditView() {
isSaving(false);
var url = '#/Projects/';
router.navigateTo(url + model().id());
}
};
var vm = {
activate: activate,
hourTypes: hourTypes,
isAdded: isAdded,
model: model,
save: save,
title: 'Details View'
};
return vm;
});
the html
<section data-bind="with:model">
<h1 data-bind="text: name"> <i class="icon-asterisk" data-bind="visible: hasChanges" style="font-size: 30px;"></i></h1>
<div class="errorPanel"></div>
<div id="overview" class="project" >
<div class="row">
<div class="span4">
<label class="requiredLabel">Name*</label>
<input type="text" name="name" data-bind="value: name" style="width: 27em;" class="required" placeholder="Project Name" required validationMessage="Project Name required" /><span class="k-invalid-msg" data-for="title"></span>
</div>
</div>
<div class="row">
<div class="span3"><label class="requiredLabel">Start*</label></div>
<div class="span3"><label class="requiredLabel">End</label></div>
</div>
<div class="row">
<div class="span3"><input name="start" data-bind="shortDate: start" class="date required" required="required" placeholder="mm/dd/yyyy" style=" width:142px"></div>
<div class="span3"><input name="end" data-bind="shortDate: end" class="date" placeholder="mm/dd/yyyy" style=" width:142px"><span class="k-invalid-msg" data-for="end"></span></div>
</div>
<br/>
<div class="row">
<div class="span3"><label for="hourType" class="requiredLabel">Measure As*</label></div>
<div class="span2"><label for="hoursPerWeek" class="requiredLabel">Hours/Week</label></div>
<div class="span2"><label for="totalHours" class="requiredLabel">Total Hours</label></div>
</div>
<div class="row">
<div class="span3">
<select id="hourType" data-bind="options: $parent.hourTypes, optionsText: 'name', value: hourType" required validationMessage="Measure As required"></select><span class="k-invalid-msg" data-for="hourType"></span>
</div>
<div class="span2">
<input name="hoursPerWeek" type="number" min="1" max="120" required="required" data-bind="value: hoursPerWeek, validationOptions: { errorElementClass: 'input-validation-error' }, enable: hourType().id() == 1" class="hours required"" style="width: 80px;" validationMessage="Hours required"><span class="k-invalid-msg" data-for="projectHours"></span>
<span class="k-invalid-msg" data-for="totalHours"></span>
</div>
<div class="span2">
<input name="totalHours" type="number" min="40" max="2080" required="required" data-bind="value: totalHours, validationOptions: { errorElementClass: 'input-validation-error' }, enable: hourType().id() == 2" class="hours required"" style="width: 80px;" validationMessage="Hours required"><span class="k-invalid-msg" data-for="projectHours"></span>
<span class="k-invalid-msg" data-for="totalHours"></span>
</div>
</div>
<div class="row">
<div class="span4">
<label class="requiredLabel">Description*</label><span class="k-invalid-msg" data-for="description"></span><span id="posMinDesc" style="visibility:hidden"></span>
<textarea id="description" name="description" style="height: 200px; width: 650px;" data-bind="value: description, enabled:true, click: $parent.clearDefaults" rows="4" cols="60" class="richTextEditor k-textbox" required validationMessage="Description required" ></textarea>
</div>
</div>
</div>
<div class="button-bar">
<button class="btn btn-info" data-bind="click: $parent.goBack"><i class="icon-hand-left"></i> Back</button>
<button class="btn btn-info" data-bind="click: $parent.save, enable: $parent.canSave"><i class="icon-save"></i> Save</button>
</div>
</section>
The json breeze sends to my controller is this:
{
"entities": [
{
"Id": -1,
"Description": "poi",
"End": null,
"Gauge": 0,
"Score": 0,
"HourTypeId": 1,
"HoursPerWeek": 45,
"HourlyRate": null,
"TotalHours": null,
"WeightedHours": 0,
"CreditMinutes": 0,
"TotalCompensation": null,
"IsCurrent": false,
"Name": "poi",
"PositionId": 1,
"MemberId": 1,
"Start": "2011-09-01T00:00:00Z",
"undefined": false,
"entityAspect": {
"entityTypeName": "Project:#SkillTraxx.Model",
"defaultResourceName": "Projects",
"entityState": "Modified",
"originalValuesMap": {
"Name": "",
"HourTypeId": 0,
"HoursPerWeek": null,
"Description": ""
},
"autoGeneratedKey": {
"propertyName": "Id",
"autoGeneratedKeyType": "Identity"
}
}
}
],
"saveOptions": {}
}
As you can see, the above is incorrect b/c state is "Modified" and the Id = -1. This throws an error server side. I suppose I could trap the DbUpdateConcurrencyException, unwind the JObject and change "Modified" to added, but that's got code smell all over it.
If anyone can help me find the face-palm moment in all of this, I'm ready.
Thanks for looking!
FACE PALMED IT
I took Jays advice and started stripping away the html then I realize it was my handler.
The update method on shortDate handler was responsible. I wrapped it in an if statement not to send the update if the current state is added.
ko.bindingHandlers.shortDate = {
init: function (element, valueAccessor) {
//attach an event handler to our dom element to handle user input
element.onchange = function () {
var value = valueAccessor();//get our observable
//set our observable to the parsed date from the input
value(moment(element.value).toDate());
};
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var value = valueAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(value);
if (valueUnwrapped) {
element.value = moment(valueUnwrapped).format('L');
if (!viewModel.entityAspect.entityState.isAdded())
{
**viewModel.entityAspect.setModified();**
}
}
}
};