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
Related
How in controller can I call function clearCopy from directive?
This is part of my html:
<tr ng-form="mForm" my-directive>
<td>
<div>
<button class="btn btn-default" ng-click="saveData(row)"> </button>
</div>
</td>
</tr>
This is my Directive:
angular.module("w.forms").directive("myDirective", function () {
return {
require: ["^form"],
link: function (scope, element, attrs, ctrls) {
scope.$watch(function () {
// ...... something
}, true);
scope.clearCopy = function () {
// do something
}
}
};
});
This is my Controller:
angular.module("app").controller("datalesController", function ($scope) {
$scope.saveData(row) = function {
// do something then run function from directive
// till this part everything works fine
$scope.clearCopy() // unfortunately it doesn't work :(
}
}
Everything works fine, except function $scope.clearCopy() in controller doesn't work.
HTML
<html>
<script src="library/angular.min.js"></script>
<script src="practice.js"></script>
<head>
</head>
<body ng-app="app" ng-controller="datalesController">
<div my-directive>
<button ng-click="saveData()">press </button>
</div>
</body>
</html>
controller
angular.module('app',[]).controller("datalesController", function ($scope) {
$scope.saveData = function() {
// do something then run function from directive
// till this part everything works fine
$scope.clearCopy(); // unfortunately it doesn't work :(
};
});
Directive
angular.module('app',[]).directive("myDirective" , function () {
return {
restrict:'A',
link: function (scope, element, attrs, ctrls) {
scope.clearCopy = function () {
console.log("calling from controller");
};
}
};
});
I change your code for running your request
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>
<!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".
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>
So I am trying to make a directive that is encapsulated inside a Bootstrap popover. Almost everything is working - I have event listeners to know when buttons get clicked, links, everything is hunkey-dory - except for the fact that even though I am using ng-model to bind to the input in the innermost directive, nothing is getting updated. It's almost as if it's doing a one-way binding instead of a two-way. FieldValue on the model is always null (its initial state), and is never altered (even though it should be equal to whatever was put in the input field). I have looked through a ton of answers and nothing seems to address the issue.
Here is my code:
Page HTML
<table class="table table-bordered table-striped">
<tbody>
<tr ng-repeat="field in model.CustomFields">
<td>{{field.Title}}</td>
<td>
<a popover field-description="field.CustomField.Description" model-value="field.FieldValue" class="trigger">
<text-custom-field text-model="field"></text-custom-field>{{field.FieldValue || 'Empty'}}
</a>
</td>
</tr>
</tbody>
</table>
Popover Directive
(function() {
'use strict';
angular
.module('app.directives')
.directive('popover', popover);
popover.$inject = ['$compile'];
function popover($compile) {
var directive = {
link: link,
restrict: 'A',
scope: {
fieldDescription: '=',
modelValue: '='
}
};
return directive;
function link(scope, element, attrs) {
$(element).popover({
html: true,
placement: 'top',
title: scope.fieldDescription,
content: function () {
return $(this).parent().find('.js-popover-content').html() + '<button ng-click="submit()" type="submit" class="btn btn-primary btn-sm" style="margin-right: 5px;"><i class="glyphicon glyphicon-ok"></i></button><button type="button" class="btn btn-default btn-sm edit-cancel" ng-click="closePop()" style="margin-right: 5px;"><i class="glyphicon glyphicon-remove"></i></button>';
}
});
scope.closePop = function () {
console.log('close');
$('.trigger').popover('hide');
};
scope.submit = function () {
console.log('submit');
scope.$parent.$broadcast('submit');
};
scope.$parent.submitData = function (value) {
scope.modelValue = value;
console.log('submitted data');
scope.closePop();
};
$(element).on('click', function (e) {
$('.trigger').not(this).popover('hide');
$compile($('.popover.in').contents())(scope);
});
}
}
})();
Inner Directive
(function() {
'use strict';
angular
.module('app.directives')
.directive('textCustomField', textCustomField);
function textCustomField() {
var directive = {
templateUrl: 'url/templates/TextCustomField.html',
restrict: 'E',
transclude: true,
link: link,
scope: {
textModel: '='
}
};
return directive;
function link(scope, element, attrs) {
scope.$parent.$on('submit', function (event) {
console.log('heard submit event');
var value = scope.textModel.FieldValue;
console.log(value);
scope.$parent.submitData(value);
});
}
}
})();
Directive HTML
<div class="js-popover-content hide">
<div class="form-group" style="display: inline-flex;">
<input type="text" class="form-control" ng-model="textModel.FieldValue" style="width: {{textModel.CustomField.DisplaySize}}; margin-right: 5px;">
</div>
</div>
Does anyone have any ideas why this isn't working?