ng-click inside ng-repeat doesnt work - angularjs

I have html which looks like one below, I have 2x ng-click in whole code in both situation I call same function. Both functions are in same controller.
<div class="tagselect tagselect--frameless">
<div class="combobox__body combobox__body--open combobox__body--frameless" ng-show="focus">
<ul class="list-unstyled">
<li class="combobox__item" ng-repeat="pos in listCtrl.positions | filter:query as results"
ng-click="listCtrl.choosePosition(pos)">{{pos.name}}
</li>
</ul>
</div>
</div>
<div class="col-md-2 no-padding">
<button type="button" class="btn btn-success" ng-click="listCtrl.chosenPositions(789456)">Add</button>
</div>
controller looks like:
myApp.controller('ListCtrl', ['$scope', '$cookies', '$http', function ($scope, $cookies, $http) {
var listCtrl = {
candidates: [],
positions: [],
chosenPositions: [],
init: function () {
listCtrl.getCandidates();
listCtrl.getPositions();
},
getCandidates: function () {
$http.get('candidates.json').then(function (res) {
listCtrl.candidates = res.data;
});
},
getPositions: function () {
$http.get('positions.json').then(function (res) {
listCtrl.positions = res.data;
});
},
choosePosition: function (position) {
console.log(position);
}
};
listCtrl.init();
$scope.listCtrl = listCtrl;
}]);
I double check for missspells and make sure its not because of function (I create a new one with simple console log).
Problem is that button click correctly call function but ng-repeat <li ng-click=""> doesnt do anything. I read in angular documentation that ng-repeat create new scope but this should be still okey in my opinion as soon as I use reference to object listCtrlchoosePosition()
Can someone tell me what I am doing wrong?
Thanks
EDIT: Plunker example:
http://plnkr.co/edit/ooUQA2n1Vyj8RZtsQ1Pj?p=preview

ng-blur is doing something weird, so I'm going to suggest you to change the $scope.focus value from the ListCtrl instead of using the ng-blur.
html file
<!-- more html code -->
<!-- input without ng-blur directive -->
<input class="tagselect__input" placeholder="Position" ng-focus="focus=true" ng-model="query">
<!-- more html code -->
<li class="combobox__item" ng-repeat="pos in listCtrl.positions | filter:query as results" ng-click="listCtrl.choosePosition(pos)">{{pos.name}}
<!-- more html code -->
js file
// more code goes here.
choosePosition: function (position) {
//alert('Going to choosen position');
//$scope.query = position.name;
$scope.focus = false; // Hide div options from here.
// rest of your code.
},
// more code goes here.
Working in this plunkr

Related

Unable to get content AngularJs dynamic popover inside ngrepeat

I need to display a small popover which should open on click and goaway on clicking anywhere on the page.
I found a plunker (http://plnkr.co/edit/K7cYQSDEBS3cHvDfJNLI?p=preview) which matches this requirement however, unable to get it to work inside ng-repeat.
I saw several answers and Plunker examples but not able to get this to work.
Here is my html
<div ng-controller="TestController">
<div class="row" style="background-color: #ebebeb !Important; ">
<div style="text-align:center">
<table style="width:100% !important;">
<tr ng-repeat="member in TeamMembers" style="font-size:18px !important; height: 108px;">
<td style="display:block;margin-top:30px;text-align:left;"> {{member.FirstName}} {{member.LastName}} <i class="fa fa-info-circle" aria-hidden="true" ng-show="member.Description != null" popover-template="dynamicPopover.templateUrl" popover-placement="bottom" popover-elem descr="{{member.Description}}"></i></td>
</tr>
</table>
</div>
</div>
...
<script type="text/ng-template" id="descriptionModal.html">
<div class="adp-info-dialog">
<div class="modal-body">
<div class="row">
<div class="col-md-8 col-md-offset-1">
<div class="form-group">
<label class="fieldset-label">Test {{ dynamicPopover.descr }}</label>
</div>
</div>
</div>
</div>
</div>
</script>
Here is the JS
testApp.controller('TestController', function ($scope, $rootScope, $log, $modal, SiebelAccountTeamService, $filter, $element) {
$scope.dynamicPopover = {
templateUrl: 'descriptionModal.html',
descr: null
};
var result = TestService.GetTeamMembers();
result.then(function (data) {
$scope.TeamMembers = data.data;
}, function (e) {
console.log(e);
}).finally(function () {
$scope.CompleteLoading();
});
});
testApp.directive('popoverClose', function ($timeout) {
return {
scope: {
excludeClass: '#'
},
link: function (scope, element, attrs) {
var trigger = document.getElementsByClassName('trigger');
function closeTrigger(i) {
$timeout(function () {
angular.element(trigger[0]).triggerHandler('click').removeClass('trigger');
});
}
element.on('click', function (event) {
var etarget = angular.element(event.target);
var tlength = trigger.length;
if (!etarget.hasClass('trigger') && !etarget.hasClass(scope.excludeClass)) {
for (var i = 0; i < tlength; i++) {
closeTrigger(i)
}
}
});
}
};
});
testApp.directive('popoverElem', function () {
return {
scope: {
descr: '#'
},
link: function (scope, element, attrs) {
$scope.dynamicPopover.descr = scope.descr,
alert($scope.dynamicPopover.descr),
element.on('click', function () {
element.addClass('trigger');
});
}
};
});
Appreciate your help.
Update:
To show the data of the ng-repeat inside the popover content, we need to access the individual objects through the $index of the ng-repeat. Refer the below example.
Plunkr Demo
The problem here is that you are using ng-repeat which creates a new scope read more here.
Since replicating the issue with your code is tedious, I tried replicating the issue with the plunkr!
Solution:
Plunkr Demo
You can simply define a new controller inside the descriptionModal.html like so
HTML:
<script type="text/ng-template" id="myPopoverTemplate.html">
<div class="adp-info-dialog" ng-controller="tester">
<div class="modal-body">
<div class="row">
<div class="col-md-8 col-md-offset-1">
<div class="form-group">
<label class="fieldset-label">Test {{ $parent.$parent.dynamicPopover.content }}</label>
</div>
</div>
</div>
</div>
</div>
</script>
JS:
app.controller('tester', function ($rootScope, $scope) {
console.log($scope.$parent.$parent.dynamicPopover.title);
});
Then, we will be able to access the parent scope, using $parent, the html inside the above script uses the $parent to get the variable!
Please note: It took me two $parent to reach the required $scope to access the scope variable. In your scenario it will also require two, the way to check how many is needed is use console.log($scope), then open the console(F12), then traverse through the objects $parent property till you find the correct $scope. Then count the number of $parent traversed, that will be your required number of $parent to traverse!
P.S:
There is another method you can do this, since this method will require a significant rewrite of your code, I will provide the GIST, you can use the controller as syntax and access the correct scope.
Here is the SO Answer giving the method to do it
SO Answer
I hope this fixes you issue.

Angular returns [object Object]

my Angular code returns [object Object]. I am calling 2 controllers on different pages. First one sets the data on ng-click and the second one gets (displays) the data. Here is the code:
Angular App code:
var careerApp = angular.module("careerApp", []);
careerApp.factory('myService', function () {
var savedData = {};
function set(data) {
savedData = data;
}
function get() {
return savedData;
}
return {
set: set,
get: get
}
});
careerApp.controller("JobList", function ($scope,myService) {
myService.set(data);
});
careerApp.controller("JobSelection", function ($scope, myService) {
$scope.jobname = myService.get();
});
HTML on Page 1
<div class="center-details" ng-controller="JobList">
<div class="details" ng-click="set(data)" >
<h2 class="name" ng-model="jobtitle">
Winter
</h2>
<p><b>Job ID#</b> <span class="jobid">2017-01</span></p>
</div>
</div>
HTML on Page 2
<div ng-controller="JobSelection">
<label ng-bind="jobname"></label>
</div>
You are bringing the whole object in
<label ng-bind="jobname"></label>
If you intented to write the object with a better formatting try changing it to:
<label> {{ jobname | json }}</label>
This way it will be formatted and printed as a json object.
Use Angular expressions intead of ng-bind. Otherwise you will have to specify a specific property of your object.
page 1
<div class="center-details" ng-controller="JobList">
<div class="details" ng-click="set('Winter')" >
<h2 class="name">
Winter
</h2>
<p><b>Job ID#</b> <span class="jobid">2017-01</span></p>
</div>
</div>
Controller:
careerApp.controller("JobList", function ($scope,myService) {
$scope.set= function(data){
myService.set(data);
}
});
page 2
<label ng-bind="jobname"></label>
<label>{{jobname}}</label>
How to make it dynamically, based on the input
<input stype="text" ng-model="jobTitle" ng-change="set()" >
<h2 class="name">
{{jobTitle}}
</h2>
Controller:
careerApp.controller("JobList", function ($scope,myService) {
$scope.jobTitle = "";
//This function will be called every time that jobTitle change its value.
$scope.set= function(){
myService.set($scope.jobTitle);
}
});
Notes:
Take into account that ng-model directive binds an input, select, textarea value to a property on the scope.
Since you have this assignment in your controller definition
$scope.jobname = myService.get();
If you run this controller before the user make a click it will be empty. it wont be refreshed in every click.

Adding directive dynamically using ng-click

I am working on a modal for a client. I have created a directive that works great, the problem is that one modal is made ahead each time it is used like..
What I have
<div ng-repeat="item in items">
<a data-toggle="modal" data-target="{{item.id}}">Click</a>
<my-dialog element-id="item.id">
<h1>This is the body of the modal</h1>
</my-dialog>
</div>
This works great for a small amount of modals but we are using a very large number of modals. So I would like to add the directive at runtime, something closer to...
What I want...
<div id="warning"></div>
<div ng-repeat="item in items">
<a data-toggle="modal" data-target="{{item.id}}" ng-click="showModal(item)">Click</a>
</div>
...
// inside controller
$scope.showModal = function(item){
$http.get('/someUrl').success(function(data){
var result = $compile('<my-dialog element-id="'+item.id+'">'+data+'</my-dialog>').($scope);
$("#warning").append(result);
});
}
$scope.hideModal = function(){
$( "#warning" ).empty();
}
This of course isn't working yet. Also it doesn't feel like the best way. This would allow me to remove the directive once it has been closed.
Please include a plunker or equivalent for the check.
One way you could do this is to use ng-repeat with your items, then call $scope.$apply() after you push a new item to the list. The HTML could look like this ...
<div ng-repeat="item in items">
<span dialog>
<a class="dialog-anchor">{{item.name}}</a>
<div class="dialog-body">{{item.id}}</div>
</span>
</div>
... and the directive like this
.directive('dialog', [function () {
return {
scope: {
id: '#elementId',
}
, link: function (scope, el, attrs) {
var body = $(el).find('.dialog-body').detach();
$(el).find('.dialog-anchor').on('click', function () {
$('body').append(body);
});
}};
}])
... and the controller like this
.controller('app', ['$scope', function ($scope) {
$scope.items = [
{name: 'first', id: 001},
{name: 'second', id: 002}
];
setTimeout(function () {
$scope.items.push({name: 'three', id: 003});
if (!$scope.$$phase) $scope.$apply();
}, 2000);
}])
Here's the plunker... http://plnkr.co/edit/2ETbeCKGcHW3CJCfD9d7?p=preview. You can see the $scope.$apply call in the setTimeout where I push a new item to the array.
Try this:
var result = $compile('<my-dialog element-id="'+item.id+'">'+data+'</my-dialog>')($scope);

how to call http request on ng-click event?

I am using angularjs on front end. I have two input boxes on index.html (namely first-name and last-name), and one button. On the click of button (ng-click="search()") I want to call an http GET request with first-name and last-name as parameters. And then I want to show the response in the same page in some other DIV tag. How would I achieve this?
HTML:
<div ng-app="MyApp" ng-controller="MyCtrl">
<!-- call $scope.search() when submit is clicked. -->
<form ng-submit="search()">
<!-- will automatically update $scope.user.first_name and .last_name -->
<input type="text" ng-model="user.first_name">
<input type="text" ng-model="user.last_name">
<input type="submit" value="Search">
</form>
<div>
Results:
<ul>
<!-- assuming our search returns an array of users matching the search -->
<li ng-repeat="user in results">
{{user.first_name}} {{user.last_name}}
</li>
</ul>
</div>
</div>
Javascript:
angular.module('MyApp', [])
.controller('MyCtrl', ['$scope', '$http', function ($scope, $http) {
$scope.user = {};
$scope.results = [];
$scope.search = function () {
/* the $http service allows you to make arbitrary ajax requests.
* in this case you might also consider using angular-resource and setting up a
* User $resource. */
$http.get('/your/url/search', { params: user },
function (response) { $scope.results = response; },
function (failure) { console.log("failed :(", failure); });
}
}]);

Display list of items and edit them via AngularJS

I try to solve a classic problem using AngularJS: I need to display list of some entities and provide ability to add, edit and view details of this entities.
I implement two controllers: ListController to iterate list of entities and ItemController to display and save entity details. This is html code:
<div ng-app="myApp">
<a class="btn" data-toggle="modal" data-target="#modal">Add new item</a>
<div ng-controller="ListController">
<h4>List</h4>
<ul>
<li ng-repeat="item in list">
{{item.name}}
<a class="btn" data-toggle="modal" data-target="#modal" ng-click="editItem(item)">Edit item</a>
</li>
</ul>
</div>
<div id="modal" role="dialog" class="modal hide fade">
<div ng-controller="ItemController">
<div class="modal-header">
Item Dialog
</div>
<div class="modal-body">
<label for="txtName" />
<input type="text" id="txtName" ng-model="item.name" />
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="saveItem()" data-dismiss="modal">OK</button>
</div>
</div>
</div>
and controllers code:
var db_list = [{ name: "Test1" }, { name: "Test2" }];
var app = angular.module('myApp', []).
controller('ListController', function($scope, $rootScope) {
$scope.list = db_list;
$scope.editItem = function(item) {
$rootScope.item = item;
}
}).
controller('ItemController', function($scope, $rootScope) {
$scope.saveItem = function() {
db_list.push($rootScope.item);
$rootScope.item = null;
}
});
Also you can find the working ptototype at http://jsfiddle.net/yoyoseek/9Qntw/16/.
The general problem in this code that I store entity to display its description using scope of the ListController (via editItem()), but I need this stored entity details in the ItemController. I use $rootScope for sharing entity to edit and it looks like hack for me. Is it a normal practice?
This code has one more drawback: $rootScope.item have to been cleared on modal dialog hide.
It looks like the main problem here is that events triggered by data-toggle happen outside of your control and it's not part of the AngularJS bindings (I am new to it so I may be wrong).
Anyway, it seems like there is no way to cross-reference controllers in Angular, and the only way to get hold of them is via inspecting the DOM. But, once you get into that, you may as well initialize the scope directly (http://jsfiddle.net/B4kAW/4/):
var db_list = [{ name: "Test1" }, { name: "Test2" }];
var app = angular.module('myApp', []);
app.controller('ListController', function($scope) {
$scope.list = db_list;
$scope.editItem = function(item) {
angular.element(document.getElementById("modal")).scope().item = item;
};
});
app.controller('ItemController', function($scope) {
$scope.saveItem = function(item) {
//db_list.push(item);
//$rootScope.item = null;
};
});
Note:
The modal dialog here has no way of knowing whether it's opened for editing, or adding a new item (I commented out push).
Since the dialog is linked with "main" item in the list, it updates it instantly (can be seen while the dialog is open, on the background). You may need to copy it instead of using a reference.
Inspired by this answer. It looks like "the Angular way" around dialogs is to convert them into services.

Resources