I'm using bootstrap and angular to build an application. In a HTML page, i'm using this:
<div class="btn-group-justified" data-toggle="buttons-checkbox">
<button type="button" class="btn fzbtn-consServices" ng-repeat="service in availability.services">{{service.name}}</button>
</div>
It's building a button group with dynamic values. Is there a practical way to obtain the selected buttons inside this button group?
I already tried some solutions, some of them are working but I don't know if it's the best way...
1: On "ng-click" method I would change a attribute value (eg. "checked" attribute) of each service to true or false;
2: I searched about any html attribute for btn-group which could offer me all the selected buttons inside this group, but i had no success;
3: I heard that i could beat this problem using Angular Filter, but i didn't find any similar example;
Anyone with a better idea? Thanks so much :)
This is the best solution I found until now:
<div class="btn-group-justified" data-toggle="buttons-checkbox">
<button type="button" class="btn fzbtn-consServices" ng-repeat="service in availability.services" ng-click="onClickService(service)" ng->{{service.name}}</button>
</div>
Controller:
$scope.onClickService = function (service) {
if (service.checked) {
service.checked = "false"
} else {
service.checked = "true";
}
}
Answer: Bootstrap UI
I feel your pain. Bootstrap is not always angular-friendly. But there is a good solution:
The easiest (and by far the cleanest) approach is to use Bootstrap UI. Built by the Angular Team, it is a rewrite of the javascript-portion of Bootstrap but for an Angular-friendly usage. Here's the section about buttons: http://angular-ui.github.io/bootstrap/#/buttons
Example solution: Checkbox button behavior
In this solution, the initial services array is used to store a boolean field 'selected' to know if any particular service is selected or not. (Similar to the "checked" in the question). This field is 2-way bounded to the checkbox state. Clicking the checkbox changes the field and changing the field changes the checkbox state.
var app = angular.module('plunker', ['ui.bootstrap']);
app.controller('MainCtrl', function($scope) {
var services = [
{ name:'Service A' },
{ name:'Service B', selected:true },
{ name:'Service C' },
{ name:'Service D', selected:true }
];
$scope.availability = { services:services };
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS BootStrap UI radios</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.2.js"></script>
<script src="app.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body ng-controller="MainCtrl">
<div class="btn-group-justified">
<label ng-repeat="service in availability.services" class="btn btn-primary" ng-model="service.selected" btn-checkbox>{{service.name}}</label>
</div>
<div ng-repeat="service in availability.services">{{service.name}} <span ng-show="service.selected">- selected</span></div>
</body>
</html>
Radio button behavior
I've included a solution for a "single selection" checkbox also known as a "radio-button". The "current selection" is bound to a variable on the scope. It will get updated automatically when the user picks an element. Setting it will, in turn, change the current selection.
var app = angular.module('plunker', ['ui.bootstrap']);
app.controller('MainCtrl', function($scope) {
var services = [
{ name:'Service A' },
{ name:'Service B' },
{ name:'Service C' },
{ name:'Service D' }
];
$scope.availability = { services:services };
$scope.model = {};
// Here we are using the service object instance as the "selection value".
// Depending on what you need, you could also use some sort of identifier or
// even the $index if that's more useful.
// Immediately select the second one.
$scope.model.selectedService = services[1];
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS BootStrap UI radios</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.2.js"></script>
<script src="app.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body ng-controller="MainCtrl">
<div class="btn-group-justified">
<label ng-repeat="service in availability.services" class="btn btn-primary" ng-model="model.selectedService" btn-radio="service">{{service.name}}</label>
</div>
<div>Current Selection is: {{model.selectedService.name}}</div>
</body>
</html>
NOTE: I used <label> instead of <button>, but I did not have your special CSS styles so it wasn't behaving on the screen, functionality-wise it works equally well with <button> elements.
You don't really specify why you are wanting to "get" the buttons. In general, getting a reference to DOM elements like this is not the "angular way." It is generally better to find a way to use angular's data-binding to manipulate UI elements.
For instance, if you want to show or hide buttons based on data or on another UI event, then use ng-show or ng-hide bound to a property of the service object you are binding these buttons to. Then you can update the object's property to change the UI. You should be able to find a similar way to make other changes (like setting classes, attributes, etc.) with angular's data-binding rather than doing it manually.
Related
Im using AngularJS 1.5x
I have a Web Form to submit Car information 1)model, 2)make to Backend(Api: SaveCarDetails).to save this form information in the Backend DB.
On success, I have to make a API call(GetCarsInfo) to get the array list of cars information(so far saved in the BE).
I need to display car information Cards showing the make & model and with EDIT button so that user can update exactly that particular car information and we use (SaveCarDetails same API as used for adding new details).
On the succesfull update, I need to again make to call to (GetBanks) to get the updated list of bank account information.
Now I need to show Tickmark Icon ONLY on the Car info card that has been updated or newly added.
What is the best way to do this? (show the icon only on the card that got updated or newly added).
How to find out exactly which card is updated by comparing or watching an Object? there is no unique identifier that is being passed for any updates to the response object.
I tried to do an $watch on the object but I couldn't get it to work.
Here is the code: http://plnkr.co/edit/RztTjy?p=preview
// Code goes here
angular.module('app', [])
.controller('MyCtrl', function($scope) {
var vm = this;
vm.carobj = [{
'make': 'acura',
'model': "TL",
'name':'joe'
},
{
'make': "bmw",
'model': '5series',
'name':'doe'
}];
console.log(vm.carobj);
});
/* Styles go here */
.card{
border: 10px solid #cccccc;
width: 200px;
height:160px;
margin:20px;
position:relative;
}
.tickWhenUpdated{
float:right;
position:absolute;
right:0;
bottom:0;
}
.editbtn{
width:100px;
}
<!DOCTYPE html>
<html>
<head>
<link data-require="font-awesome#4.3.0" data-semver="4.3.0" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" />
<link data-require="bootstrap#4.0.5" data-semver="4.0.5" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" />
<script data-require="bootstrap#4.0.5" data-semver="4.0.5" src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/js/bootstrap.min.js"></script>
<script data-require="angularjs#1.5.7" data-semver="1.5.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app">
<h1>Hello Plunker!</h1>
<div ng-controller="MyCtrl as mc">
<div class="card" ng-repeat="car in mc.carobj">
<p>Car Info Box </p>
<i class="fa fa-check tickWhenUpdated" aria-hidden="true"></i>
Account num:{{car.make}} <br />
Routung num: {{car.model}} <br />
<input value="edit" class="btn btn-primary editbtn" type="button" />
</div>
</div>
</body>
</html>
First of all, instead of dynamically updating the class using ng-class or something, you could just set a variable on the car objects called updated that is truthy when the car was updated. Then place ng-show="car.updated" on the tag.
Regarding how to set that updated variable on the object, you can either try to manipulate it directly when you click the edit button. Just pass the element to a function which will modify the updated field. Something like;
Html:
<div ng-repeat="car in cars">
<input type="button" ng-click"update(cars, car.id)">
</div>
Js:
$scope.update = function(cars, id) {
cars = cars.map(function(car) {
if (car.id == id) {
car.updated = true
} else {
car.updated = false
}
})
}
If you absolutely don't have an ID, the only thing I can think of is to put something that increments on the object (unix timestamp, incrementor). Something like
Js:
$scope.itr = 0;
$scope.update = function(car) {
$scope.itr++
car.itr = $scope.itr
}
Then have an ngChange on the array that calls a function and sets car.updated to false on all objects except the one with the largest itr value, which it sets to true.
This is a hardcore hack, and I would really try to just have some sort of unique ID.
I have a pseudo code like this where using the one-way-binding operator(::) I tried to see if angular is watching for changes. So I had to include it inside an input tag. the model data inside the input tag should be resolved one way because of :: before it. However if I make changes to the input and click the button to see the changes it reflects the changes in the log. But it should not watch for the changes.
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-beta.2/angular-animate.js"></script>
</head>
<body class="container" ng-controller="ItemsController">
<ul ng-repeat="item in ::items">
<li>
<!-- in actual code the input will not be included -->
<input type="text" ng-model="::item.name"> {{ ::item.name }}
<!-- actual code -->
<!-- {{ ::item.name }} -->
</li>
</ul>
<button type="btn" ng-click="click()">Button</button>
<script>
angular.module('app', [])
.controller('ItemsController', function ($scope) {
$scope.items = [
{name: 'item 1'},
{name: 'item 2'},
{name: 'item 3'}
];
$scope.click = function () {
for (var obj of $scope.items) {
console.log(obj.name);
}
};
})
</script>
</body>
</html>
A couple of things.
Is one time, no one way bonding. Is useful when you want an expression to be evaluated only once and not watching for changes.
The :: in ng-model is doing nothing, it will still update the scope with the value that you put and update the item name.
At the same time the {{ ::item.name}} should remain unchanged, because is one time binding and it won't watch for additional changes.
So you will see the changes in the log, because the value actually changes, what will not change is the view.
Ok, I've been stuck here for a while, and I'm sure it's something relatively dumb
http://plnkr.co/edit/YcBnbE5VCU5rizkDWreS?p=preview
<head>
<link href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css" rel="stylesheet" type="text/css" />
<link href="http://twitter.github.com/bootstrap/assets/css/bootstrap-responsive.css" rel="stylesheet" type="text/css" />
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.3/angular.min.js"></script>
<script >
function myCtrl($scope, $window) {
$scope.vm = {};
$scope.vm.Courses = [
{ Id: 1, Name: "Course 1"},
{ Id: 2, Name: "Course 2"}
];
$scope.OpenCourse = function(courseId) {
$window.alert("Called " + courseId);
}
}
</script>
</head>
<body ng-controller="myCtrl">
<div>
<div ng-repeat="course in vm.Courses" ng-click="vm.OpenCourse(course.Id)">
<div>
<div>
<label>{{course.Name}}</label>
</div>
</div>
</div>
</div>
</body>
Why isn't ng-click firing here? It seems that this question is asked a lot, but none of the answers seem to help. It also looks like moving the div out of the repeat makes it work, but again, I'm not sure why.
Thanks
Remove vm.
Result:
<div ng-repeat="course in vm.Courses" ng-click="OpenCourse(course.Id)">
Why?, because everything you set to $scope becomes available on the partial, then you just have to call it.
The lack of "vm" in front of OpenCourse(course.Id) was actually a typo on my part when creating the plunker. I've marked an answer as correct, since it did cause the plunker to work, but my problem wasn't this. It turned out that I had a class assigned to outer div that was changing the z-index, putting that div "behind" the others, and not allowing the click to propagate.
If you use <label> you might experience weird behaviour when clicking on it. Try changing it to something else if possible and re-test your code.
Change it to :
ng-click="OpenCourse(course.Id)"
Working plunker
I'm trying to set a tab as active through the markup. For some reason when I set the active attribute on a tab it seems to mangle the state of the tabs. The page loads up fine and the tab that was set as active will be deactivated when clicking another tab. When I click back on the tag that was set with active="true" the previously selected tab will not be deselected.
...
<tab heading="Dynamic Title 1" active="true">Some Title 1</tab>
...
http://plnkr.co/edit/xzDbezXgkSMr6wokov6g?p=info
I switched to creating a variable that is set to true at init and plopped that into the active attribute. I'm hoping there's a better way to this though.
<tabset ng-init="startActive = true">
...
<tab heading="Dynamic Title 1" active="startActive">Some Title 1</tab>
...
</tabset>
http://plnkr.co/edit/mt5MQSZEl730fsMuMxg8
I don't want to define the tabs in js because this is a project that uses webforms and piping data from that to js might be worse than what I'm doing here. I change the page to be completely built with angular in which case piping data like the tab to be selected could be part of some config endpoint that would be hit on the controller's init. I'd rather not have to redesign a complete page to make this change but it seems like the most correct way to go. Any thoughts and tips would be appreciated.
I know this is quite old, but after wasting hours of my life, I came up with a super dirty hack, that does the trick (assuming I understood your question correctly and you have the same issue as me).
Problem Statement
Using UI Bootstrap Tabs, dynamically adding tabs based on list data and maintaining the active state outside of this data.
When using the Tabs of UI Bootstrap and generating tabs like this:
<tab ng-repeat="item in page.data.list" active="item.active">
UI Bootstrap will use the binding of the item to store the active state. If we omit the active attribute, UI Bootstrap will maintain it internally but then it becomes impossible to manipulate the active state from the outside, except for accessing it via one of these: $$ (which are the untouchables!)
Solution (The Hack)
Maintain the active state in another list:
<div ng-controller="parasample-tabs">
{{activeState}}
<tabset ng-show="page.data.list.length">
<tab ng-repeat="item in page.data.list" active="activeState[$index]">
<tab-heading>
<i style="cursor: pointer" class="glyphicon glyphicon-remove" ng-click="delTab($index)" prevent-default></i>
Item {{$index +1}}
</tab-heading>
{{item.text}} - {{item.transcript}} - {{item.active}}
</tab>
</tabset>
<!--
For me this problem arose because I like to use self-contained, self-managing data
from factories, hence I call addItem not on a controller
-->
<button ng-click="page.addItem()">Add Item</button>
</div>
Now for the controller, that is wrapped around that tabs and manages them, and their active state instead of writing it into my data:
app.controller('parasample-tabs', function ($scope) {
$scope.maxItems = 5;
$scope.activeState = [];
$scope.delTab = function (idx) {
var list = $scope.page.data.list;
if (list.length > 0) {
$scope.page.delItem(idx);
$scope.activeState.splice(idx, 1);
}
};
$scope.$watch(
"page.data.list.length",
function (newLen, oldLen) {
if (!newLen) return;
// new item => new tab, make active
if (newLen > oldLen)
$scope.activeState.push("true");
}
);
});
Now UI Bootstrap will access the array activeState and store the active state there. There is no need for initialisation, as that is taken care of.
When a new item is added to our data list, the watch will set the new tab as the active tab (thats my preference) and the rest of the list will be updated by UI Bootstrap.
When deleting however, it is not easily possible to determine which item was removed, so I had to wrap my page.delItem into the controller's delTab method.
Here is a fiddle to play with, too.
Let's hope that UI Bootstrap will allow for a different way to maintain the active state instead of having a two way binding in the active attribute. I like having an "active ID" variable.
Disclaimer: I am aware of how dirty this is and I only tested it in Chrome so far and it works nicely.
You're missing quite a few here. Here's a more extensible way:
var app = angular.module('myApp',[]);
app.controller('MyController', ['$scope',function($scope){
$scope.tab = 0;
$scope.changeTab = function(newTab){
$scope.tab = newTab;
};
$scope.isActiveTab = function(tab){
return $scope.tab === tab;
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE HTML>
<html ng-app="myApp">
<head>
<style type="text/css">
.active{
background-color:red;
}
</style>
</head>
<body ng-controller="MyController">
<div>
<div ng-class="{'active':isActiveTab(0)}" ng-click="changeTab(0)">Some Title 1</div>
<div ng-class="{'active':isActiveTab(1)}" ng-click="changeTab(1)">Some Title 2</div>
</div>
<br/>
<div ng-show="isActiveTab(0)">tab1</div>
<div ng-show="isActiveTab(1)">tab2</div>
<script type="text/javascript" src="js/angular-1.2.24.min.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</body>
</html>
Initialization should always be in the controller.
Change the values using a controller function. Here, defined as 'changeTab()'
For checking active tabs, create a controller function to compare if the current value of $scope.tab is equal to the current tab.
I also added a bit of styling to impose which tab is active.
I am looking at this basic example of AngularJS:
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js"></script>
</head>
<body>
<div>
<label>Name:</label>
<input type="text" ng-model="yourName" placeholder="Enter a name here">
<hr>
<h1>Hello {{yourName}}!</h1>
</div>
</body>
</html>
It is the first example on the website and has no controller or anything. A textfield is attached to the model field yourName.
Now, I was wondering: since editing the text in the textfield changes the model value, how do I change the value of "yourName" programmatically? Thus far, I have found no simple way of doing this without adding a controller and whatnot.
Also, how can I attach a listener to that model to be notified whenever it changes?
In this case, you don't need a controller for this simple example. Angular behind the scenes is "creating"
$scope.yourName
In terms of changing it programmatically, you have to use a controller. In terms of watching for changes, use $scope.$watch
$scope.$watch($scope.yourName, function (newVal, oldVal) {
if (newVal !== oldVal) {
// logic here
}
});