AngularJS + Datatables + Dropdownlist - angularjs

I'm using AngularJS to populate my datatable. What I want to know is how can I populate the datatable based on the dropdownlist
This is my dropdownlist
<div>
Get Users with Role:
<select id="ddlRole" data-ng-model="selectedRole" data-ng-change="populateDataTable()" data-ng-options="v.name for (k,v) in roles"></select>
<input type="hidden" value="{{selectedRole}}" />
</div>
This is my angular code
$scope.roles = [
{name: 'XXX' },
{name: 'YYY' }
];
$scope.selectedRole = $scope.roles[0];
//onchange event
$scope.populateDataTable = function () {
$scope.selectedRole = $scope.selectedRole.name;
RefreshDataTable(); //TODO
};
How can I change this to make an ajax call to retreive the data, populate the datatable based on the dropdownlist value and retain the value of dropdownlist as well.
I'm sure we can do this using JQuery but I dont want to mix these and make a mess. Is there any way I can acheive this using AngularJS?

Here is a simple data table directive:
appModule.directive('dataTable', [function () {
return function (scope, element, attrs) {
// apply DataTable options, use defaults if none specified by user
var options = {};
if (attrs.myTable.length > 0) {
options = scope.$eval(attrs.myTable);
} else {
options = {
"bStateSave": true,
"iCookieDuration": 2419200, /* 1 month */
"bJQueryUI": true,
"bPaginate": false,
"bLengthChange": false,
"bFilter": false,
"bInfo": false,
"bDestroy": true
};
}
// Tell the dataTables plugin what columns to use
// We can either derive them from the dom, or use setup from the controller
var explicitColumns = [];
element.find('th').each(function (index, elem) {
explicitColumns.push($(elem).text());
});
if (explicitColumns.length > 0) {
options["aoColumns"] = explicitColumns;
} else if (attrs.aoColumns) {
options["aoColumns"] = scope.$eval(attrs.aoColumns);
}
// aoColumnDefs is dataTables way of providing fine control over column config
if (attrs.aoColumnDefs) {
options["aoColumnDefs"] = scope.$eval(attrs.aoColumnDefs);
}
if (attrs.fnRowCallback) {
options["fnRowCallback"] = scope.$eval(attrs.fnRowCallback);
}
// apply the plugin
var dataTable = element.dataTable(options);
// watch for any changes to our data, rebuild the DataTable
scope.$watch(attrs.aaData, function (value) {
var val = value || null;
if (val) {
dataTable.fnClearTable();
dataTable.fnAddData(scope.$eval(attrs.aaData));
}
});
if (attrs.useParentScope) {
scope.$parent.dataTable = dataTable;
} else {
scope.dataTable = dataTable;
}
};
}]);
Then initialize it in your controller. Override fnServerData method, append your selected value (selected role) and filter data on server side.
$scope.overrideOptions = {
"bStateSave": true,
"iDisplayLength": 8,
"bProcessing": false,
"bServerSide": true,
"sAjaxSource": 'Data/Get',
"bFilter": false,
"bInfo": true,
"bLengthChange": false,
"sServerMethod": 'POST', ,
"fnServerData": function(sUrl, aoData, fnCallback, oSettings) {
var data = {
dataTableRequest: aoData,
selectedDropDownValue: $scope.selectedRole
};
$http.post(sUrl, data).success(function (json) {
if (json.sError) {
oSettings.oApi._fnLog(oSettings, 0, json.sError);
}
$(oSettings.oInstance).trigger('xhr', [oSettings, json]);
fnCallback(json);
});
}
};
var columnDefs = [
{
"mData": "id",
"bSortable": false,
"bSearchable": false,
"aTargets": ['tb-id']
},
{
"mData": "data",
"aTargets": ['tb-data']
}
];
Refresh the datatable on select change.
$scope.populateDataTable = function () {
if ($scope.dataTable) {
$scope.dataTable.fnDraw();
}
};
Html markup:
<table class="display m-t10px" data-table="overrideOptions" ao-column-defs="columnDefs">
<thead>
<tr>
<th class="tb-id"></th>
<th class="tb-data></th>
</tr>
</thead>
<tbody>
</tbody>
</table>

Hope above your code is in controller.
Inject $http and make a $http get or post call
$scope.populateDataTable = function () {
$scope.selectedRole = $scope.selectedRole.name;
$http.get('api/controller', function(result){
//response from the service call in result
});
};

Related

Angular UI-Grid filtering

On my angular application I have a UI-Grid and a button to open a modal window.
In my gridoptions I enabled external filtering. But the problem is whenever I enter the filter field and press enter the button(modal window) is triggered and the modal opens up. I don't have any key events inside my controller.
Here are my gridoptions for the ui-grid:
gridOptions: uiGrid.IGridOptionsOf<any> = {
appScopeProvider: this,
data: [] as any[],
enableFiltering: true,
enablePaginationControls: false,
useExternalPagination: true,
minRowsToShow: 5,
useExternalSorting: true,
useExternalFiltering: true,
onRegisterApi: (gridApi: uiGrid.IGridApiOf<any>) => {
this.gridApi = gridApi;
this.gridApi.core.on.sortChanged(this.$scope, (grid: uiGrid.IGridInstanceOf<any>, columns: Array<uiGrid.IGridColumnOf<any>>) => this.sortRequests(grid, columns));
this.gridApi.pagination.on.paginationChanged(this.$scope, (newPage: number, pageSize: number) => this.changePage(newPage, pageSize));
this.gridApi.core.on.filterChanged(this.$scope, () => {
if (this.filterTimeout != undefined) {
this.$timeout.cancel(this.filterTimeout);
}
this.filterTimeout = this.$timeout(() => this.filterRequests(), 500);
});
},
I've set debuggers inside the filter methods but it doesn't get triggered by pressing enter only when you type a character.
Does anyone know what is triggering this button when enter is pressed?
UPDATE 1
<button class="btn btn-primary" ng-click="vm.open()" translate="country.headers.selectCountry"></button>
<div id="grid1" ui-grid="vm.gridOptions" ui-grid-pagination></div>
open() {
let instance = this.$uibModal.open({
templateUrl: 'app/common/controllers/html.html',
controller: 'controller as vm',
backdrop: 'static',
windowClass: 'country-modal',
resolve: {
programId: () => this.$stateParams['programId']
}
});
instance.result.then((dataGenerated: boolean) => {
if (dataGenerated) {
this.loadData();
}
});
}
UPDATE 2:
private filterRequests(): void {
let grid = this.gridApi.grid;
this.filtering = [];
for (let i = 0; i < grid.columns.length; i++) {
let column = grid.columns[i];
if (column.filters.length > 0) {
for (let j = 0; j < column.filters.length; j++) {
let filter = column.filters[j].term;
if (filter != undefined && filter != '')
this.filtering.push(column.field + '.contains("' + filter + '")');
}
}
}
this.loadRequests();
}
loadRequests(): void {
this.service
.get(
this.model.id,
{
params: {
page: this.pagingInfo.page,
pageSize: this.pagingInfo.pageSize,
sort: this.sorting,
filter: this.filtering
}
})
.then((data: any) => {
this.gridOptions.data = data.data;
this.gridHeight = (this.gridOptions.data.length + 2) * this.gridOptions.rowHeight;
this.pagingInfo.page = data.page;
this.pagingInfo.pageSize = data.pageSize;
this.pagingInfo.totalCount = data.totalCount;
});
}
With kinds regards, Brent

How to set all the rows of ng-repeat to be selected in Angular

I have a smimple ng-repeat that displays user details. I am trying to set all the rows to be selected.
Currently I can manually click and select all the rows individually using following code:
<tr ng-repeat="room in classrooms" ng-class="{'selected': room.selected}" ng-click="select(room)">
in controller
$scope.select = function(item) {
item.selected ? item.selected = false : item.selected = true;
}
and to get data from the selected rows I use following logic
$scope.getAllSelectedRows = function()
{
var x = $filter("filter")($scope.classrooms,
{
selected: true
}, true);
console.log(x);
}
UPDATED FROM #KADIMA RESPONSE
$scope.toggleSelectAll = function()
{
angular.forEach($scope.classrooms, function(room) {
room.selected ? room.selected = false : room.selected = true;
})
}
Set up a new function in your controller:
$scope.selectAll = function() {
angular.forEach(classrooms, function(room) {
room.selected = true
}
}
And then you can create a button in your html to call this function.
If you want to select all data you got you can set selected property using angular.forEach() method.
https://docs.angularjs.org/api/ng/function/angular.forEach
In your case:
angular.forEach(x, fuction(value) {
value.selected = true;
}

Kendo Multiselect with AngularJS

I want to set maxSelectedItems from a textbox value. Apart from that I want to reset kendo multiselect if textbox value change. I have tried below thing but it is not working.
<input type="hidden" ng-model="DTO.ProgramID" id="ProgramID" value="3">
<input class="form-control" data-val="true" id="ClassSize " maxlength="2" name="ClassSize " ng-model="DTO.ClassSize "type="text" ng-blur="OnClassSizeChange()">
<select id="CourseClientIDs" kendo-multi-select k-options="selectClientOptions" ng-model="DTO.CourseClientIDs"></select>
$scope.selectClientOptions = {
placeholder: "---Select Clients---",
dataTextField: "ClientFLName",
dataValueField: "ClientID",
valuePrimitive: true,
autoBind: false,
dataSource: {
type: "jsonp",
serverFiltering: true,
transport: {
read: {
url: "Home/ClientDataSource/",
cache: false
},
parameterMap: function (data, action) {
if (action === "read") {
if ($scope.DTO.ProgramID != undefined) {
return {
programID: $scope.DTO.ProgramID
};
}
} else {
return data;
}
}
}
}
};
$scope.OnClassSizeChange = function () {
if ($scope.DTO.ClassSize == 0) {
var message = "Class size cannot be 0.";
return false;
}else{
$scope.selectClientOptions.maxSelectedItems = $scope.DTO.ClassSize; //Not Working
$scope.selectClientOptions.dataSource.read(); //Not Working
}
return true;
};
I am trying angularjs with kendo first time. Please help me.
In the textbox define ngChange specify the function to execute when value change. Supposing ngModel for textbox is in $scope.nvm, try this:
$scope.nvm = 1; //value in textfield
$scope.setnvm = function(){//change maxSelectedItems and reset values
var ms = angular.element(document.getElementById("sems")).data("kendoMultiSelect");
ms.setOptions({
maxSelectedItems: $scope.nvm,
});
ms.value([]);
};

Angular with Coffeescript: why my method are executed?

I'm an angular beginner, and coming from Ruby I choose to use Coffescript instead of JS. I'm using ng-classify to define my controller, services and Factory with Coffeescript classes, but I cannot understand what is wrong.
I have my code in this [github repo], but I try to explain here my issue.
I have this controller
class Setting extends Controller
constructor: (#DataService,$log) ->
#examType = #DataService.getObject('setting_examtype') || { checked: false }
#settingList = #DataService.getObject('setting_list') || [
{ text: 'Dai precedenza a domande sbagliate', checked: false },
{ text: 'Dai precedenza a domande mai fatte', checked: false },
{ text: 'Mostra subito la soluzione', checked: false }
]
#questionPossibility = [10,20,30,40,50]
#questionNumber = #DataService.get('question_number') || 30
return
examTypeChecked: () =>
#DataService.setObject('setting_examtype',#examType)
console.log 'examTypeChecked'
return
settingListChecked: () =>
console.log 'settingListChecked'
#DataService.setObject('setting_list',#settingList)
return
questionNumberChecked: () =>
console.log 'questionNumberChecked'
#DataService.set('question_number',#questionNumber)
return
The compiled version is:
(function() {
var Setting,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
Setting = (function() {
function Setting(DataService, $log) {
this.DataService = DataService;
this.questionNumberChecked = __bind(this.questionNumberChecked, this);
this.settingListChecked = __bind(this.settingListChecked, this);
this.examTypeChecked = __bind(this.examTypeChecked, this);
this.examType = this.DataService.getObject('setting_examtype') || {
checked: false
};
this.settingList = this.DataService.getObject('setting_list') || [
{
text: 'Dai precedenza a domande sbagliate',
checked: false
}, {
text: 'Dai precedenza a domande mai fatte',
checked: false
}, {
text: 'Mostra subito la soluzione',
checked: false
}
];
this.questionPossibility = [10, 20, 30, 40, 50];
this.questionNumber = this.DataService.get('question_number') || 30;
return;
}
Setting.prototype.examTypeChecked = function() {
this.DataService.setObject('setting_examtype', this.examType);
console.log('examTypeChecked');
};
Setting.prototype.settingListChecked = function() {
console.log('settingListChecked');
this.DataService.setObject('setting_list', this.settingList);
};
Setting.prototype.questionNumberChecked = function() {
console.log('questionNumberChecked');
this.DataService.set('question_number', this.questionNumber);
};
return Setting;
})();
angular.module('app').controller('settingController', ['DataService', '$log', Setting]);
}).call(this);
As you can see I insert some log statement, and from the console I understand that all my methods are executed. Why? Why examTypeChecked is called?
I call it only if someone use a toggle..
<ion-toggle ng-model="setting.examType" ng-checked="setting.examTypeChecked()" toggle-class="toggle-calm" ng-true-value="oltre" ng-false-value="entro">Tipo di esame</ion-toggle>
You got it wrong way, your code is fine, use of code is not what you expected
<ion-toggle ng-model="setting.examType" ng-checked="setting.examTypeChecked()" toggle-class="toggle-calm" ng-true-value="oltre" ng-false-value="entro">Tipo di esame</ion-toggle>
setting.examTypeChecked() will be called every time $digest() process is triggered, and it's triggered with each change of model, by $scope.apply(), $scope.digest(), $timeout() and few more

Should $bind save child data added in an ng-repeat

Hi I have a problem with $bind, I am binding a model and outputting the models via a ng-repeat. The ng-repeat outputs the stored data and also offers some fields for adding/changing data. The changes are reflected in the scope but are not being synced to Firebase.
Is this a problem with my implementation of $bind?
The HTML:
<iframe id="fpframe" style="border: 0; width: 100%; height: 410px;" ng-if="isLoaded"></iframe>
<form>
<ul>
<li ng-repeat="asset in upload_folder" ng-class="{selected: asset.selected}">
<div class="asset-select"><input type="checkbox" name="selected" ng-model="asset.selected"></div>
<div class="asset-thumb"></div>
<div class="asset-details">
<h2>{{asset.filename}}</h2>
<p><span class="asset-filesize" ng-if="asset.size">Filesize: <strong><span ng-bind-html="asset.size | formatFilesize"></span></strong></span> <span class="asset-filetype" ng-if="asset.filetype">Filetype: <strong>{{asset.filetype}}</strong></span> <span class="asset-dimensions" ng-if="asset.width && asset.height">Dimensions: <strong>{{asset.width}}x{{asset.height}}px</strong></span> <span class="asset-type" ng-if="asset.type">Asset Type: <strong>{{asset.type}}</strong></span></p>
<label>Asset Description</label>
<textarea ng-model="asset.desc" cols="10" rows="4"></textarea>
<label>Creator</label>
<input type="text" ng-model="asset.creator" maxlength="4000">
<label>Release Date</label>
<input type="text" ng-model="asset.release">
<label for="CAT_Category">Tags</label>
<input type="text" ng-model="asset.tags" maxlength="255">
</div>
</li>
</ul>
</form>
The Controller: (fpKey is a constant that stores the Filepicker API key)
.controller('AddCtrl',
['$rootScope', '$scope', '$firebase', 'FBURL', 'fpKey', 'uploadFiles',
function($rootScope, $scope, $firebase, FBURL, fpKey, uploadFiles) {
// load filepicker.js if it isn't loaded yet, non blocking.
(function(a){if(window.filepicker){return}var b=a.createElement("script");b.type="text/javascript";b.async=!0;b.src=("https:"===a.location.protocol?"https:":"http:")+"//api.filepicker.io/v1/filepicker.js";var c=a.getElementsByTagName("script")[0];c.parentNode.insertBefore(b,c);var d={};d._queue=[];var e="pick,pickMultiple,pickAndStore,read,write,writeUrl,export,convert,store,storeUrl,remove,stat,setKey,constructWidget,makeDropPane".split(",");var f=function(a,b){return function(){b.push([a,arguments])}};for(var g=0;g<e.length;g++){d[e[g]]=f(e[g],d._queue)}window.filepicker=d})(document);
$scope.isLoaded = false;
// Bind upload folder data to user account on firebase
var refUploadFolder = new Firebase(FBURL.FBREF).child("/users/" + $rootScope.auth.user.uid + "/upload_folder");
$scope.upload_folder = $firebase(refUploadFolder);
$scope.upload_folder.$bind($scope,'upload_folder');
// default file picker options
$scope.defaults = {
mimetype: 'image/*',
multiple: true,
container: 'fpframe'
};
// make sure filepicker script is loaded before doing anything
// i.e. $scope.isLoaded can be used to display controls when true
(function chkFP() {
if ( window.filepicker ) {
filepicker.setKey(fpKey);
$scope.isLoaded = true;
$scope.err = null;
// additional picker only options
var pickerOptions = {
services:['COMPUTER', 'FACEBOOK', 'GMAIL']
};
var storeOptions = {
location: 'S3',
container: 'imagegrid'
};
var options = $.extend( true, $scope.defaults, pickerOptions );
// launch picker dialog
filepicker.pickAndStore(options, storeOptions,
function(InkBlobs){
uploadFiles.process(InkBlobs, $scope.upload_folder);
},
function(FPError){
$scope.err = FPError.toString();
}
);
} else {
setTimeout( chkFP, 500 );
}
})();
}])
I also have a service handling the input from Filepicker, this creates new entries in the firebase at the reference that is bound (using Firebase methods rather than AngularFire maybe this breaks the binding?)
.service('uploadFiles', ['$rootScope', 'FBURL', function($rootScope, FBURL) {
return {
process: function(InkBlobs, upload_folder) {
var self = this;
var countUpload = 0;
// write each blob to firebase
angular.forEach(InkBlobs, function(value, i){
var asset = {blob: value};
// add InkBlob to firebase one it is uploaded
upload_folder.$add(asset).then( function(ref){
self.getDetails(ref);
countUpload++;
});
});
// wait for all uploads to complete before initiating next step
(function waitForUploads() {
if ( countUpload === InkBlobs.length ) {
self.createThumbs(upload_folder, { multi: true, update: false, location: 'uploads' });
} else {
setTimeout( waitForUploads, 500 );
}
})();
},
getDetails: function(ref) {
// after InkBlob is safely stored we will get additional asset data from it
ref.once('value', function(snap){
filepicker.stat(snap.val().blob, {size: true, mimetype: true, filename: true, width: true, height: true},
function(asset) {
// get asset type and filetype from mimetype
var mimetype = asset.mimetype.split('/');
asset.type = mimetype[0].replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
asset.filetype = mimetype[1];
// add metadata to asset in upload folder
ref.update(asset);
});
});
},
createThumbs: function(ref, options) {
var self = this;
// default options
options.multi = options.multi || false;
options.update = options.update || false;
options.location = options.location || 'asset';
// if pathbase is not provided generate it based on provided location
if (!options.pathbase) {
if (options.location === 'assets') {
options.pathbase = FBURL.LIBRARY + "/assets/";
} else if (options.location === 'uploads') {
options.pathbase = "/users/" + $rootScope.auth.user.uid + "/upload_folder/";
} else {
throw new Error('SERVICE uploadFiles.createThumbs: options.location is not valid must be assets or uploads');
}
}
var generateThumb = function(blob, path) {
filepicker.convert( blob,
{ width: 200, height: 150, fit: 'crop' },
{ location: 'S3', access: 'public', container: 'imagegrid', path: '/thumbs/' },
function(tnInkBlob){
var refThumbBlob = new Firebase(FBURL.FBREF).child(path);
refThumbBlob.set(tnInkBlob);
},
function(FPError){
alert(FPError);
},
function(percentage){
// can use to create progress bar
}
);
};
if (options.multi) {
// look at all assets in provided ref, if thumbnail is mission or update options is true generate new thumb
angular.forEach(ref, function(value, key){
if (typeof value !== 'function' && (!value.tnblob || options.update)) {
// thumb doesn't exist, generate it
var blob = value.blob;
var path = options.pathbase + key + '/tnblob';
generateThumb(blob, path);
}
});
} else {
// getting thumbnail for a single asset
var refAsset = new Firebase(FBURL.FBREF).child(options.pathbase + ref);
var blob = refAsset.val().blob;
var path = options.pathbase + ref + '/tnblob';
generateThumb(blob, path);
}
}
};
}]);
So to recap, data is being saved to /users/$rootScope.auth.user.uid/upload_folder and this is being rendered in the HTML. Changes in the HTML form are reflected in the scope but not in Firebase, despite the binding:
var refUploadFolder = new Firebase(FBURL.FBREF).child("/users/" + $rootScope.auth.user.uid + "/upload_folder");
$scope.upload_folder = $firebase(refUploadFolder);
$scope.upload_folder.$bind($scope,'upload_folder');
Any ideas as to why this is? Is my implementation incorrect or am I somehow breaking the binding? Is $bind even supposed to work with ng-repeat in this manner?
Thanks
Shooting myself for how simple this is, the error was in how I defined the binding. You can't set the binding on itself, you need two separate objects in the scope...
The firebase reference $scope.syncdata loads the initial data and all modifications made to $scope.upload_folder will be synced to firebase.
var refUploadFolder = new Firebase(FBURL.FBREF).child("/users/" + $rootScope.auth.user.uid + "/upload_folder");
$scope.syncdata = $firebase(refUploadFolder);
$scope.syncdata.$bind($scope,'upload_folder');

Resources