Angular Directive How can we pass binding attr - angularjs

Assuming that we have custom directive which have ng-repeat inside:
//...
<div class="item" ng-repeat="item in items" data-value="{{item.id}}">
{{item.name}}
</div>
//...
And the caller is:
<dropdown items="assetTypesData"></dropdown>
Question is how can we pass name and id of {{item.name}} and {{item.id}} to directive as:
<dropdown items="assetTypesData" text="name" value="id"></dropdown>

Using isolate scope, you can pass in the three things you need: items, text, id. Then in your template, reference the {{item[text]}} for getting the property passed in for text (name in this exmaple) and {{item[value]}} to get the value property on the item (e.g. id).
angular.module('myApp', [])
.controller('MainController', function ($scope) {
var vm = {};
$scope.vm = vm;
activate();
function activate() {
var items = [];
for (var i = 0; i <= 15; ++i) {
items.push({id: i, name: 'Item ' + i});
}
vm.items = items;
}
})
.directive('myDropdown', function () {
var template = '<div class="item" ng-repeat="item in items" data-value="{{item[value]}}">' +
'{{item[text]}}' +
'</div>';
return {
restrict: 'E',
template: template,
scope: {
items: '=',
text: '#',
value: '#'
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MainController">
<my-dropdown items="vm.items" text="name" value="id"></my-dropdown>
</div>

Related

Angular - changing color and text of a div on ng-click

I am trying to change the color of a div on click of ng-click. I could make it work by changing the color, but now I need to change the text of the div when the same is clicked, so per example, I need change the color and replace the text inside per the one: clicked.
HTML:
<div ng-controller="MyCtrl">
Hello, {{name}}!
<div ng-init="item.isyellow = false" ng-repeat="item in realName" ng-class="{yellow : $index == row}" ng-class-odd="'odd'" ng-class-even="'even'" ng-click="colorRow($index)" style="cursor:pointer" >
{{item.id}}
{{item.name}}
</div>
</div>
JS:
var myApp = angular.module('myApp',[]);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
$scope.name = 'Superhero';
$scope.realName=[{"id":1,"name":"A"},{"id":2,"name":"B"},{"id":3,"name":"c"},{"id":4,"name":"D"},{"id":5,"name":"E"},{"id":6,"name":"F"}];
$scope.colorRow = function(index){
$scope.row = index;
}
}
CSS:
.odd{
background-color:white;
}
.even{
background-color:grey;
}
.yellow{
background-color:yellow;
}
jsfiddle: http://jsfiddle.net/HB7LU/26382/
A variant of #user3249448's ans. It might be what you are looking for
Look at JsFiddle
JS:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.row = -1;
$scope.name = 'Superhero';
$scope.realName=[{"id":1,"name":"A"},{"id":2,"name":"B"},{"id":3,"name":"c"},{"id":4,"name":"D"},{"id":5,"name":"E"},{"id":6,"name":"F"}];
$scope.colorRow = function(index){
$scope.row = index;
}
}
HTML:
<div ng-controller="MyCtrl">
Hello, {{name}}!
<div ng-init="item.isyellow = false" ng-repeat="item in realName" ng-class="{yellow : $index == row}" ng-class-odd="'odd'" ng-class-even="'even'" ng-click="colorRow($index)" style="cursor:pointer" >
<span ng-show="$index != row ">{{item.id}}
{{item.name}}</span>
<span ng-show="$index == row"> : Cliked</span>
</div>
</div>
You can check this http://jsfiddle.net/71rqq1o1/
Modify your CSS class
.yellow{
background-color:yellow;
color:green;
}
and
HTML to
<div ng-init="item.isyellow = false" ng-repeat="item in realName" ng-class="{yellow : $index == row}" ng-class-odd="'odd'" ng-class-even="'even'" ng-click="colorRow($index)" style="cursor:pointer" >
{{item.id}}
{{item.name}} <span ng-show="$index == row"> : Cliked</span>
</div>
I took the liberty of rewriting the entire thing using directives instead, because I believe your use-case was begging for it :)
Please note:
What follows seems complicated but it's actually terribly simple to use. Just drop the JS code for the directives in your Angular module and you're good to go.
You don't even need to read it (although it wouldn't hurt) or replace anything, it's ready to use -- and re-use for many similar cases.
I created 2 directives that work together:
A base element: clickable-element, which will know about its clicked state.
And a container: clickable-container that will be a wrapper around a collection of "clickable" elements
The clickable-container
The clickable-container will manage the state of all its children clickable-elements. Clicking on one on them toggles it on and all the others off.
The clickable-element
For your simple use-case, the clickable-elements have only two simple features:
They inject a boolean value $clicked in their scope
They have a clicked class added/removed depending on their clicked state.
Example
Your code would now simply look like this:
<clickable-container>
<clickable-element ng-repeat="elem in ['A', 'B', 'C', 'D', 'E']">
<div ng-if="$clicked">clicked!</div>
<div ng-if="!$clicked">{{elem}}</div>
</clickable-element>
</clickable-container>
... convenient, isn't it?
Moreover
Using this approach, you can manage several distinct collections of clickable-elements with separate clickable-containers.
The styling and the content are entirely handled by your stylesheet and your HTML code respectively.
Demonstration
angular.module('myApp', [])
.directive('clickableContainer', [
function() {
return {
restrict: 'EA',
transclude: true,
template: '<div ng-transclude></div>',
controller: [
function() {
const elements = [];
this.newElement = function() {
var element = {
id: elements.length,
clicked: false
};
return elements.push(element), element;
};
this.toggle = function(element) {
elements.forEach(function(e) {
e.clicked = e.id === element.id;
});
};
}
]
};
}
])
.directive('clickableElement', [
function() {
return {
require: '^^clickableContainer',
restrict: 'EA',
transclude: true,
template: '<div ng-click="_toggle()" ng-class="{clicked: $clicked}" ng-transclude></div>',
link: function(scope, element, attrs, clickableContainerCtrl) {
scope._toggle = clickableContainerCtrl.toggle.bind(clickableContainerCtrl, scope._state = clickableContainerCtrl.newElement());
scope.$watch('_state.clicked', function(clicked) {
scope.$clicked = clicked;
});
}
};
}
]).name;
clickable-element {
cursor: pointer;
}
[clickable-container] [clickable-element]:nth-of-type(even) {
background-color: #eee;
}
.clicked {
background-color: yellow !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js"></script>
<div ng-app="myApp">
<div clickable-container>
<div clickable-element ng-repeat="elem in ['A', 'B', 'C', 'D', 'E']">
<div ng-if="$clicked">clicked!</div>
<div ng-if="!$clicked">{{elem}}</div>
</div>
</div>
</div>

Angular JS change property of controller for directive

I am trying to change boolean property of my controller from directive. It is set to true, but when i set it to false it should display other html template. Her is my code:
Directive code:
app.directive('entityTaskList', function(){
return {
restrict: 'E',
templateUrl: 'views/task/taskList.html',
scope: {
taskItems: '='
},
bindToController: true,
controller: 'TasksCtrl as taskCtrl',
link: function(scope, element, attrs){
scope.openItem = function(){
console.log("Open Items");
var ctrl = scope.taskCtrl;
ctrl.newTask = false;
};
}
};
});
TaskCtrl code:
app.controller('TasksCtrl', ['$scope', 'TaskService', function ($scope, TaskService) {
// initialize function
this.newTask = true;
this.name = "Nedim";
this.templates = {
new: "views/task/addTask.html",
view: "views/task/viewTask.html"
};
// load all available tasks
TaskService.loadAllTasks().then(function (data) {
$scope.items = data.tasks;
});
$scope.$on('newTaskAdded', function(event, data){
$scope.items.concat(data.data);
});
return $scope.TasksCtrl = this;
}]);
taskList.html
<ul class="list-group">
<li ng-repeat="item in taskCtrl.taskItems" class="list-group-item">
<a ng-click="openItem()">
<span class="glyphicon glyphicon-list-alt" aria-hidden="true"> </span>
<span>{{item.name}}</span>
<span class="task-description">{{item.description}}</span>
</a>
</li>
</ul>
and task.html
<!-- Directive showing list of available tasks -->
<div class="container-fluid">
<div class="row">
<div class="col-sm-6">
<entity-task-list task-items="items"></entity-task-list>
</div>
<div class="col-sm-6" ng-controller="TaskDetailCtrl as taskDetailCtrl">
<!-- form for adding new task -->
<div ng-show="taskCtrl.newTask" ng-include="taskCtrl.templates.new"></div>
<!-- container for displaying existing tasks -->
<div ng-show="!taskCtrl.newTask" ng-include="taskCtrl.templates.view"></div>
</div>
</div>
</div>
If you change this:
// load all available tasks
TaskService.loadAllTasks().then(function (data) {
$scope.items = data.tasks;
});
To this(literally):
// load all available tasks
TaskService.loadAllTasks().then(function (data) {
this.items = data.tasks;
});
your code will work, this is due to you using the controllerAs syntax for TaskCtrl but not assigning it to this instead to the $scope

AngularJS - directive with a dynamic array of objects in form

I'm trying to create a form that has a dynamic set of input boxes. I'm trying to get ng-models to work with array items and I am clearly doing something wrong.
My problem seems to be indexing the scope array lineItems in the template.
Here is my jsfiddle: http://jsfiddle.net/dk253/9ykuuobq/
Here is my code:
<div ng-controller="MyCtrl">
<h2>Testing Arrays</h2>
<button type="button" ng-click="addItem()" class="btn btn-primary">Howdy</button>
<div ng-repeat="item in items" item="item" my-index="{{$index}}" itemlist></div>
</div>
<script type="text/ng-template" id="template.html">
<div class = "row">
<div class = "col-xs-6"> <input name= "itemNum-{{item.itemNum}}" type = "number" class="form-control"
placeholder = "Item Number" ng-model="items[{{item.itemNum}}].itemNum" required> </div>
<div class = "col-xs-6"> <input name= "name-{{item.itemNum}}" type = "text" class="form-control"
placeholder = "Name" ng-model="items[{{item.itemNum}}].name" required> </div>
</div>
</script>
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', function ($scope) {
$scope.count = 0;
$scope.items = [];
var baseItem = {
itemNum: 0,
name: "New"
};
$scope.addItem = function () {
newItem.itemNum = $scope.count;
var newItem = angular.copy(baseItem);
newItem.itemNum = $scope.count;
$scope.items.push(newItem);
$scope.count += 1;
};
});
myApp.directive('itemlist', function ($compile) {
return {
templateUrl: 'template.html',
restrict: 'A',
replace: true,
transclude: true,
scope: { item: '=', myIndex: '#' }
};
});
Thanks for your help!
The fiddle has several mistakes:
$scope.addItem is using the newItem before defining it. Remove the first line (probably a typo).
The ng-model:
It accepts expressions without {{...}}. So ng-model="items[{{item.itemNum}}].itemNum" would become ng-model="items[item.itemNum].itemNum"
This is still wrong because the directive has isolated scope and the items variable is not visible. But item is visible, so you only need: ng-model="item.itemNum" (and ng-model="item.name").
With these changes, it seems to be working: http://jsfiddle.net/9ykuuobq/19/

ANgularjs: ng-repeat and nested custom directive

I am trying to ng-repeat a custom directive, which has an attribute that should change over the iteration.
This is my html:
<div ng-controller="WalletsController as controller">
<bitcoin-address ng-repeat="bitcoin_label in controller.getWallets()" bitcoin-label="bitcoin_label"></bitcoin-address>
</div>
This is my controller:
(function() {
var app = angular.module('wallets', [ ]);
app.controller(
"WalletsController",
function($scope, $http) {
this.wallets = [];
var controller = this;
this.getWallets = function() {
return controller.wallets;
};
$http.get("wallet_addresses").success(
function(data) {
for (var i = 0; i < data.length; i++) {
var curWallet = data[i];
$scope[curWallet.label] = {
label: curWallet.label,
address: curWallet.address,
balance: curWallet.balance
};
controller.wallets.push(curWallet.label);
}
}
);
});
app.directive(
'bitcoinAddress',
function() {
return {
restrict: 'E',
templateUrl: '../../resources/html/bitcoin-address.html',
scope: {
bitcoinLabel: '=',
}
};
}
);
})();
And this is my template:
<div class="col-md-8 dashboardAddressCell dropdown-toggle" data-toggle="dropdown">{{bitcoinLabel.address}}</div>
What happens is that the template can not resolve the bitcoinLabel variable. I have tried specifying a constant value and the template works. My conclusion is that I am not correctly specifying the bitcoin_label attribute in the html section.I have also tried using {{bitcoin_address}}, but angularjs complains about that.
I have also tried with the following html code:
<div ng-controller="WalletsController as controller">
<!-- <tr><th>Indirizzo</th><th>Saldo</th><th></th>-->
<div ng-repeat="bitcoin_label in controller.getWallets()">
{{bitcoin_label}}
<bitcoin-address bitcoin-label="bitcoin_label"></bitcoin-address>
</div>
<bitcoin-address bitcoin-label="ciccio"></bitcoin-address>
</div>
It does not work either, but at least it shows the {{bitcoin_label}} value.
The problem seems pretty simple. Instead of
controller.wallets.push(curWallet.label);
you should push corresponding $scope[curWallet.label] object:
controller.wallets.push($scope[curWallet.label]);
Because curWallet.label is just a string so in the first case wallets ends up as array of stings. However you need wallets to be an array of objects, each having address, label, balance properties.
You have some problems with your logic. You're putting wallet labels into .wallets, then iterating over the labels, and then in your bitcoinAddress template you're trying to read .address property of the label string (not from the object where you saved it). Why not simplify the whole thing to this script:
.controller("WalletsController", function($scope, $http) {
$scope.wallets = [];
$http.get("wallet_addresses").success(function(data) {
$scope.wallets = data.slice();
});
})
.directive('bitcoinAddress', function() {
return {
restrict: 'E',
templateUrl: '...',
scope: {
wallet: '=',
}
};
})
this directive template:
<div class="..." ...>{{wallet.address}}</div>
and this body template:
<div ng-controller="WalletsController as controller">
<bitcoin-address ng-repeat="wallet in wallets" wallet="wallet"></bitcoin-address>
</div>
Both bitcoinAddress and ng-repeat directives creating scopes on the same element could cause some conflict (isolate scope in the bitcoinAddress case).
Try adjusting your html structure slightly:
<div ng-controller="WalletsController as controller">
<div ng-repeat="bitcoin_label in controller.getWallets()">
<bitcoin-address bitcoin-label="bitcoin_label"></bitcoin-address>
</div>
</div>
Why not use $scope.wallets instead of this.wallets? Also in your getWallets function. After that try
<div ng-controller="WalletsController">
<div ng-repeat="bitcoin_label in wallets">
<bitcoin-address bitcoin-label="bitcoin_label"></bitcoin-address>
</div>
</div>
But if your wallets is an array of non-object like array of string or integer, use
<div ng-controller="WalletsController">
<div ng-repeat="bitcoin_label in wallets track by $index">
<bitcoin-address bitcoin-label="wallets[$index]"></bitcoin-address>
</div>
</div>

How to use checklistbox in angularjs or in bootstrap

Can anyone suggest me how can I display a checkbox list in angularjs
HTML:
<div ng-app="app">
<div ng-controller="TodoCtrl">
<span ng-repeat="(checkboxName, checkboxValue) in checkboxes">
{{ checkboxName }}
<input type="checkbox" ng-model="checkboxValue"/>
</span>
</div>
</div>
Javascript:
var app = angular.module('app', []);
app.controller('TodoCtrl', function($scope) {
$scope.checkboxes = {
'foo': true,
'bar': false,
'baz': true,
'baa': true
}
});
Update:
HTML:
<div ng-app="app">
<div ng-controller="TodoCtrl">
<!--<span ng-repeat="(checkboxName, checkboxValue) in checkboxes">
{{ checkboxName }}
<input type="checkbox" ng-model="checkboxValue"/>
</span>-->
<checklist checkboxes="checkboxes" on-change="checkboxInfo(name, value)"></checklist>
</div>
</div>
Javascript:
var app = angular.module('app', []);
app.controller('TodoCtrl', function($scope) {
$scope.foo = true;
$scope.bar = false;
$scope.baz = true;
$scope.baa = true;
$scope.checkboxes = {
'foo': $scope.foo,
'bar': $scope.bar,
'baz': $scope.baz,
'baa': $scope.baa
};
// Non-encapsulated activity: updates parent scope values
$scope.checkboxInfo = function(name, value) {
console.log('Checkbox "' + name + '" clicked. It is now: ' + value + '.');
$scope[name] = value;
};
// This shouldn't do anything if we delete '$scope[name] = value', since we isolated the scope:
$scope.$watchCollection('[foo, bar, baz, baa]', function() {
console.log($scope.foo, $scope.bar, $scope.baz, $scope.baa);
});
})
.directive('checklist', function($timeout) {
return {
restrict: 'E',
scope: {
checkboxes: '=', // create an isolate scope w/ 2-way binding
onChange: '&' // execute a function in the parent scope
},
template:
'<span ng-repeat="(checkboxName, checkboxValue) in checkboxes">' +
'{{ checkboxName }}' +
'<input type="checkbox" ng-model="checkboxValue" ng-change="onChange({name:checkboxName, value:checkboxValue})"/>' +
'</span>',
link: function(scope, element, attrs) {
// Add encapsulated activity as necessary
}
};
});

Resources