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>
Related
I am trying that on a button click, a div and and input tag are created and the input tag contain ng-model and the div has binding with that input.
Kindly suggest some solution.
You can create the div and input beforehand and and do not show it by using ng-if="myVar". On click make the ng-if="true".
<button ng-click="myVar = true">
In controller : $scope.myVar = false;
$scope.addInputBox = function(){
//#myForm id of your form or container boxenter code here
$('#myForm').append('<div><input type="text" name="myfieldname" value="myvalue" ng-model="model-name" /></div>');
}
Here is another solution, in which there's no need to create a div and an input explicitly. Loop through an array of elements with ng-repeat. The advantage is that you will have all the values of the inputs in that array.
angular.module('app', [])
.controller('AppController', AppController);
AppController.$inject = ['$scope'];
function AppController($scope) {
$scope.values = [];
$scope.add = function() {
$scope.values.push('');
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="AppController">
<button ng-click="add()">Click</button>
<div ng-repeat="value in values track by $index">
<input type="text" ng-model="values[$index]"/>
<div>{{values[$index]}}</div>
</div>
<pre>{{values}}</pre>
</div>
UPDATE. And if you want only one input, it's even simpler, using ng-show.
angular.module('app', []);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<button ng-click="show = true">Click</button>
<div ng-show="show">
<input type="text" ng-model="value"/>
<div>{{value}}</div>
</div>
</div>
You should use $compile service to link scope and your template together:
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', '$compile', '$document' , function MyCtrl($scope, $compile, $document) {
var ctrl = this;
var inputTemplate = '<div><span ng-bind="$ctrl.testModel"></span>--<span>{{$ctrl.testModel}}</span><input type="text" name="testModel"/></div>';
ctrl.addControllDynamically = addControllDynamically;
var id = 0;
function addControllDynamically() {
var name = "testModel_" + id;
var cloned = angular.element(inputTemplate.replace(/testModel/g, name)).clone();
cloned.find('input').attr("ng-model", "$ctrl." + name); //add ng-model attribute
$document.find('[ng-app]').append($compile(cloned)($scope)); //compile and append
id++;
}
return ctrl;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//code.angularjs.org/1.6.2/angular.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl as $ctrl">
<input type="button" value="Add control dynamically" ng-click="$ctrl.addControllDynamically()"/>
</div>
</div>
UPDATE: to add a new compiled template each time the button is clicked, we need to make a clone of the element.
UPDATE 2: The example above represents a dirty-way of manipulating the DOM from controller, which should be avoided. A better (angular-)way to solve the problem - is to create a directive with custom template and use it together with ng-repeat like this:
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', function MyCtrl($scope) {
var ctrl = this;
ctrl.controls = [];
ctrl.addControllDynamically = addControllDynamically;
ctrl.removeControl = removeControl;
function addControllDynamically() {
//adding control to controls array
ctrl.controls.push({ type: 'text' });
}
function removeControl(i) {
//removing controls from array
ctrl.controls.splice(i, 1);
}
return ctrl;
}])
.directive('controlTemplate', [function () {
var controlTemplate = {
restrict: 'E',
scope: {
type: '<',
ngModel: '='
},
template: "<div>" +
"<div><span ng-bind='ngModel'></span><input type='type' ng-model='ngModel'/></div>" +
"</div>"
}
return controlTemplate;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//code.angularjs.org/1.6.2/angular.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl as $ctrl">
<input type="button" value="Add control dynamically" ng-click="$ctrl.addControllDynamically()"/>
<div ng-repeat="control in $ctrl.controls">
<control-template type="control.type" ng-model="control.value"></control-template>
</div>
</div>
</div>
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
can someone please give me an idea on how will i set focus on my textbox when I click the radiobutton assigned to it? Im using angular js. Thanks for any idea
I created example for you.
HTML:
<body ng-app="scopeExample">
<div ng-controller="MyController">
<div ng-repeat='item in mas'>
<input type="radio" name='somename' ng-model="item.select" value="true" ng-click='clear(item)'>
<input type='text' focus='item.select'/>
<br>
</div>
</div>
</body>
Javascript:
angular.module('scopeExample', [])
.controller('MyController', ['$scope', function($scope) {
$scope.mas=[
{id:1,select:false},
{id:2,select:false},
{id:3,select:false},
{id:4,select:false}
];
$scope.clear = function(item){
$scope.mas.forEach(function(x){
if(x != item)
x.select=false;
});
}
}]).directive('focus',['$timeout', '$parse', function($timeout, $parse){
return {
link:function (scope, element, attrs) {
var model = $parse(attrs['focus']);
scope.$watch(model, function (value) {
if (eval(value))
$timeout(function () {
element[0].focus();
});
});
}
}
}]);
I will provide you pseudo code which you can alter on the basis of your needs. For now, the code has one radio button which when selected will focus on the input element. However, you can achieve those on list, on checkbox as per your need.
HTML:
<div ng-controller="MyCtrl">
<input type="radio" ng-model="focusRadio" value=0>
<input ng-model="name" focus>
</div>
Script:
var myApp = angular.module('myApp',[]);
myApp.directive('focus', function() {
return function(scope, element) {
scope.$watch('focusRadio',
function (newValue) {
newValue && element[0].focus()
})
}
});
function MyCtrl($scope) {}
Here is the plunker: http://plnkr.co/edit/ABHCRIm95EyNUI8nWczh?p=preview . Change the input type to checkbox to better know about the functionality.
<!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 start learning Angular and faced with some strange behaviour.
I want to add a new header to accordion dynamically but I accordion doesn't reflect it on UI till I explicitly click on some of his items. By some reason he doesn't react on items changes before it starts load itserlf aggain durnig DOM rendering.
var mainApp = angular.module('ui.bootstrap.demo', ['ui.bootstrap', 'ngResource']);
mainApp.factory('teamSharedObj',['$rootScope', function($rootScope) {
return {
teams: [],
peopleInTeam: [],
addNewTeam: function(item) {
console.log("add new team: " + item);
this.teams.push(item);
$rootScope.$broadcast('team.new');
},
addTeamMembers: function(team, teamMembers) {
for (var i = 0; i < teamMembers.length; i++) {
var temp;
// put in team as key-value pair
temp[team] = teamMembers[i]
console.log("add new team member: " + temp);
peopleInTeam.push(temp);
}
if (teamMembers.length != 0) {
$rootScope.$broadcast('teamMember.new');
}
}
}
}]);
mainApp.directive("addNewTeam", ['teamSharedObj', 'teamSharedObj', function (teamSharedObj) {
return {
restrict: 'A',
link: function( scope, element, attrs ) {
element.bind('click', function() {
console.log(scope.teamName)
teamSharedObj.addNewTeam(scope.teamName)
});
}
}
}])
mainApp.controller('teamListCtrl', ['$scope', 'teamSharedObj', function($scope, teamSharedObj) {
$scope.$on('team.new', function(event) {
console.log('new team ' + event);
$scope.items = teamSharedObj.teams;
}
);
$scope.oneAtATime = true;
$scope.items = ['new', 'another one'];//teamSharedObj.teams;
}]);
<!DOCTYPE html>
<html>
<head lang="en">
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script>
<script src= "http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-resource.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.2.js"></script>
<script src="js/test.team.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body ng-app="ui.bootstrap.demo">
<div id="teamBlock">
<input type="text" ng-model="teamName" >
<input add-new-team type="submit" value="Add new team" >
<!--<button add-book-button>Add data</button>-->
</div>
<div>
{{teamName}}
</div>
<div ng-controller="teamListCtrl">
<accordion close-others="oneAtATime" >
<accordion-group heading="{{d}}" ng-repeat="d in items">
This content is straight in the template.
</accordion-group>
</accordion>
<div ng-repeat="item in items">{{item}}</div>
</div>
</body>
</html>
Can you suggest me please a right way to notifu component about changes in its datasource?
bind is jqLite/jQuery method and does not automatically trigger the digest loop for you. This means no dirty checking will take place and the UI will not be updated to reflect the model changes.
To trigger it manually wrap the code in a call to $apply:
element.bind('click', function() {
scope.$apply(function () {
teamSharedObj.addNewTeam(scope.teamName);
});
});
And since teamSharedObj contains a reference to the array the controller can reference it directly. Then you do not need to use $broadcast:
addNewTeam: function(item) {
this.teams.push(item);
},
And:
mainApp.controller('teamListCtrl', ['$scope', 'teamSharedObj',
function($scope, teamSharedObj) {
$scope.oneAtATime = true;
$scope.items = teamSharedObj.teams;
}
]);
Demo: http://plnkr.co/edit/ZzZN7wlT10MD0rneYUBM?p=preview