Attach a jQuery Extension containing Angular - angularjs

I have a grid element that generates the following HTML:
<div id="grid">
<table class="grid test" ng-controller="gridController" width="900">
<thead>
<tr><td class="th-break" colspan="8"> </td></tr>
<tr>
<th>Id</th>
<th>Title</th>
<th>Potential Customer</th>
<th>Est. Close Date</th>
<th>Est. Revenue</th>
<th>Probability</th>
<th>Rating</th>
<th></th>
</tr>
<tr><td class="th-break" colspan="8"> </td></tr>
</thead>
<tbody ui-sortable="sortableOptions" ng-model="opps">
<tr ng-repeat="obj in opps|orderBy:orderByField:reverseSort" style="cursor:move;">
<td>{{obj.id}}</td>
<td>{{obj.title}}</td>
<td>{{obj.customer}}</td>
<td>{{obj.closeDate}}</td>
<td>{{obj.revenue}}</td>
<td>{{obj.probability}}</td>
<td>{{obj.rating}}</td>
<td><i class="fa fa-times" ng-click="delete(obj)"></i></td>
</tr>
</tbody>
</table>
</div>
I created angular code that bound data to the grid and it worked perfectly. However, it was all manual binding and I'm tidying up my code. I want to create a jQuery extension so that I can pass in a ton of options and bind the grid using:
$('div#grid').bindGrid();
Here's my jQuery:
(function ($) {
'use strict';
$.fn.bindGrid = function (options) {
var settings = $.extend({
}, options);
var grid = this;
var gridMod = angular.module('grid', ['ui.sortable']);
gridMod.controller('gridController', function ($scope) {
$scope.orderBy = 'title';
$scope.reverseSort = false;
var list = [
{ 'id': 1, 'title': 'Interested in Product Designer', 'customer': '', 'closeDate': '4/2/2013', 'revenue': '$349,383.00', 'probability': '70', 'rating': 'Hot' },
{ 'id': 2, 'title': 'Interested in Product Designer', 'customer': 'Bold Sales Accessories', 'closeDate': '6/11/2013', 'revenue': '$234,382.00', 'probability': '20', 'rating': 'Cold' },
{ 'id': 3, 'title': 'Interested in Product Designer', 'customer': 'Coho Winery', 'closeDate': '6/18/2013', 'revenue': '$182,796.00', 'probability': '50', 'rating': 'Warm' },
{ 'id': 4, 'title': 'Interested in Plotters', 'customer': 'Daring Farm', 'closeDate': '7/28/2013', 'revenue': '$685,780.00', 'probability': '50', 'rating': 'Warm' }
];
$scope.opps = list;
$scope.delete = function (item) {
var index = $scope.opps.indexOf(item);
$scope.opps.splice(index, 1);
}
$scope.sortableOptions = {
stop: function (e, ui) {
var newOrder = list.map(function (i) {
return i.id;
}).join(', ');
console.log(newOrder);
}
}
});
angular.element(document).ready(function () {
angular.bootstrap(grid, ['grid']);
});
};
}(jQuery));
When I run this, I get "'gridController' not a function, got undefined." I'm sure it has something to do with scope, but not sure what's going on. Any suggestions?
And I don't want to use a directive as I need this to be highly configurable via options passed in through jQuery.
Thanks.

Nevermind, I had a typo. grrhhh, too many late nights. I've edited the code above.

Related

Accessing row in ngTable after it was sorted

In this plunk I have an ngTable with three rows. What I need to know is the row number after it was sorted. For example, sort the ngTable by name ascending. You should see in the name column 'aaa', 'bbb' and 'ccc'. Then click on the button, I'm expecting to see the first row updated, however the last row is set with the new values. This happens because the $scope.data array itself is not sorted, but ngTable sorts a copy internally. I need to update the first "visible" row, how can I achieve that?
HTML
<table ng-table="tableParams" class="table table-bordered">
<tbody>
<tr ng-repeat="u in $data">
<td title="'User ID'" sortable="'uid'" >
{{ u.uid }}
</td>
<td title="'Name'" sortable="'nm'" >
{{ u.nm }}
</td>
</tbody>
</table>
Javascript
var app = angular.module('app', ['ngTable']);
app.controller('myCtl', function($scope, NgTableParams) {
$scope.data = [{
uid: 1,
nm: 'ccc'
}, {
uid: 2,
nm: 'bbb'
}, {
uid: 3,
nm: 'aaa'
}];
$scope.tableParams = new NgTableParams({
count: 5
}, {
data: $scope.data
});
$scope.updateRow = function(row){
$scope.data[row].uid = 999;
$scope.data[row].nm = 'XXXXX';
$scope.tableParams.reload();
};
});
You have access to the data in tableParams.data. So to do what you model above, you would just need to do something like this:
$scope.updateRow = function(){
$scope.tableParams.data[0].uid = 999;
$scope.tableParams.data[0].nm = 'XXXXX';
$scope.data = $scope.tableParams.data;
};

How to use select filter with static values on async (server-side) ngTable

I'm using ngTable 0.3.3 and am doing a async call to populate the table (pagination also done on server side). Here's my code:
var data = [];
$scope.tableParams = new ngTableParams({
page: 1,
count: 10
}, {
getData: function($defer, params) {
// getData gets called when you click on a different page in the pagination links.
// get the page number and size from params.$params
var pageNumber = params.$params.page;
var pageSize = params.$params.count;
// set up the query parameters as expected by your server
var mm = params.filter();
var queryParams = {"page_size":pageSize, "page_number":pageNumber, "sorting": params.sorting(), role: mm.role, id: mm.id, email: mm.email};
$log.log(mm.role);
// $log.log(mm.role, mm.email);
User.getCount().then(function (total) {
User.query(queryParams).then(function (result) {
params.total(total);
$defer.resolve(result);
});
});
}
});
with this html:
<table ng-table="tableParams" show-filter="true" class="table table-custom">
<tr ng-repeat="user in $data">
<td data-title="'Name'">
{{user.first_name}} {{user.last_name}}
</td>
<td data-title="'Role'" sortable="'role'" filter="{ 'role': 'text' }">
{{user._role}}
</td>
<td data-title="'Email'" sortable="'email'" filter="{ 'email': 'text' }">
{{user.email}}
</td>
<td data-title="'Created date'">
{{user.created_at | date: 'dd MMM yyyy'}}
</td>
<td data-title="'Edit'">
<a ui-sref="app.users.edit({userId: user.id})">Edit</a>
</td>
<td data-title="'Edit'">
<a ng-click="deleteUser(user.id)">Delete</a>
</td>
</tr>
</table>
And both filtering and pagination works as expected. You can see I have a $log call which tells me when the filter is being triggered while I type in the text field. Now I'm trying to get the role as a select rather than a text field, so I changed my code like so:
$scope.roles = function (column) {
var def = $q.defer();
var docType = [{role: 'admin'}, {role: 'customer'}, {role: 'singer'}, {role: 'voiceArtist'}];
def.resolve(docType);
return def;
};
var data = [];
$scope.tableParams = new ngTableParams({
page: 1,
count: 10
}, {
getData: function($defer, params) {
// getData gets called when you click on a different page in the pagination links.
// get the page number and size from params.$params
var pageNumber = params.$params.page;
var pageSize = params.$params.count;
// set up the query parameters as expected by your server
var mm = params.filter();
var queryParams = {"page_size":pageSize, "page_number":pageNumber, "sorting": params.sorting(), role: mm.role, id: mm.id, email: mm.email};
$log.log(mm.role);
// $log.log(mm.role, mm.email);
User.getCount().then(function (total) {
User.query(queryParams).then(function (result) {
params.total(total);
$defer.resolve(result);
});
});
}
});
And this html:
<table ng-table="tableParams" show-filter="true" class="table table-custom">
<tr ng-repeat="user in $data">
<td data-title="'Name'">
{{user.first_name}} {{user.last_name}}
</td>
<td data-title="'Role'" sortable="'role'" filter="{ 'role': 'text' }">
{{user._role}}
</td>
<td data-title="'Role1'" filter="{role: 'select'}" filter-data="roles($column)">{{user._role}}</td>
<td data-title="'Email'" sortable="'email'" filter="{ 'email': 'text' }">
{{user.email}}
</td>
<td data-title="'Created date'">
{{user.created_at | date: 'dd MMM yyyy'}}
</td>
<td data-title="'Edit'">
<a ui-sref="app.users.edit({userId: user.id})">Edit</a>
</td>
<td data-title="'Edit'">
<a ng-click="deleteUser(user.id)">Delete</a>
</td>
</tr>
</table>
The roles (that I want to populate the select from) is a static array with five elements in it. I can see the select on the Role column, but is empty (doesn't show admin, customer, etc) and it doesn't trigger the filter. When I select an item, nothing happens anywhere. Wha am I missing here?
PS: I've seen similar questions and have even tried their plunker when was available, but the questions I've found they either use async values for the select (which I don't want to) or the whole data is static, which again, I don't want to. Any help would be much appreciated.
EDIT
How I got it to work:
My js code:
$scope.roles = function (column) {
var def = $q.defer();
var docType = [{id: 'admin', title: 'Admin'}, {id: 'customer', title: 'Customer'}, {id: 'singer', title: 'Singer'}, {id: 'voiceArtist', title: 'Voice Artist'}];
def.resolve(docType);
return def;
};
var data = [];
$scope.deleteUser = function (id) {
User.get(id).then(function (result) {
$scope.tmpUser = result;
$scope.tmpUser.remove().then(function (result) {
if (result) {
$scope.showAlert('User deleted successfully.', 'success');
var n = true;
angular.forEach(data, function (v, k) {
if ($scope.tmpUser.id === v.id) {
if (n) {
data.splice(k, 1);
$scope.tableParams.reload();
n = false;
}
}
});
}
});
});
};
$scope.tableParams = new ngTableParams({
page: 1,
count: 10
}, {
getData: function($defer, params) {
// getData gets called when you click on a different page in the pagination links.
// get the page number and size from params.$params
var pageNumber = params.$params.page;
var pageSize = params.$params.count;
// set up the query parameters as expected by your server
var mm = params.filter();
var queryParams = {"page_size":pageSize, "page_number":pageNumber, "sorting": params.sorting(), role: mm.role, id: mm.id, email: mm.email};
User.getCount().then(function (total) {
User.query(queryParams).then(function (result) {
params.total(total);
$defer.resolve(result);
});
});
}
});
Its important to note that the array you pass must in the format of [{id:'someId', title: 'SomeTitle'}, {...}]
Your select is not populated because you're not waiting for the promise to resolve.
Here's a plunker that I stumbled upon when I faced this issue a few months ago:
plnkr.co/edit/XJo9rp?p=preview
Try something like that:
function getData() {
return $q.when([{role: 'admin'},...]);
}
var promise = getData();
Which is the short version for:
function getData() {
var def = $q.defer();
var docType = [{role: 'admin'},...];
def.resolve(docType);
return def;
}
var promise = getData();
and your scope function:
$scope.roles = function(column) {
var select = promise.then(function(results) {
return results;
})
return select;
};

Factory not providing proper values to controller

In my angular js project factory is not providing values to the controller as needed. I always get empty result in view. When i logged in browser using console.log() all i can see in console is :
[object Object],[object Object],[object Object]. I am stuck at this. Tried many things but nothing worked.
This is my controller code:
var controllers = {};
controllers.ProductController = function ($scope, $route, $routeParams, $location, ProductFactory) {
$scope.products = [];
var init = function () {
$scope.products = ProductFactory.getProducts();
console.log('got products in controller');
console.log($scope.products)
};
var initProductEdit = function () {
var code = $routeParams.code;
if (code = undefined) {
$scope.currentProduct = {};
}
else
{
$scope.currentProduct = ProductFactory.loadProductByCode(code);
}
};
$scope.$on('$viewContentLoaded', function () {
var templateUrl = $route.current.templateUrl;
if (templateUrl == '/Partials/ProductEdit.html') {
initProductEdit();
}
else if (templateUrl == '/Partials/ProductList.html')
{
var code = $routeParams.code;
if(code!=undefined)
{
$scope.deleteProduct(code);
}
}
});
init();
$scope.saveProduct = function () {
ProductFactory.saveProduct($scope.currentProduct);
$location.search('code', null);
$location.path('/');
};
$scope.deleteProduct = function (code) {
ProductFactory.deleteProduct(code);
$location.search('code', null);
$location.path('/');
};
};
angSPA.controller(controllers);
This is my factory code:
angSPA.factory('ProductFactory', function () {
var products = [
{ code: 1, name: 'Game of Thrones', description: 'Series' }
{ code: 2, name: 'DmC', description: 'Game' },
{ code: 3, name: 'Matrix', description: 'Movie' },
{ code: 4, name: 'Linkin Park', description: 'Music Band' }];
var factory = {};
console.log('initializing factory');
factory.getProducts = function () {
console.log('factory now providing products');
return products;
};
factory.loadProductByCode = function (code) {
var product;
for (var i = 0; i < products.length; i++) {
if (products[i].code == code) {
product = products[i];
return product;
}
}
};
factory.saveProduct = function (product) {
products.push(product);
console.log('factory saved product');
};
factory.deleteProduct = function (code) {
var product = factory.loadProductByCode(code);
if (product != null) {
products.remove(product);
console.log('factory deleted product');
}
};
console.log('returning factory');
return factory;
});
This is my view:
<div class="container">
<h2 class="page-title">Product Listing</h2>
<div class="searchbar">
<ul class="entity-tabular-fields">
<li>
<label>Search: </label>
<span class="field-control">
<input type="text" data-ng-model="filter.productName" />
</span>
<label></label>
</li>
</ul>
</div>
<h2>Add New Product</h2>
<table class="items-listing">
<thead>
<tr>
<th>Code</th>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td data-ng-repeat="product in products|filter:filter.productName"></td>
<td>{{product.code}}</td>
<td>{{product.name}}</td>
<td>{{product.description}}</td>
<td>Delete</td>
</tr>
</tbody>
</table>
</div>
My routing function:
angSPA.config(function ($routeProvider) {
$routeProvider
.when(
'/',
{
controller: 'ProductController',
templateUrl: 'Partials/ProductList.html'
})
.when(
'/ProductEdit',
{
controller: 'ProductController',
templateUrl: 'Partials/ProductEdit.html'
})
.otherwise({
redirectTo: '/'
});
console.log('routing done');
});
Change your htmt given
var angSPA = angular.module('angSPA', []);
angSPA.controller("ProductController", function($scope, ProductFactory) {
$scope.products = [];
var init = function() {
$scope.products = ProductFactory.getProducts();
console.log('got products in controller');
console.log($scope.products + "")
};
init();
});
angSPA.factory('ProductFactory', function() {
var products = [
{code: 1, name: 'Game of Thrones', description: 'Series'},
{code: 2, name: 'DmC', description: 'Game'},
{code: 3, name: 'Matrix', description: 'Movie'},
{code: 4, name: 'Linkin Park', description: 'Music Band'}];
var factory = {};
console.log('initializing factory');
factory.getProducts = function() {
console.log('factory now providing products');
return products;
};
factory.loadProductByCode = function(code) {
var product;
for (var i = 0; i < products.length; i++) {
if (products[i].code == code) {
product = products[i];
return product;
}
}
};
factory.saveProduct = function(product) {
products.push(product);
console.log('factory saved product');
};
factory.deleteProduct = function(code) {
var product = factory.loadProductByCode(code);
if (product != null) {
products.remove(product);
console.log('factory deleted product');
}
};
console.log('returning factory');
return factory;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
<body ng-app="angSPA" ng-controller="ProductController">
<div class="container">
<h2 class="page-title">Product Listing</h2>
<div class="searchbar">
<ul class="entity-tabular-fields">
<li>
<label>Search: </label>
<span class="field-control">
<input type="text" data-ng-model="filter.productName" />
</span>
<label></label>
</li>
</ul>
</div>
<h2>Add New Product</h2>
<table class="items-listing">
<thead>
<tr>
<th>Code</th>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="prod in products|filter:filter.productName">
<td ></td>
<td>{{prod.code}}</td>
<td>{{prod.name}}</td>
<td>{{prod.description}}</td>
<td>Delete</td>
</tr>
</tbody>
</table>
</div>
Your ng-repeat directive should be on the tr element and not the td.
<tr data-ng-repeat="product in products|filter:filter.productName">
Not the cause of your problem, but to log in a service or controller, you can use the $log service and stringify to serialize your objects.
$log.debug(JSON.stringify($scope.products));
Looking at your code, you do $scope.products = [] right at the beginning. This will make angular watch the empty array.
In your init function you assign the products array to $scope.products. But as angular is still watching the initial array it will not be aware of the change.
The solution is to delete the initial assignment $scope.products = [] and make sure to alter the original array but never set it to a new array.
BTW: you could do console.log(JSON.stringify($scope.products)) to get better log information.

AngularJs: Creating a calender

I have this error when running the snippet below.
The code is actualy working, but there seem to be a problem.
angular.js:9037 Error: [$rootScope:infdig] http://errors.angularjs.org/undefined/$rootScope/infdig?p0=10&p1=%5B%5B%22f…5Ct%5C%5CtMi%5C%5Cn%20%20%20%20%5C%5Ct%5C%5Ct%5C%5Ct%5C%5Ct%5C%22%22%5D%5D
at Error (native)
at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js:6:453
at g.$get.g.$digest (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js:99:110)
at g.$get.g.$apply (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js:101:12)
at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js:17:415
at Object.d [as invoke] (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js:30:328)
at c (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js:17:323)
at Wb (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js:18:30)
at Oc (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js:17:99)
at HTMLDocument.<anonymous> (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js:198:494)
MINERR_ASSET:22 Uncaught Error: [$rootScope:infdig] http://errors.angularjs.org/undefined/$rootScope/infdig?p0=10&p1=%5B%5B%22f…5Ct%5C%5CtMi%5C%5Cn%20%20%20%20%5C%5Ct%5C%5Ct%5C%5Ct%5C%5Ct%5C%22%22%5D%5D
Please run snippet below for further understanding.
I get this error because of the day names im looping trough. When deleting following:
this.firstDay++;
if (this.firstDay == 6) {
this.firstDay = 0;
}
There is no error, but Only Monday is displayed
(function() {
var app = angular.module('myApp', []);
app.controller('CalenderController', function() {
this.days = dayNames;
this.date = date;
this.firstDay = 0;
this.getDays = function(num) {
return new Array(num);
};
this.getDayName = function() {
this.firstDay++;
if (this.firstDay == 6) {
this.firstDay = 0;
}
return dayNames[this.firstDay];
};
});
//Variables
var dayNames = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'];
var date = {
'2015': [{
'month': 'Januar',
'day': 31
}, {
'month': 'Februar',
'day': 28
}, {
'month': 'März',
'day': 31
}, {
'month': 'April',
'day': 30
}, {
'month': 'Mai',
'day': 31
}, {
'month': 'Juni',
'day': 30
}, {
'month': 'Juli',
'day': 31
}, {
'month': 'August',
'day': 31
}, {
'month': 'September',
'day': 30
}, {
'month': 'Oktober',
'day': 31
}, {
'month': 'November',
'day': 30
}, {
'month': 'Dezember',
'day': 31
}]
};
})();
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular.min.js"></script>
<div class="container" ng-app="myApp" ng-controller="CalenderController as calender">
<table ng-repeat="date in calender.date.2015" class="table table-striped table-bordered">
<thead>
<tr class="info">
<td colspan="{{date.day}}"> {{date.month}} </td>
</tr>
</thead>
<tbody>
<tr>
<td ng-repeat="_ in calender.getDays(date.day) track by $index">
{{calender.getDayName()}}
</td>
</tr>
<tr>
<td ng-repeat="_ in calender.getDays(date.day) track by $index">
{{$index + 1 }}
</td>
</tr>
</tbody>
</table>
</div>
Change this.firstDay to var firstDay i.e. javascript variable. The issue is while it is scope variable this.firstDay, angular internally place watch on it, and whenever any scope update occurs it run it digest cycle instantly, in you case this.firstDay is update exact 4015 times, that leads to an indefinite loop of digest cycle, and i don't think you are using this scope variable anywhere inside the controller. So better make it javascript var
Controller
app.controller('CalenderController', function(){
this.days = dayNames;
this.date = date;
var firstDay = 0;
this.getDays = function(num){
return new Array(num);
};
this.getDayName = function(){
firstDay++;
if(firstDay == 6){
firstDay = 0;
}
return dayNames[this.firstDay];
};
});
Working Plunkr
Hope this could help you. Thanks.

How to create in angular simple independent grid directive fully configured in controller?

I need to create independent grid directive in angular. Data and actions will be set in controller. Action can refer to other controllers or other action in parent controller. Both are possible.
Here is live example: http://plnkr.co/edit/efe4notoE1TyONH3W347?p=preview
in index.html
<body ng-controller="MainCtrl">
<div grid data="data" action-list="actionList" call="call(fun, row)"></div>
</body>
controller
app.controller('MainCtrl', function($scope) {
$scope.data = [
{id: 1, name: 'aaa'},
{id: 2, name: 'bbb'},
{id: 3, name: 'ccc'}
];
// actions (show, delete) have to be defined before $scope.actionList
$scope.show = function (row) {
alert('show: ' + row.id + ':' + row.name);
}
$scope.delete = function (row) {
alert('delete: ' + row.id);
}
$scope.actionList = [
{ label: 'edit', href: '#/edit/{{row.id}}', title: 'to other CTRL' },
{ label: 'show', click: $scope.show, title: 'use ngClick' },
{ label: 'delete', click: $scope.delete, title: 'use ngClick' }
];
// call any action in this CTRL
$scope.call = function (fun, row) {
fun(row);
}
});
grid directive. Parameter call will be used to pass all action functions to controller. hrefCompiled compile href of action (e.g. #/edit/{{row.id}})
app.directive('grid', function () {
return {
scope: {
data: '=',
actionList: '=',
call: '&'
},
//
controller: function ($scope, $interpolate) {
$scope.hrefCompiled = function (action, row) {
if (action.href) {
return $interpolate(action.href)({row: row})
}
return '';
}
},
templateUrl: 'grid.html'
}
});
grid.html
<table>
<tr>
<th ng-repeat="(key, value) in data[0]">{{key}}</th>
<th>actions</th>
</tr>
<tr ng-repeat="row in data">
<td>{{row.id}}</td>
<td>{{row.name}}</td>
<td>
<span ng-repeat="action in actionList">
<a href="{{hrefCompiled(action, row)}}"
ng-click="call({fun: action.click, row: row})"
title="{{action.title}}">{{action.label}}</a>
</span>
</td>
</tr>
</table>
Here is live example: http://plnkr.co/edit/efe4notoE1TyONH3W347?p=preview
May be this is not best practice, but it works. Please help me to improve it. Thanks.
You may refer or use one of those angular-table plugins: http://angular-ui.github.io/ng-grid/ or http://angulartable.com/
You can use ng-table directive allow to liven your tables. It support sorting, filtering and pagination. Header row with titles and filters automatic generated on compilation step.
Examples
Pagination
Sorting
Filtering
Cell template
Row template
Params in url
Ajax
Custom template(pagination)
Custom filters
Table with checkboxes

Resources