Generate directive content dynamically on ngclick - angularjs

I have one table. in that i have declared my custom directive
<table ng-show="dataset.length" ng-table="tableParams" class="table">
<thead>
<tr>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="server in $data" ng-class-odd="'odd-row'" ng-class-even="'even-row'">
<td width="30" class="text-center">
<i class="ion-plus-round **toggle-icon**" group-row></i>
</td>
</tr>
</tbody>
</table>
While clicking on toggle-icon class
i need to generate one more tr data in next row.
custom directive is
app.directive('groupRow', function(){
return {
restrict: 'EA',
transclude: true,
controller: 'groupRowDirCtrl',
templateUrl: 'views/directives/templates/group-row.html',
link: function( scope, element, attrs, groupRowDirCtrl ) {
element.bind('click', function() {
$compile(el)(scope);
element.parent().parent().after(el);
});
}
};
})
.controller('scrollableTableviewDirCtrl',
function($scope) {
});
data have to fetch it from html page and append into next row.
How to do this?

if i understand you correctly, the on click function is in the directive.
so, i would add a service for the $data array that is being repeated, and on click add another item to that array by using the service.
like so
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, dataService) {
dataService.setData([1, 2, 3]);
$scope.data = dataService.getData();
});
app.directive('dir', function(dataService) {
return {
restrict: 'E',
replace: true,
template: '<tr><td ng-click="addMore()">One More</td></tr>',
link: function($scope, elem, attrs) {
$scope.addMore = function() {
dataService.addRow();
}
}
}
});
app.service('dataService', function() {
var _data = [];
var _service = {};
var _cb;
_service.getData = function() {
return _data;
}
_service.setData = function(data) {
_data = data;
}
_service.onUpdate = function(cb) {
_cb = cb;
}
_service.addRow = function( /* attibutes here */ ) {
_data.push({});
if (angular.isFunction(_cb)) _cb();
}
return _service;
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myCtrl">
<table>
<tbody>
<dir ng-repeat="d in data"></dir>
</tbody>
</table>
</div>
</div>

Related

Enable Button After Page Load in AngularJS Directive

I am trying to enable a button after page load in an AngularJS directive. I applied ng-disabled for all my buttons DURING load and I would like to keep certain buttons disabled AFTER load.
I need some direction/advice on:
manipulating the DOM: from ng-disabled="!newAnimal.isDisabled" to ng-disabled="newAnimal.isDisabled"
I appreciate the help. Thanks.
HTML:
<a href="#/{{animal.id}}">
<button class="btn btn-success" ng-disabled="!newAnimal.isDisabled" id="add-animal" loading-animals>
Add Animal
</button>
</a>
FACTORY:
var animalFactory = angular.module('app.myFactory',[])
animalFactory.factory('newAnimal',function(){
var newAnimal = function(){
this.animal = "";
this.totalAnimals = 0;
this.totalAdopted = 0;
this.isDisabled = false;
};
return newAnimal
});
CONTROLLER (Modal):
.controller('InformationCtrl', function($scope, $modalInstance, $http) {
$scope.ok = function(){
//check if button successfully clicked
$modalInstance.dismiss('success');
//the code below was from a directive ('LoadingAnimals') that I was working on
//check if all data has been loaded from backend
var loadingPage = function(){
return $http.pendingRequests.length > 0;
//when all objects are loaded, manipulate DOM
//make ng-disabled = "!isDisabled" to "isDisabled"
element.attr("!newAnimal.isDisabled", "newAnimal.isDisabled);
}
loadingPage();
}
DIRECTIVE:
app.directive('loadingAnimals', ['$http', function($http) {
return {
restrict: 'A',
link: function (scope, element, attrs) {

var addButton = attrs.ngDisabled;
//console.log(element.attr('ng-disabled'));
scope.pageLoad = function() {
return $http.pendingRequests.length > 0;
};
scope.$watch(scope.pageLoad(), function (value) {
if (value) {
element.attr("ng-disabled", "newAnimal.isDisabled");
}
else {
element.removeAttr("ng-disabled");
}
})
}
}
}]);
UPDATE:
I updated my directive and it works, not the best way of achieving the results but it's one way.
(I would have preferred not to disable the button for 3 seconds but rather to listen to the $http request but since it's a workaround, I won't complain)
Thanks for all the answers. I'll update in the future if I figure out a more efficient way.
DIRECTIVE:
.directive('loadingAnimals', function() {
return {
restrict: 'A',
link: function (scope, element) {
var disableLink = (function() {
element.removeClass('disabled');
});
setTimeout(disableLink, 3000);
}
}
}
]);

Not sure if I'm correct but to do something after page is completely load, you can use angular.element(document).ready() (as you can see in this answer).
So you can have a <button> structured like this:
<button type="button" class="btn btn-primary" ng-disabled="!isDisabled || !animal.totalAnimals">Add animal</button>
See the example below:
(function() {
'use strict';
angular
.module('app', [])
.controller('MainCtrl', MainCtrl);
function MainCtrl($scope) {
var vm = this;
vm.animals = [
{
"name":"hamster",
"totalAnimals": 20,
"totalAdopted": 5,
},
{
"name":"turtle",
"totalAnimals": 0,
"totalAdopted": 0,
},
{
"name":"cat",
"totalAnimals": 9,
"totalAdopted": 6,
},
{
"name":"dog",
"totalAnimals": 7,
"totalAdopted": 2,
},
{
"name":"tiger",
"totalAnimals": 0,
"totalAdopted": 0,
}
];
vm.isDisabled = true;
angular.element(document).ready(function () {
console.log('completely load!');
vm.isDisabled = false;
});
}
})();
<!DOCTYPE HTML>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body ng-controller="MainCtrl as main">
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>#of Animals Added</th>
<th>#of Animals Adopted</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="animal in main.animals track by $index">
<td ng-bind="animal.name"></td>
<td ng-bind="animal.totalAnimals"></td>
<td ng-bind="animal.totalAdopted"></td>
<td>
<button type="button" class="btn btn-primary" ng-disabled="!main.isDisabled || !animal.totalAnimals">Add animal</button>
</td>
</tr>
</tbody>
</table>
</body>
</html>
I hope it helps.
You can also use a directive to make changes post page load
<button id="submit" ng-click="test()">Submit</button>
<disable-button></disable-button>
This is how the directive will look like
.directive('disableButton', function () {
return {
restrict: 'E',
compile: function (el, attr) {
return {
pre: function (scope, el, attr) {
},
post: function (scope, el, attr) {
$('#submit').attr("disabled", true);
//add your logic here
}
}
}
}
})
Refer the demo

Append Data to existing json array through Angular-Bootstrap dialog modal

I am facing issue while adding data through Angular Dialog modal
Here My plunker
http://plnkr.co/edit/EJpkmXqNAcuN3GJiLvAL?p=preview
<table class="table table-bordered">
<thead>
<tr>
<th>
<input type="checkbox" ng-model="isAll" ng-click="selectAllRows()"/>ALL
</th>
<th>
ID
</th>
<th>
NAME
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in data" ng-class="{'success' : tableSelection[$index]}">
<td>
<input type="checkbox" ng-model="tableSelection[$index]" />
</td>
<td>{{row.id}}</td>
<td>{{row.name}}</td>
</tr>
</tbody>
</table>
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<h3 class="modal-title">Im a modal!</h3>
</div>
<form name = "addFriendForm">
<input ng-model = "user.id"class="form-control" type = "text" placeholder="id" title=" id" />
<input ng-model = "user.name"class="form-control" type = "text" placeholder="name" title=" name" />
</form>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
</script>***strong text***
Here Script.js
While trying get data from dialog modal it was not coming
Can you please any one help me out of this problem
var app = angular.module('myapp', ['ui.bootstrap']);
app.controller('MainCtrl', function($scope,$modal,$log) {
$scope.user = {id: "",name:""}
$scope.data = [{
id: 1,
name: 'Name 8'
}, {
id: 2,
name: 'Name 7'
}];
$scope.tableSelection = {};
$scope.isAll = false;
$scope.selectAllRows = function() {
//check if all selected or not
if ($scope.isAll === false) {
//set all row selected
angular.forEach($scope.data, function(row, index) {
$scope.tableSelection[index] = true;
});
$scope.isAll = true;
} else {
//set all row unselected
angular.forEach($scope.data, function(row, index) {
$scope.tableSelection[index] = false;
});
$scope.isAll = false;
}
};
$scope.removeSelectedRows = function() {
//start from last index because starting from first index cause shifting
//in the array because of array.splice()
for (var i = $scope.data.length - 1; i >= 0; i--) {
if ($scope.tableSelection[i]) {
//delete row from data
$scope.data.splice(i, 1);
//delete rowSelection property
delete $scope.tableSelection[i];
}
}
};
$scope.addNewRow = function() {
//set row selected if is all checked
$scope.tableSelection[$scope.data.length] = $scope.isAll;
$scope.data.push({
id: $scope.data.length,
name: 'Name ' + $scope.data.length
});
};
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
resolve: {
data: function () {
return $scope.data;
}
}
});
modalInstance.result.then(function (data) {
$scope.user = data;
$scope.data.push($scope.user);
// $scope.data.push({'id':$scope.data.id,'name':$scope.data.name});
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
});
angular.module('myapp').controller('ModalInstanceCtrl', function ($scope, $modalInstance, data) {
$scope.ok = function () {
$modalInstance.close($scope.user);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
You should create $scope.user object in your ModalInstanceCtrl and add $scope.user in your $modalInstance.close like this:
angular.module('myapp').controller('ModalInstanceCtrl', function ($scope,$modalInstance) {
$scope.user = {};
$scope.ok = function () {
$modalInstance.close($scope.user);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
I've checked this in your plunker, so it works

angularjs directive doesn't update when scope value update

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<script type="text/javascript">
(function(){
'use strict';
angular.module('myApp',[])
.controller('TestCtrl',TestCtrl)
.directive('trxTablePersonTd',trxTablePersonTd);
function TestCtrl($scope){
var vm = $scope.vm = this;
vm.trxs = [
{id:"1",acctKey:"2",persons:[{name:'peter',age:20},{name:'hank',age:23}]},
{id:"2",acctKey:"3",persons:[{name:'Joe'},{name:'Jason'}]}
];
$scope.changePerson = function(){
vm.trxs[1]['persons']['age'] = 33;
vm.trxs[1]['acctKey'] = 123;
}
}
function trxTablePersonTd($compile){
return{
scope:{persons:'=persons'},
restrict:'A',
link:link,
replace:false,
//compile:compile
}
function compile(elem,attrs){
return function(scope){
var html = [];
scope.persons.map(function(person,index){
html.push('<td>'+person.name+'</td>');
html.push('<td>'+person.age+'</td>');
});
}
}
function link(scope, elem, attrs){
var html = [];
if(scope.persons){
scope.persons.map(function(person,index){
html.push('<td>'+person.name+'</td>');
html.push('<td>'+person.age+'</td>');
});
}
elem.replaceWith(html.join(''));
$compile(elem.contents())(scope);
}
}
}());
</script>
</head>
<body ng-app="myApp" ng-controller="TestCtrl">
<button type="button" name="button" ng-click="changePerson()">change person</button>
<table border="1">
<tbody>
<tr ng-repeat="trx in vm.trxs">
<td>
{{trx.id}}
</td>
<td>
{{trx.acctKey}}
</td>
<td trx-table-person-td persons="trx.persons">
</td>
</tr>
</tbody>
</table>
</body>
</html>
Blockquote
when I click the button the undefined age doesn't get update. can someone help me look at this problem
when I click the button the undefined age doesn't get update. can someone help me look at this problem
You didn't properly update your collection in $scope.changePerson method. persons is also an array, it should like
$scope.changePerson = function () {
vm.trxs[0]['persons'][0]['age'] = 33; //look ['persons'][0]
vm.trxs[0]['acctKey'] = 123;
}
As #Ed Staub suggest you need to $watch your collection to propagate further model change like
app.directive('trxTablePersonTd', function ($compile) {
return {
scope: { persons: '=persons' },
restrict: 'A',
link: link,
replace: false,
}
function link(scope, elem, attrs) {
if (scope.persons) {
scope.$watch('persons', function () {
var html = scope.persons.map(function (person, index) {
return '<td>' + person.name + '</td>' + '<td>' + (person.age ? person.age : "") + '</td>';
});
elem.empty().append(html.join(''));
$compile(elem.contents())(scope);
}, true);
}
}
});
Also if you don't have additional html except td in your directive, i think you don't need directive at all, just use ng-repeat like
<td ng-repeat="person in trx.persons">
<span>{{person.name}}</span>
<span>{{person.age}}</span>
</td>
I've been waiting to see if someone would answer this better and more authoritatively than I can. I believe the problem is because scope updates are not watched as deeply into data structures as your changes are. I think you need to implement a deep watch ("watch by value") on vm.trxs. See https://docs.angularjs.org/guide/scope, scroll down to section on "Controllers and Scopes".

Angularjs, set focus on an input in a table

I have a table with input in each cell, I can add records to that table by clicking on a button.
I would like to set focus on the first input of the last record created.
I don't know if that's possible.
If anyone can help me on this...
function Ctrl($scope) {
$scope.adresses = [];
$scope.add = function() {
var adr = {ville:null, codePostal:null}
$scope.adresses.push(adr);
};
}
<!doctype html>
<html ng-app>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="Ctrl" >
<button ng-click="add()"> Add </button>
<table>
<tr>
<th>ville</th>
<th>adresse</th>
<tr>
<tr ng-repeat="adresse in adresses">
<td>
<input ng-model="adresse.ville"/>
</td>
<td>
<input ng-model="adresse.codePostal"/>
</td>
<tr>
</table>
</div>
</body>
</html>
Try this approach.
Controller
controller('AppCtrl', function ($scope, $timeout, $element) {
$scope.adresses = [];
$scope.add = function() {
var adr = {ville:null, codePostal:null}
$scope.adresses.push(adr);
$timeout(function () {
$element[0].querySelector('[is-last=true]').focus();
})
};
});
Markup
<input ng-model="adresse.ville" is-last="{{$last}}"/>
Working Plnkr
Yes, very easily doable with a directive:
My example with your code: http://plnkr.co/edit/aDNdjjBKZHVfTXnLy2VZ?p=preview
// the directive I use
.directive('focusOnMe', ['$timeout', '$parse',
function($timeout, $parse) {
return {
//scope: true, // optionally create a child scope
link: function(scope, element, attrs) {
var model = $parse(attrs.focusOnMe);
scope.$watch(model, function(value) {
// console.log('value=',value);
if(value === true) {
$timeout(function() {
element[0].focus();
});
}
});
}
};
}
]);
The HTML: the condition for focus is a boolean value, here it is whether the element is last. So each time you add a new row, the condition is re-evaluated and focus is assigned.
<td>
<input ng-model="adresse.ville" focus-on-me="$last"/>
</td>

angular directive for tables: bind transcluded elements with ng-bind-html

I'm trying to write a generic table directive like this:
<h-table rows="customers">
<h-column field="Id">
<a ng-click="editCustomer(row.Id)">{{row.Id}}</a>
</h-column>
<h-column field="Name">
</h-column>
</h-table>
That will generate the following html:
<table>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
<tr>
<td>
<a ng-click="editCustomer(1)">1</a>
</td>
<td>
Alexandre
</td>
</tr>
...
</table>
My h-table template is something like:
<script type="text/ng-template" id="hTableTemplate.html">
<div>
<div ng-transclude id="limbo" style="display: none"></div>
<table>
<tr>
<th ng-repeat="col in cols">{{col.field}}<th>
</tr>
<tr ng-repeat="row in rows">
<td ng-repeat="col in cols">
// Here I need to put the content of h-column directive
// if it exists, or just bind the value for the column
<span ng-bind-html="getContentOrValueFor(row, col)" />
</td>
</tr>
<table>
</div>
</script>
So I need to create two directives: h-table and h-column. The h-table directive uses a directive controller, that will be used by both directives.
The h-column directive will use this controller to add cols to the table and get value of a row/col.
So far, this is my directive's controller:
.controller("hTable", function ($scope, $element, $attrs, $compile) {
$scope.cols = [];
this.addCol = function (col) {
$scope.cols.push(col);
};
$scope.getContentOrValueFor = function (row, col) {
// MY PROBLEM IS HERE! I will explain below ...
return col.content && col.content.html() ? col.content.html() : row[col.field];
};
})
My h-column directive receives h-table's controller.
It uses transclude to get it content and save this content inside col object, to bind it after:
.directive("hColumn", function () {
return {
restrict: "E",
require: "^hTable",
transclude: true,
scope: {
field: "#",
},
link: function(scope, element, attrs, hTableController, transclude) {
var col = {};
col.field = scope.field;
col.content = transclude(); // <-- Here I save h-column content to bind after
hTableController.addCol(col);
...
}
};
})
And finally :) my h-table directive:
.directive("hTable", function () {
return {
restrict: "E",
scope : {
rows: "="
},
controller: "hTable",
require: "hTable",
replace: true,
transclude: true,
templateUrl: "hTableTemplate.html",
link: function(scope, element, attrs, hTableController) {
...
}
};
})
I need to put h-column's content inside the td tag. So, I call getContentOrValueFor function to get this content that was inside h-column's directive.
If there is no content, so I bind with the value for the field.
It works normally if the h-column's content is something like {{1+2+3}} (it will show me 6, that's ok).
But if this content is an html tag like:
test
I get the error "html.indexOf is not a function"
How can I achieve this??
Thats caused by not including ngSanatize i think. See : https://docs.angularjs.org/api/ng/directive/ngBindHtml

Resources