Scope variable not updating in $scope.$watch - javascript

I have a watcher on a $scope variable which sets off a function to change the $scope variable to the beginning of the week of the date chosen. The function is returning the correct date but when I try to set the $scope variable to that date the view does not update. But checking it inside the inspector the $scope variable is changed but the view never updates. Here is the $watch function:
angular.module('hansenApp', ['ngAnimate', 'ui.bootstrap']);
angular.module('hansenApp').controller('DatepickerCtrl', function ($scope) {
$scope.startDate = new Date();
$scope.endDate = new Date();
$scope.status = {
startOpened: false,
endOpened: false,
};
$scope.openStart = function ($event) {
$scope.status.startOpened = true;
};
$scope.openEnd = function ($event) {
$scope.status.endOpened = true;
};
$scope.$watch('startDate', function (dateVal) {
var oldDate = new Date(dateVal);
var tempDate = getStartDateOfWeek(dateVal);
if (oldDate.getTime() != tempDate.getTime()) {
$scope.startDate = tempDate;
}
});
$scope.$watch('endDate', function (dateVal) {
var oldDate = new Date(dateVal);
var tempDate = getEndDateOfWeek(dateVal);
if ($scope.endDate.getTime() != tempDate.getTime()) {
$scope.endDate = tempDate;
}
});
function getStartDateOfWeek(date) {
var ISOweekStart = date;
ISOweekStart.setDate(date.getDate() - date.getDay());
return ISOweekStart;
}
function getEndDateOfWeek(date) {
var ISOweekEnd = date;
ISOweekEnd.setDate(date.getDate() + (6 - date.getDay()));
return ISOweekEnd;
}
});
Edit 1: Here's the view:
<p class="input-group">
<input type="text" id="endDate" class="form-control" uib-datepicker-popup="MM/dd/yyyy" ng-model="endDate" is-open="status.endOpened" close-text="Close" placeholder="End Date:" runat="server" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="openEnd($event)"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>

You do not change the reference of the date, but just the value of it.
A Date is an object in javascript, hence if you do not change the reference (replace it by a new Date() object) the angular change detector will not trigger again.
So you will have to replace the startDate by a completely new instance of a javascript date with the given properties.
So replace
var ISOweekEnd = date;
by
var ISOweekEnd = new Date();

Related

show only the current month in datepicker and remaining all months should be disabled using angularjs

I have datepicker showing only months.
It should show only current month and remaining months should be disabled.
I have tried with min-date and max-date, but it is not working.
HTML:
<input type="text" class="form-control"
readonly="readonly" ng-model="xxxx"
min-mode="'month'" min-date="startmnth" max-date="endmnth"
datepicker-popup="yyyy-MM" is_open="status.date_opened"
ng-required="true" placeholder="YYYY-MM"
datepicker-options="datepickerOptions"
datepicker-mode="'month'" ng-click="openfromDate($event)"
show-button-bar="false">
JS:
$scope.startmnth = new Date().getMonth()
$scope.endmnth = new Date().getMonth()
$scope.datepickerOptions = {
startingDay: 1,
};
$scope.openfromDate = function ($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.status.date_opened = true;
};
You can add "dateDisabled: this.disabledDates" as part of your options, and then create a function like this:
this.disabledDates = function(data) {
var date = moment(data.date);
//check date and return true/false to disable/enable
return false;//to disable
}

Angularjs. TypeError: Cannot read property 'apply' of undefined

I have angularjs app with a form that has a dropdownlist and and a datetimepicker.
When I change the dropdownlist I want to update the date displayed in the datepicker.
I get the following error when I change selected item in the dropdownlist
TypeError: Cannot read property 'apply' of undefined
at HTMLInputElement.<anonymous> (bootstrap-datetimepicker.min.js:2)
at Function.each (jquery-3.1.1.min.js:2)
at r.fn.init.each (jquery-3.1.1.min.js:2)
at r.fn.init.a.fn.datetimepicker (bootstrap-datetimepicker.min.js:2)
at m.$scope.SymbolChanged (moduleConfigformController.js:29)
at fn (eval at compile (angular.js:15197), <anonymous>:4:159)
at m.$eval (angular.js:18017)
at angular.js:25775
at Object.<anonymous> (angular.js:28600)
at q (angular.js:357)
This is the offending line of code:
$("#fmStartDate").datetimepicker("setDate", new Date($scope.simulationsettings.StartDate));
Here is my controller:
mainApp2.controller("moduleConfigformController",
function moduleConfigformController($scope, moduleConfigformService, $uibModalInstance) {
$scope.close = function (e) {
$uibModalInstance.dismiss();
e.stopPropagation();
};
$scope.formDebug = "loaded";
var settingsPromise = moduleConfigformService.simulationsettings();
settingsPromise.then(function (settings) {
$scope.simulationsettings = settings;
$scope.symbols = $scope.simulationsettings.symbols;
$scope.intervals = $scope.simulationsettings.intervals;
}).catch(function (error) {
throw error;
});
$scope.SymbolChanged = function () {
console.log("Symbol ddl changed");
console.log("New value is " + $scope.simulationsettings.Symbol);
// hardcoded date
// TODO: Find StartDate and EndDate where Symbol = $scope.simulationsettings.Symbol
$scope.simulationsettings.StartDate = "24/12/2014 8:26 PM";
// Display the new date in the datetimepicker
// This line produced the TypeError
$("#fmStartDate").datetimepicker("setDate", new Date($scope.simulationsettings.StartDate));
console.log("startdate is " + $scope.simulationsettings.StartDate);
console.log("startdate is " + $scope.simulationsettings.EndDate);
}
$scope.submitConfigForm = function () {
console.log("configform submitted");
var startDate = $scope.simulationsettings.StartDate;
var endDate = $scope.simulationsettings.EndDate;
var symbol = $scope.simulationsettings.Symbol;
var interval = $scope.simulationsettings.Intervals;
$scope.formDebug = "StartDate: " + startDate + " EndDate: " + endDate + " Symbol: " + symbol + " Interval: " + interval;
}
});
Here is my form:
<form name="configForm" ng-submit="submitConfigForm()">
<div class="modal-header" style="text-align:center">
<h3 class="modal-title">Configure</h3>
<div style="margin-top:10px">
<button tabindex="100" class="btn btn-success pull-left" type="submit" ng-class="{'btn-primary':configForm.$valid}">Start analysis</button>
<button class="btn btn-warning pull-right" ng-click="close($event)">Close</button>
</div>
</div>
<div class="modal-body">
<div class="col-sm-6" style="width: 100%;">
<div class="form-horizontal">
<div class="form-group">
<label class="control-label col-sm-3">Symbol</label>
<div class="col-sm-9">
<select ng-model="simulationsettings.Symbol" ng-change="SymbolChanged()" name="fmSymbols" id="fmSymbols">
<option ng-repeat="item in symbols" value="{{item.Symbol}}">{{item.Symbol}}</option>
</select>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-3">start date</label>
<div class="col-sm-9">
<input type="text" id="fmStartDate" class="form-control input-sm"
datetimepicker
ng-model="simulationsettings.StartDate"
placeholder="..."
name="fmStartDate">
</div>
</div>
</div>
form debug: '{{formDebug}}'
</div>
</div>
The datetimepicker directive
"use strict";
angular.module("datetimepicker", [])
.provider("datetimepicker", function () {
var defaultOptions = {};
this.setOptions = function (options) {
defaultOptions = options;
};
this.$get = function () {
return {
getOptions: function () {
return defaultOptions;
}
};
};
})
.directive("datetimepicker", [
"$timeout",
"datetimepicker",
function ($timeout,datetimepicker) {
var defaultOptions = datetimepicker.getOptions();
return {
require : "?ngModel",
restrict: "AE",
scope : {
datetimepickerOptions: "#"
},
link : function ($scope, $element, $attrs, ngModelCtrl) {
var passedInOptions = $scope.$eval($attrs.datetimepickerOptions);
var options = jQuery.extend({}, defaultOptions, passedInOptions);
$element
.on("dp.change", function (e) {
if (ngModelCtrl) {
$timeout(function () {
ngModelCtrl.$setViewValue(e.target.value);
});
}
})
.datetimepicker(options);
function setPickerValue() {
var date = options.defaultDate || null;
if (ngModelCtrl && ngModelCtrl.$viewValue) {
date = ngModelCtrl.$viewValue;
}
$element
.data("DateTimePicker")
.date(date);
}
if (ngModelCtrl) {
ngModelCtrl.$render = function () {
setPickerValue();
};
}
setPickerValue();
}
};
}
]);
Any idea how to update the datetimepicker so it displays the updated value?
You are updating the model here:
$scope.simulationsettings.StartDate = "24/12/2014 8:26 PM";
Then you try to set the datepickers value here:
$("#fmStartDate").datetimepicker("setDate", new Date($scope.simulationsettings.StartDate));
But the datepicker has $scope.simulationsettings.StartDate as model. This is why you get the error. Angular is trying to call the digest cycle twice.
Your function should look like this:
$scope.SymbolChanged = function () {
console.log("Symbol ddl changed");
console.log("New value is " + $scope.simulationsettings.Symbol);
// hardcoded date
// TODO: Find StartDate and EndDate where Symbol = $scope.simulationsettings.Symbol
// the binding should be of the same type as your input, it means that the value returned from the datepicker must be a Date
$scope.simulationsettings.StartDate = new Date("24/12/2014 8:26 PM");
// This line is useless, the datepicked model is binded to $scope.simulationsettings.StartDate
// $("#fmStartDate").datetimepicker("setDate", new Date($scope.simulationsettings.StartDate));
console.log("startdate is " + $scope.simulationsettings.StartDate);
console.log("startdate is " + $scope.simulationsettings.EndDate);
}
But since you are using an input type="text" we need more information on the datetimepicker directive that you are using.
your are trying to put a date object in text field which may work if you're not using a date-picker
1/ change your input type to type date or change this $("#fmStartDate").datetimepicker("setDate", new Date($scope.simulationsettings.StartDate));
to this $("#fmStartDate").datetimepicker("setDate", $scope.simulationsettings.StartDate);
and it may work if your date-picker accepts this format "24/12/2014 8:26 PM"
2/ you may wanna check the date-picker documentation for the accepted format types

The popup calendar show local date but the selected date is UTC

I want to keep the selected date in UTC format.
But the pop-up calendar is not sync with the selected datetime.
Lets's say when I click 08/13 and the selected date is 08/12
Because in my timezone is 08/13 but the it's still on 08/12 with UTC timezone
HTML
<div class="col-sm-10" ng-click="open($event)">
<input type="text" class="form-control ng-pristine ng-untouched ng-valid ng-isolate-scope ng-valid-date ng-valid-required" datepicker-popup="yyyy/MM/dd" is-open="opened" datepicker-options="dateOptions" ng-required="true" close-text="Close" required="required" aria-required="false" aria-invalid="false" ng-model="form.start_date" />
</div>
JS controller
app.controller('FlightSkuStartDatepickerCtrl', ['$scope',
function($scope) {
// Disable weekend selection
$scope.disabled = function(date, mode) {
return (mode === 'day' && (date.getDay() === 0 || date.getDay() === 6));
};
$scope.toggleMin = function() {
$scope.minDate = $scope.minDate ? null : new Date();
};
$scope.toggleMin();
$scope.open = function($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.opened = true;
};
$scope.dateOptions = {
formatYear: 'yy',
startingDay: 1,
class: 'datepicker'
};
$scope.formats = ['YYYY/MM/DD'];
$scope.format = $scope.formats[0];
}
]);
If you are storing it as UTC why do you have problem with keeping it as UTC?. If you want to change your time from UTC to another timezone you can use moment for angular. But you must give moment a timezone for converting. For example:
function utctoTimeZoneFormat(date, current_tz) {
var offset = moment(date).tz(current_tz).utcOffset();
var returnObj = {
"day": "",
"time": ""
}
returnObj.day = moment.utc(date).tz(current_tz).utcOffset(offset).format('dddd');
returnObj.time = moment.utc(date).tz(current_tz).utcOffset(offset).format('HH:mm:ss')
return returnObj;
}
You can pass date parameter as your selected date from the UI and current_tz as your timezone. But you must pass it like " America/Los_Angeles" as title of timezone.
function timeZoneToUTCFormat(date, current_tz) {
var offset = moment(date).tz(current_tz).utcOffset();
offset = offset * -1;
var returnObj = {
"day": "",
"time": ""
}
returnObj.day = moment.utc(date).tz(current_tz).utcOffset(offset).format('dddd');
returnObj.time = moment.utc(date).tz(current_tz).utcOffset(offset).format('HH:mm:ss')
return returnObj;
}
The second function enables you to store your selected date as UTC. You should pass the parameters accordingly. For example; if you are in "America/Los_Angeles" you should pass your timezone as the given string and you'll convert it to UTC. So you can sync your UI and backend by using the two functions written above.
In the newer versions of ui-datepicker there is an easier way to solve this
ng-modal-options='{"timezone":"utc"}'
this remove the timezone component from date
In older versions
The solution is to remove the timezone component of the date part (better have directive for reusability...
myapp.directive("dateToIso", function () {
var linkFunction = function (scope, element, attrs, ngModelCtrl) {
ngModelCtrl.$parsers.push(function (datepickerValue) {
return moment(datepickerValue).format("YYYY-MM-DD");
});
};
return {
restrict: "A",
require: "ngModel",
link: linkFunction
};
});
You can use it like below
<input ng-model='abc' date-to-iso ui-datepicker>

Asp.net MVC run javascript on button click

I kind of messed up the logic of my code, and I can't figure out how to fix it. I have a Bootstrap navtab panel that when the tabs are clicked, based on which tab is clicked it runs an MVC C# function in my controller. I actually need this to happen on a button click. SO the user enters a date into the datepicker, clicks submit, and then based on which tab is selected, a function will be run. How can I do this on a button click?
Here is my datepicker and button:
<div class="row spiff-datepicksection">
<div class="col-lg-6 pull-right">
<div class="col-sm-5 col-lg-offset-4">
<div class="form-group">
<div class="input-group date">
<input id="startDate" type="text" class="form-control" />
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
<div class="col-lg-3">
<input class="spiffdate-btn" type="submit" value="Submit" />
</div>
</div>
</div>
Here is my javascript:
<script>
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
var wrongid = $('.tab-content .active').attr('id');
$('a[data-toggle="tab"]').removeClass("active");
$(this).addClass("active");
var correctid = $(this).data("id");
alert($('.tab-content .active')[0].outerHTML);
var startDate = $('#startDate').val();
if (correctid == "delayedspiff")
$.get("#Url.Action("DelayedSpiffDate", "Dashboard")", { startDate: startDate });
else
$.get("#Url.Action("InstantSpiffDate", "Dashboard")", { startDate: startDate });
});
</script>
And here is my controller if it is needed:
public ActionResult DelayedSpiffDate(DateTime startDate)
{
var available = _appService.GetFeatureStatus(1, "spiffDashboard");
if (!available)
return RedirectToAction("DatabaseDown", "Error", new { area = "" });
var acctId = User.AccountID;
//startDate = DateTime.Today.AddDays(-6); // -6
var endDate = DateTime.Today.AddDays(1); // 1
Dictionary<DateTime, List<SpiffSummaryModel>> dict = new Dictionary<DateTime, List<SpiffSummaryModel>>();
try
{
var properties = new Dictionary<string, string>
{
{ "Type", "DelayedSpiff" }
};
telemetry.TrackEvent("Dashboard", properties);
dict = _reportingService.GetDailyDelayedSpiffSummaries(acctId, startDate, endDate);
}
catch (Exception e)
{
if (e.InnerException is SqlException && e.InnerException.Message.StartsWith("Timeout expired"))
{
throw new TimeoutException("Database connection timeout");
}
var error = _errorCodeMethods.GetErrorModelByTcError(PROJID.ToString("000") + PROCID.ToString("00") + "001", "Exception Getting DelayedSpiff Dashboard View", PROJID, PROCID);
error.ErrorTrace = e.ToString();
_errorLogMethods.LogError(error);
return RedirectToAction("index", "error", new { error = error.MaskMessage });
}
var spiffDateModels = new List<DelayedSpiffDateModel>();
foreach (var entry in dict)
{
var spiffDateModel = new DelayedSpiffDateModel();
spiffDateModel.Date = entry.Key;
spiffDateModel.Carriers = new List<DelayedSpiffCarrierModel>();
foreach (var item in entry.Value)
{
var spiffCarrierModel = new DelayedSpiffCarrierModel();
spiffCarrierModel.Carrier = item.CarrierName;
spiffCarrierModel.CarrierId = item.CarrierId;
spiffCarrierModel.ApprovedSpiffTotal = item.ApprovedSpiffTotal;
spiffCarrierModel.EligibleActivationCount = item.EligibleActivationCount;
spiffCarrierModel.IneligibleActivationCount = item.IneligibleActivationCount;
spiffCarrierModel.PotentialSpiffTotal = item.PotentialSpiffTotal;
spiffCarrierModel.SubmittedActivationCount = item.SubmittedActivationCount;
spiffCarrierModel.UnpaidSpiffTotal = item.UnpaidSpiffTotal;
spiffDateModel.Carriers.Add(spiffCarrierModel);
}
spiffDateModels.Add(spiffDateModel);
}
spiffDateModels = spiffDateModels.OrderByDescending(x => x.Date).ToList();
return PartialView(spiffDateModels);
}
Any ideas on how to make this happen on a button click?
You can try to create a handler of the 'click' event, which should retrieve a valid identifier of the selected tab and send a GET request to the server.
$(".spiffdate-btn").click(function(){
var correctid = $(".tab-content .active").attr("id");
var startDate = $("#startDate").val();
if (correctid == "delayedspiff")
$.get("#Url.Action("DelayedSpiffDate", "Dashboard")", { startDate: startDate });
else
$.get("#Url.Action("InstantSpiffDate", "Dashboard")", { startDate: startDate });
});
I realize this is an old question, but I am struggling with a similar issue so I am looking at old questions.
I think I see your problem though:
<script>
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
Your script calls "on shown".
If you do not want it running when it is shown, change it to "on click".
How? I can't help you with that yet. My javascript isn't that good.

Knockout js how to Implement Countdown Timer

I'm new on Knockout js.Trying to implement countdown timer on html with using knockout js
For this purpose I added 4 html elements(input, span and start, stop buttons) on view. When start button is pressed, the value that written on <input> objects should be passed to refreshViewModel, and there will be countdown process. When the countdown is processing remaining time will be showed inside <span> element. If the stop button is pressed countdown will be stopped.
If the countdown finishes another function(that is callbacked from another viewModel) which is filtering the page with some parameters will be initiated.
Binded textbox value to span value. I cannot figure out how to count and show to remaining value inside span dynamically?
Html:
<div id="pnlTimer" class="row">
<div class="span2 pull-right" style="border:1px solid rgb(218, 218, 218)" >
<span style="font-weight:bold">Reload Interval</span>
<br />
<input id="initialTime" style="width:20px;height:14px" data-bind="value: initialTime" />
<span id="remainingTime" style="visibility:hidden"> / 15</span> second(s)
<button class="btn" style="margin-top:5px" id="StartCounter" data-bind="click: StartCounter">
<i class="icon-play"></i>
</button>
<button style="visibility:hidden;margin-top:5px;margin-left:-44px" class="btn" id="StopCounter" data-bind="click: StopCounter">
<i class="icon-stop"></i>
</button>
</div>
</div>
Js:
#Url.Content("~/Content/App/viewModels/listCasesViewModel.js
#Url.Content("~/Content/App/viewModels/RefreshPageTimerViewModel.js
$(document).ready(function () {
var viewModel = new ListCasesViewModel();
viewModel.init();
var pnl = $("#pnlFilterPanel").get()[0];
ko.applyBindings(viewModel, pnl);
var viewModelTimer = new RefreshPageTimerViewModel();
viewModelTimer.init();
var pnlTimer = $("#pnlTimer").get()[0];
ko.applyBindings(viewModelTimer, pnlTimer);
viewModelTimer.callBackMethod = viewModel.filter;
});
First viewModel :RefreshPageTimerViewModel:
var RefreshPageTimerViewModel = function () {
var self = this;
self.StartCounter = ko.observable();
self.StopCounter = ko.observable();
self.initialTime = ko.observable();
self.remainingTime = ko.computed(function () {
return self.initialTime();
}, self);
countDown: ko.observable()
this.init = function () {
self.Count();
}
this.callBackMethod = function () {
alert("not implemented!");
}
this.Count = function () {
var initial = self.initialTime; // initialTime value;
var remaining = self.remainingTime;
if (remainingTime <= 0) {
this.ExecuteCallBackMethod();
}
}
this.ExecuteCallBackMethod = function () {
this.callBackMethod();
}
};
Second viewModel: ListCasesViewModel:
var ListCasesViewModel = function () {
var self = this;
self.selectedStartDate = ko.observable(null);
self.selectedEndDate = ko.observable(new Date());
self.selectedSearchKey = ko.observable("");
self.selectedStatuses = ko.observableArray();
self.selectedHospitals = ko.observableArray();
// methods...
this.init = function () {
self.selectedEndDate(new Date());
self.filter();
}
this.filter = function () {
// get filter control values
var startDate = self.selectedStartDate(); // dtStart.value();
var endDate = self.selectedEndDate(); //dtEnd.value();
var searchText = self.selectedSearchKey();
//And Some calculations....
The main problem is your ViewModel code, it uses an observable where you wanted a function (to Start and Stop the counter). Also, it does not seem to have a clear definition of what it is trying to do.
Also, im assuming you wanted the Start button to show when the timer is stopped, and the Stop button to show when the timer is started - so ive taken the liberty to add this functionality too.
Here is the rewritten view model:
var RefreshPageTimerViewModel = function () {
var self = this;
self.timerId = 0;
self.elapsedTime = ko.observable(0);
self.initialTime = ko.observable(0);
self.remainingTime = ko.computed(function(){
return self.initialTime() - self.elapsedTime();
});
self.isRunning = ko.observable(false);
self.StartCounter = function(){
self.elapsedTime(0);
self.isRunning(true);
self.timerId = window.setInterval(function(){
self.elapsedTime(self.elapsedTime()+1);
if(self.remainingTime() == 0){
clearInterval(self.timerId);
self.isRunning(false);
self.Callback();
}
},1000)
}
self.StopCounter = function(){
clearInterval(self.timerId);
self.isRunning(false);
}
self.Callback = function(){}
}
A few things to note:
Has a property timerId, which does not need to be observable, but allows us to stop the timer which is used to increment the elapsedTime
has an observable property isRunning used to control the visibility of the Start and Stop buttons
has an empty function Callback which can be used to execute any function when the countdown reaches zero.
Here is the new markup:
<div id="pnlTimer" class="row">
<div class="span2 pull-right" style="border:1px solid rgb(218, 218, 218)" >
<span style="font-weight:bold">Reload Interval</span>
<br />
<input id="initialTime" style="width:20px;height:14px" data-bind="value: initialTime" />
<span id="remainingTime" data-bind="text: remainingTime"></span> second(s)
<button class="btn" style="margin-top:5px" id="StartCounter" data-bind="click: StartCounter, visible: !isRunning()">
start
</button>
<button style="margin-top:5px" class="btn" id="StopCounter" data-bind="click: StopCounter, visible:isRunning()">
Stop
</button>
</div>
</div>
Note the addition of visible: !isRunning() to the start and visible:isRunning() to the stop buttons.
Finally, here is the init code:
$(function(){
var viewModelTimer = new RefreshPageTimerViewModel();
viewModelTimer.Callback = function(){
alert("finished");
};
ko.applyBindings(viewModelTimer);
})
Note the creation of a callback function which simply alerts. Your code could be as it was, ie viewModelTimer.callBackMethod = viewModel.filter;
Finally, a live example to allow you to play around: http://jsfiddle.net/eF5Ec/

Categories