Angularjs directive link call function in ng-click - javascript

My directive works fine, but I'd like to use it in ng-click However, the function inside link can't be triggered.
http://jsfiddle.net/ovzyro1f/
<div ng-app="editer" ng-controller="myCtrl" class="container">
<div class="parents">
<div ng-repeat="item in items" class="wrap" sibs>
<span>{{item.name}}</span>
<button ng-click="">click</button>
</div>
</div>
</div>
JS
function myCtrl($scope) {
$scope.editedItem = null;
$scope.items = [{
name: "item #1",
thing: "thing 1"
}, {
name: "item #2",
thing: "thing 2"
}, {
name: "item #3",
thing: "thing 3"
}];
$scope.show = false; //ignore this line
}
var editer = angular.module('editer', []);
editer.directive('sibs', function() {
return {
link: function(scope, element, attrs) {
element.bind('click', function() {
element.parent().children().addClass('unclicked');
element.removeClass('unclicked');
})
scope.myClick = function() {
element.parent().children().addClass('unclicked');
element.removeClass('unclicked');
}
},
}
});
I'd like to call the function in ng-click please see this one http://jsfiddle.net/ovzyro1f/2/ to remove sib from div ng-repeat="item in items" class="wrap"
<button ng-click="myClick()">click</button>

You should avoid to manipulate the DOM like we do in jQuery.
In Angular we think differently: it's the data which transforms automatically the DOM when the data changes (https://docs.angularjs.org/guide/databinding). Most of the time you never have to make the changes manually.
In doing so, you generally don't need to use the link function. You can have a controller (like in your example) or a directive with a controller (https://docs.angularjs.org/guide/directive).
Finally I just modified a little bit your controller and your template.
HTML
<div ng-app="editer" ng-controller="myCtrl" class="container">
<div class="parents">
<div ng-repeat="item in items" class="wrap" sibs>
<span ng-class="{ unclicked: !item.selected }">{{ item.name }}</span>
<button ng-click="selectItem(item)">click</button>
</div>
</div>
</div>
JS
function myCtrl($scope) {
$scope.items = [...];
$scope.selectItem = function (item) {
// reset all the items
$scope.items.forEach(function (i) {
i.selected = false;
});
// set the new selected item
item.selected = true;
}
}

Related

Select DOM elements within template

There's this template that I call multiple times on the same page:
<div ng-controller="templateController">
<div class="source">
<div ng-repeat="item in info">
<div class="content" data-value="{{item.ID}}">{{item.name}}</div>
</div>
</div>
<br style="clear:both" />
<div class="receiver"></div>
</div>
and then I want to select all the elements with class="content" within each template scope in order to manipulate them.
How can I achieve this using JS?
EDIT :
Plunker
In this planker the console.log should print "1" twice and its printing "1" and then "2" when the template loads the second time
After more explanation here is a working example:
https://plnkr.co/edit/n5GOd6MDLyvG4ZAsuLvf?p=preview
The main idea is creating 2 lists and iterating over both and just moving data around between them on click.
angular.module("demo", []);
angular
.module("demo")
.controller("demoController", ["$scope", function($scope) {
}]);
angular
.module("demo")
.controller("templateController", ["$scope", "$timeout", function($scope, $timeout) {
$scope.sourceList = [
{
name: "john",
ID: 1
},
{
name: "Edward",
ID: 0
},
{
name: "Carl",
ID: 2
}
];
$scope.receiverList = [
{
name: "Bob",
ID: 1
}
];
$scope.moveToReceiver = function(item){
$scope.receiverList.push(item);
$.each($scope.sourceList, function(i){
if($scope.sourceList[i].name == item.name){
$scope.sourceList.splice(i, 1);
return false;
}
});
}
}]);
Most of the time you do not want to do DOM manipulation in Angularjs and instead hook into events with your controller. If you have to do DOM manipulation in AngularJS you would use directives
Docs on Creating a Directive that Manipulates the DOM
You could then use your link function to grab the children of your directive's element
function link(scope, element, attrs) {
var content = angular.element(element[0].querySelector('.content'));
}
https://stackoverflow.com/a/17329630/2033671

How do I use an attribute directive inside an element directive template in angularjs?

I have a need to show a list of check boxes on my site listing products and other items. The checklist-model attribute directive works well for this because I can bind it to a list of items that relates to the items selected.
All this works fine when I simply use this code in my angular controller. However, I have several list boxes that need to be displayed the same way with the same "select all" and "select none" buttons for each list. I don't want to repeat this code and layout so I've created my own directive for the entire list.
The problem is when I use my own directive it doesn't bind correctly back to my data, the select all only works once, and the select none doesn't work at all.
I suspect it has something to do with how I'm passing the scope around, and the two directives are not working well together.
Why does this not work inside a directive?
Here is a jsfiddle: https://jsfiddle.net/fande455/m9qhnr9c/7/
HTML
<section ng-app="myApp" ng-controller="couponEditController">
<script type="text/ng-template" id="checkboxlist.template.html">
<div>
<div class="form-input form-list">
<label ng-repeat="item in valuelist | orderBy:value">
<input type="checkbox" checklist-model="model" checklist-value="item" /> {{item[value]}}
<br />
</label>
</div>
<button class="btn btn-default" style="float:left; margin-bottom: 5px;margin-left: 10px;margin-right:10px" ng-click="selectNone()">Select None</button>
<button class="btn btn-default" style="float:left; margin-bottom: 5px;" ng-click="selectAll()">Select All</button>
<div class="cleared"></div>
</div>
</script>
<div>
<checkboxlist model="coupon.Products" value="Name" valuelist="productsList"></checkboxlist>
</div>
</section>
JS
var myApp = angular.module('myApp', ['checklist-model']);
myApp.directive('checkboxlist', [function() {
return {
restrict: 'E',
templateUrl: 'checkboxlist.template.html',
controller: 'checkboxlistController',
scope: {
model: "=",
value: "#",
valuelist: "="
},
require: '^checklist-model'
}
}]);
myApp.controller('checkboxlistController', ['$scope', function($scope) {
$scope.selectAll = function() {
$scope.model = angular.copy($scope.valuelist);
};
$scope.selectNone = function() {
$scope.model = [];
};
}]);
myApp.controller('couponEditController', ['$scope', function($scope) {
$scope.coupon =
{"Id": 1,
"Name": "My Coupon",
"Products": []
}
;
$scope.productsList = [{
"Id": 1,
"Name": "Product 1"
}, {
"Id": 2,
"Name": "Product 2"
}];
}]);
From documentation:
Instead of doing checklistModelList = [] it is better to do
checklistModelList.splice(0, checklistModelList.length)
In your code you should do
$scope.selectAll = function() {
$scope.selectNone();
$scope.model.push.apply($scope.model, $scope.valuelist);
};
$scope.selectNone = function() {
$scope.model.length = 0;
};
Here's the updated fiddle: https://jsfiddle.net/m9qhnr9c/9/
The idea is not to replace the array with a new one, but modify only its elements.

Call Angular nested functions

I'm trying to call a nested function in Angular. I've formatted the functions in such a way in order to neaten up code, yet when invoking the function through an ng-click it doesnt seem to work.
In my case, a scope conflict occurs because the variable name is taken by the local scope, so I've named a controller and invoked it as a child property of the controller, but no success.
I've created a jsfiddle to demonstrate what I mean:
https://jsfiddle.net/838L40hf/16/
HTML:
<div class="InviteAppContainer" ng-app="InviteApp">
<div ng-controller="InviteController as cntrl">
<button ng-click="alert()">
Alert, standard
</button>
<div ng-repeat="invite in invites">
<button ng-click="cntrl.invite.alert(invite.name)">
Alert, {{invite.name}}
</button>
</div>
</div>
</div>
JS:
var InviteApp = angular.module('InviteApp',[])
.controller('InviteController', function($scope) {
$scope.invites = {
0 : {
"name":"invite 1",
},
1 :{
"name" : "invite 2"
}
};
$scope.invite = {
alert : function(name) {
alert(name);
}
};
$scope.alert = function() {
alert("alert2!");
};
});
If you're using the controller as syntax, you should be binding things to this instead of $scope if you wish to access them through the aliased controller name.
Simply changing the binding from $scope.invite to this.invite will do the trick.
https://jsfiddle.net/pL4wc10n/
You should use this
Javascript
var InviteApp = angular.module('InviteApp',[])
.controller('InviteController', function($scope) {
// controllerAs : cntrl
var that = this;
$scope.invites = {
0 : {
"name":"invite 1",
},
1 :{
"name" : "invite 2"
}
};
// USING THIS you have cntrl.function
that.invite = {
alert : function(name) {
alert(name);
}
};
$scope.alert = function() {
alert("alert2!");
};
});
var InviteApp = angular.module('InviteApp',[])
.controller('InviteController', function($scope) {
$scope.invites = {
0 : {
"name":"invite 1",
},
1 :{
"name" : "invite 2"
}
};
$scope.invite = {
alert : function(name) {
alert(name);
}
};
$scope.alert = function() {
alert("alert2!");
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="InviteAppContainer" ng-app="InviteApp">
<div ng-controller="InviteController as cntrl">
<button ng-click="alert()">
Alert, standard
</button>
<div ng-repeat="i in invites">
<button ng-click="invite.alert(i.name)">
Alert, {{i.name}}
</button>
</div>
</div>
</div>
What I edit:
<div ng-repeat="i in invites">
<button ng-click="invite.alert(i.name)">
Alert, {{i.name}}
</button>
</div>

Ng-repeat and Ng-show in angular

I want to display only the first element in ng-repeat and by click display the rest elements.
That's what I tried to do without success:
<button data-ng-click="term = !term">Show rest</button>
<div data-ng-repeat="y in mQues track by $index" data-ng-show="term" data-ng-init="term = $first">
...
</div>
it really display only the first but the button click do noting for some reason...
Change your ng-show condition: data-ng-show="term || $first"
try this...
<div data-ng-repeat="item in items" data-ng-show="term || $index == 0">{{item.title}}</div>
<div data-ng-repeat="y in mQues track by $index" data-ng-if="$first"></div>
I think this will help
Using the angular filter limitTo it is rather straight forward.
function MyCtrl($scope) {
$scope.items = [
{ title: "item 1" },
{ title: "item 2" },
{ title: "item 3" },
{ title: "item 4" }
]
$scope.limit = 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app ng-controller="MyCtrl">
<span ng-click="limit = limit == 1 ? items.length : 1">Toggle</span>
<ul>
<li ng-repeat="item in items | limitTo:limit" ng-bind="item.title"></li>
</ul>
</div>
https://docs.angularjs.org/api/ng/directive/ngShow
ng-show displays an element by evaluating the expression given to it.
the expression assigned to ng-show gets converted to true or false depending on whether the result is 'truthy' or 'falsy'. When you assign term a truthy value, ng-show is assigned true.
when you do click button, it evaluates term = !term and term then becomes false and therefore the whole div will be hidden
Guess simpler and more straight forward:
var app = angular.module('app', []);
app.controller('DisplayController', function($scope) {
$scope.ShowRest = 0;
$scope.items = [{
title: 'myTitle1'
}, {
title: 'myTitle2'
}, {
title: 'myTitle3'
}, {
title: 'myTitle4'
}, {
title: 'myTitle5'
}];
});
app.$inject = ['$scope'];
Then:
<div data-ng-controller="DisplayController">
<div data-ng-repeat="item in items">
<div data-ng-class="{'no-display': !$first}">{{ item.title }}</div>
</div>
<div data-ng-repeat="item in items" data-ng-show="ShowRest">
<div data-ng-class="{'no-display': $first}">{{ item.title }}</div>
</div>
<button data- ng-click="ShowRest = !ShowRest">Show rest</button>
</div>
Maybe it's not the most efficient way of doing it, but it works!
Fiddle: http://jsfiddle.net/cb6pggL8/1/

updating text on view from ng-repeat

I have problem with updating view by using ng-repeat.
When click on text, it doesnt update but it overwrites below. (I want have panel with names(links) and show its description on view)
I have searched everything and couldnt find answer or something useful what would help me.
html:
<body>
<div ng-app="myApp">
<div ng-controller="myCtrl">
<ul>
<li><a ng-repeat="item in items" ng-click="getInfo(item)" > {{item.name}} <br> </a></li>
</ul>
</div>
<hr>
<div ng-controller="myInfo">
<div ng-repeat="info in item" >
<h3>Name: {{info.name}}</h3>
<p> ID: {{info._id}}</p>
<p> temp: {{info.temp}} </p>
</div>
</div>
</div>
</body>
js
var app = angular.module('myApp', [])
app.controller('myCtrl', function($scope, $http, shareDataService) {
$http.jsonp('data.json').success(function(data) {
$scope.items = data;
});
$scope.getInfo = function(item) {
shareDataService.addItem(item);
}
});
app.controller('myInfo', function( $scope, shareDataService ){
$scope.item = shareDataService.getItem();
});
app.service('shareDataService', function() {
var myItem = [];
var addItem = function(newObj) {
myItem.push(newObj);
}
var getItem = function(){
return myItem;
}
return {
addItem: addItem,
getItem: getItem
};
});
json
angular.callbacks._0([
{
"_id": 1,
"temp": "asdgdf",
"name": "name1"
},
{
"_id": 2,
"temp": "asdasdasd",
"name": "name2"
},
{
"_id": 3,
"temp": "asasdasdadgdf",
"name": "name3"
}
]);
Plunker: http://plnkr.co/edit/X65oH0yAkRnN8npKnFY2?p=preview
You have an error in your console. Just add track by to your ng-repeat:
<div ng-repeat="info in item track by $index">
ng-repeat needs a unique id to track the items in order to be able to update them. If you add the same item twice, ng-repeat sees the same item twice, ans loses its mind. Using $index (which is unique) resolves that issue.
Keep in mind that using $index is adequate here, but it's preferred to use a unique id from the object if you can.
EDIT:
If your issue is that you want to see only the one element you clicked on in your view, then the issue is that you are adding your item to an array, when you should just be setting a value in your service. And, obviously, no need of a ng-repeat in your view.
http://plnkr.co/edit/Wfg9KhCWKMDreTFtirhR?p=preview
JS:
app.controller('myCtrl', function($scope, $http, shareDataService) {
//[...]
$scope.getInfo = function(item) {
shareDataService.setItem(item);
}
});
app.controller('myInfo', function( $scope, shareDataService ){
$scope.$watch(function () {
return shareDataService.getItem();
}, function (value) {
$scope.info = value;
})
});
app.service('shareDataService', function() {
var myItem;
return {
setItem: function(newObj) {
myItem = newObj;
},
getItem: function(){
return myItem;
}
};
});
HTML:
<div ng-controller="myInfo" ng-show="info">
<h3>Name: {{info.name}}</h3>
<p> ID: {{info._id}}</p>
<p> temp: {{info.temp}} </p>
</div>
If you only want to display information of the item that you just have clicked, then we don't need the second ng-repeat (as jlowcs said).
We also don't have to defined myItem as a array, it's just unnecessary, I think.
Edit:
how embarrassing i am, my answer look exactly to same as jlowcs's. I guess I took to much time to figure out the answer.
One thing to add up:
Why do I need a $watch in myInfo controller?
Because at the first time, we use ng-repeat, this component do the watch part for us. Then when I remove ng-repeat, I need to watch for data changing by myself.

Categories