Disable dates using factory response UI Bootstrap Datepicker - angularjs

I'm trying to disable a date in the UI Bootstrap Datepicker connected to a Google calendar if that dates already have 3 or more events.
Thus far I get the array of events using an Angular Factory like this:
gardenpage.factory('Dates', function($http, $q) {
var deffered = $q.defer();
var data = [];
var Dates = {};
Dates.async = function() {
$http.get('http://localhost:7777/events')
.success(function (d) {
data = d;
deffered.resolve();
});
return deffered.promise;
};
Dates.data = function() { return data; };
return Dates;
});
The list of dates needs a bit more preprocessing so I have a function that puts the only dates that have 3 or more entries in a scope-variable:
$scope.occurences = ['2014-07-21','2014-07-28'];
Now finally this is my modified default UI Bootstrap date picker date disable function:
// Disable weekend selection
$scope.disabled = function(date, mode) {
return ( mode === 'day' && ( date.getDay() === 0 || date.getDay() === 6 ||
$scope.date_occurences.indexOf( $filter('date')(date, 'yyyy-MM-dd') ) !== -1 ));
};
It works as expected except for one little quirk, when the "disabled" function is called by the date picker, the array is empty, waiting for the async callback I presume. Which is why it's first as I select a date in the date picker as my dates gets disabled.
So how to get the callback before the date picker disable function is called, or how do I make it wait ? One alternative might be to refresh the Datepicker after the callback has arrived, but I'm not sure if that function exists on the date picker.

I didn't solve this exactly as stated above but a bit of a workaround:
1.
Used a small code that I found in a stack overflow comment http://plnkr.co/edit/Xwq7YtAD6qNHQw1aES3H?p=preview . Which lets you call the Angular-UI Bootstrap Datepicker "refreshView" using a button or other type of action. Basically setting up a new directive
`app.directive('jmDpRefreshView',function() {
var noop = function(){};
var refreshDpOnNotify = function (dpCtrl) {
return function() {
dpCtrl.refreshView();
};
};
return {
require: 'datepicker',
link: function(scope,elem,attrs,dpCtrl) {
var refreshPromise = scope[attrs.jmDpRefreshView];
refreshPromise.then(noop,noop,refreshDpOnNotify(dpCtrl));
}
};
});`
To call the refreshView functionality
$scope.toggleDisableMode = function() {
dateDisableDeferred.notify(new Date().getTime());
};
The function toggleDisableMode can be called using any type of action, for instance using a button to disable dates from the server: "ng-click='toggleDisableMode()'"
Another thing that might help you is either you could preload your Dates from the server
//preload
$scope.dates = disable_dates();
function disable_dates() {
console.log("disable dates function")
Dates.async().then(function() {
$scope.data = Dates.data();
//do whatever you like with your data
});
}
Or you could call the ".notify()" for the deferred when the data has been fetched from the server and it will disable when it is done.
function disable_dates() {
console.log("disable dates function")
Dates.async().then(function() {
$scope.data = Dates.data();
//console.log($scope.data )
//do whatever you like with your server data.
//notice this line, calls the disable function
dateDisableDeferred.notify(new Date().getTime());
});
}
This solution is attributed to this question and the comments in there:
angular ui datepicker refresh disabled dates

Related

ng-include template variable not changing on $scope.$watch (no button trigger)?

I have a controller 'AController' that has a template variable, $scope.tpl.partialtemplate1 = 'initialcontactlist.html'.
'AController' basically is in charge of an entire page, 'mainpage.html', where we have
<div ng-include="tpl.partialtemplate1"></div>
On 'mainpage.html', there is a form to add contacts. This form is not part of 'partialtemplate1''s views.
Upon submitting the form, I want the HTML view for 'partialtemplate1' to be reset to what it was on initial page load, because it will then reload the latest list of contacts.
I have tried things like incrementing a variable after each new contact is successfully added, and then having that variable watched and the partialtemplate variable changed.
for example, in 'AController':
$scope.tpl = {};
$scope.contactcount = 0;
$scope.contactsignupdata = new Contact();
$scope.tpl.partialtemplate1 = 'initialcontactlist.html';
$scope.successmessage = null;
$scope.addcontact = function() {
$scope.contactsignupdata.$save();
$scope.successmessage = 'Saved!';
$scope.contactsignupdata = new Contact();
$scope.contactcount = $scope.contactcount + 1;
};
$scope.$watch('contactcount', function(newValue, oldValue) {
$scope.$apply(function() {
$scope.tpl.partialtemplate1 = null;
$scope.tpl.partialtemplate1 = 'initialcontactlist.html';
});
/*$scope.partialtemplate1 = 'projecttasklists.html';*/
});
Why isn't the partialtemplate variable getting changed? Yes, the contact gets successfully saved each time - I took care of that with the Rails factory...
Your code sets partialtemplate1 to null, then straight back to 'initialcontactlist.html'. As far as Angular is concerned, nothing is changed. True bindings are not supported meaning that just because you changed partialtemplate1, doesn't mean it immediately happens or triggers any special events. For this specific scenario, you would have to set partialtemplate1 to null, set a timer, then trigger the change back to 'initialcontactlist.html'
I do not recommend this by the way
$scope.$watch('contactcount', function(newValue, oldValue) {
$scope.$apply(function() {
$scope.tpl.partialtemplate1 = null;
$timeout(function() {
$scope.tpl.partialtemplate1 = 'initialcontactlist.html';
}, 1000);
});
});
I highly recommend
Creating an API for Contacts that you can query. That way when a Contact is created, updated, or removed you can handle it yourself in a couple ways:
You can requery the data source each time something changes
If the API returns data related to the change, you don't need to requery
You should look into creating Angular Services and/or Factories to handle this. In fact it is quite easy to implement if it is a true REST API using $resource. If it is not a RESTful resource, you can use $http for custom queries
I solved this problem with $emit.
In the HTML file, upon pressing the submit button for the "Add a contact" form, two events are triggered (separated by the apostrophe button).
ng-click="addcontact(contactsignupdata.name);$emit('MyEvent')"
</form>
{{successmessage}}
In the controller file:
$scope.successmessage = null;
$scope.tpl = {};
$scope.tpl.partialtemplate1 = 'initialcontactlist.html';
$scope.addcontact = function(value) {
$scope.contactsignupdata.$save();
$scope.successmessage = 'Saved ' + $scope.contactsignupdata.name;
$scope.contactsignupdata = new Contact();
};
$scope.$on('MyEvent', function() {
$scope.tpl.partialtemplate1 = null;
$scope.resettofullcontactslist($scope.tpl.partialtemplate1);
});
$scope.resettofullcontactslist = function(value) {
$scope.tpl.partialtemplate1 = 'initialcontactlist.html';
};

Programmatically Select AngularJS Typeahead Option

I have a AngularJS Typeahead that retrieves matches asynchronously. When a barcode is scanned into the field, it returns the matching result, but the user still has to select it. I would like to automatically select the result if it's an exact match. I see that the typeahead has a select(idx) function, but am not sure how to get a reference to it from my controller.
I was envisioning something like this:
$scope.SearchItems = function (term) {
return $http.get('api/Items/Search', {
params: {
term: term
}
}).then(function (response) {
if (response.data.length == 1 && response.data[0].Code == term) {
// Somehow inform typeahead control to select response.data[0]
}
return response.data;
});
};
I had a similar issue and never figured how to access the typeahead's select(idx), but I managed to get this functionality working. Here's my hacky workaround....
$promise.then(function(res) {
angular.forEach(res, function(item, key) {
// if we have an exact match
if (item.title === term) {
// update model
$scope.src = item;
// find item in dropdown
var elm = '[id*=option-' + key + ']';
var opt = angular.element(document.querySelectorAll(elm));
//call click handler outside of digest loop
$timeout(function() {
opt.triggerHandler('click');
}, 0);
}
});
// return async results
return res;
});
Basically we just update our model manually, locate the element in our dropdown and then fire off the 'click' handler. Make sure you wrap the triggerHandler call in a $timeout() set to zero, otherwise you will get a $rootScope:inprog error since digest is already in progress.

FuelUX has no data parameter for the changed event?

Is it by design that the changed event does not pass a data parameter into the function?
$('#MyWizard').on('changed', function(e, data) {
console.log(data);
});
The above shows the data as being undefined.
Update
I suppose I can get around the issue by use this:
$('#MyWizard').on('changed', function(e, data) {
var item = $('#MyWizard').wizard('selectedItem');
});
For anyone else using FuelUX 2.6.1 (or another close version), this question helped me hook into the changed event for datepicker:
$(function() {
$('#idOfDatePicker').datepicker({
date : new Date(),
createInput : false,
dropdownWidth : 170,
restrictDateSelection : true
}).on('changed', function(event, date) {
//Useful code goes here!
});
});
As I stated in my update I used this:
$('#MyWizard').on('changed', function(e, data) { var item = $('#MyWizard').wizard('selectedItem'); });

angularjs binding/scope issue for select list?

OK switching my code to angularjs and the angular 'way', not sure what I am doing wrong.
A select list is not getting updated when the model changes unless I call $apply, and I find myself calling apply a lot.
index.html has this:
<div id='rightcol' data-ng-include="'partials/rightSidebar.html'"
data-ng-controller="rightSidebarController">
</div>
and rightSidebar.html has this:
<select id='srcList' size='10'
data-ng-model="data.source"
data-ng-click='srcOnclick()'
data-ng-options="s.title for s in data.srcList | filter:{title:data.srcFilter} | orderBy:'title'"></select>
rightSidebarController.js has this:
$scope.data = {};
$scope.data.srcList = dataProvider.getSourceList();
$scope.data.source = dataProvider.getSource();
dataProvider is a service that makes an asynchronous database call (IndexedDB) to populate srcList, which is what gets returned in dataProvider.getSource().
Is it the asynchronous database call that forces me to call $apply, or should the controller be ignorant of that?
Is there a 'better' way to do this?
Edited to add service code.
Another controller calls dataProvider.refreshSourceList:
myDB.refreshSourceList = function() {
myDB.getRecords("source", function(recs) {
myDB.srcList = recs;
$rootScope.$broadcast('SrcListRefresh');
});
};
myDB.srcList is the field being bound by $scope.data.srcList = dataProvider.getSourceList();
myDB.getRecords:
myDB.getRecords = function(storeName, callback) {
var db = myDB.db;
var recList = [];
var trans = db.transaction([storeName], 'readonly');
var store = trans.objectStore(storeName);
var cursorRequest = store.openCursor();
cursorRequest.onerror = myDB.onerror;
cursorRequest.onsuccess = function(e) {
var cursor = cursorRequest.result || e.result;
if (cursor === false || cursor === undefined) {
if (callback !== undefined) {
$rootScope.$apply(function() {
callback(recList);
});
}
} else if (cursor.value !== null) {
recList.push(cursor.value);
cursor.continue();
}
};
cursorRequest.onerror = myDB.onerror;
};
Anything you do async needs to be wrapped in $scope.$apply(). This is because angular works in a similar fashion to a game loop, however instead of constantly running, it knows to end the loop when an action is taken, and $scope.$digest() is called.
If you are using IndexedDB, I would recommend creating an angular wrapper for it, like so:
(forgive my IndexedDB code, I'm not experience with it)
angular.module('app',[])
.factory('appdb', function($rootScope){
var db = indexedDB.open('appdb', 3);
return {
get : function(table, query, callback) {
var req = db.transaction([table])
.objectStore(table)
.get(query);
req.onsuccess(function(){
$rootScope.$apply(function(){
callback(req.result);
});
});
}
};
});
This way you can be sure that any data retrieve and set on a controller scope inside of callback will have $scope.$digest() called afterward.

Capture "updater" action from AngularUI's Bootstrap Typeahead

How can I capture the "updater" event from AngularUI's UI Bootstrap directive?
I've defined my HTML:
<input type="text" pattern="[0-9]*" class="span6" placeholder="8675309" ng-model="empid" typeahead="entry for entry in httpEmpIdSearch($viewValue, 8)">
... and my associated function:
$scope.httpEmpIdSearch = function(query, limit)
{
return $http.get(
'/visitorLog/api/v1/employee/?limit=' + limit + '&empid__startswith=' + query
).then(function(response)
{
output = [];
angular.forEach(response.data.objects, function(value, key) {
this.push(value.bems.toString());
}, output);
return output;
});
}
I would like to take additional action (autocomplete a form) when the user clicks on an ID in the popup. If raw Bootstrap I would use the "updater":
$('#sampleElement').typeahead({
minLength: 3,
source: function (query, process)
{
// get the data
},
updater: function (item)
{
// user clicked on an item, do something more!
},
});
I've tried various listeners like:
$scope.$on('typeahead-updated', function(event)
{ ... }
But I've not way to let me capture such an event. Is there some way I can take additional action once a typeahead option is selected?
Also, in version 0.4.0 of the ui-bootstrap library the typeahead-on-select directive is now supported. This should provide your desired event.
See:
https://github.com/angular-ui/bootstrap/commit/91ac17c9ed691a99647b66b3f464e3585398be19
It does not appear that there is a way to hook into that event in the directive. You could however put a watch on the model value and simulate this behavior.
Check out this plunker example
Since you are pulling the list of data from the server on the fly this solution may not work for you. But I would definitely have a look at watch it seems like the best way to achieve the desired outcome.
Something along these lines could work.
$scope.output = [];
$scope.httpEmpIdSearch = function(query, limit)
{
return $http.get(
'/visitorLog/api/v1/employee/?limit=' + limit + '&empid__startswith=' + query
).then(function(response)
{
$scope.output.length = 0;
angular.forEach(response.data.objects, function(value, key) {
this.push(value.bems.toString());
}, $scope.output);
return $scope.output;
});
}
$scope.$watch('empid', function(newValue, oldValue){
if(newValue !== oldValue && $scope.output.indexOf(newValue) !== -1){
//do work here
}
});
The full list of typeahead related directives is here:
https://github.com/angular-ui/bootstrap/tree/master/src/typeahead/docs
typeahead-editable, typeahead-input-formatter, typeahead-loading, typeahead-min-length, typeahead-on-select, typeahead-template-url, typeahead-wait-ms

Resources