Second instance of angular controller does not display declared variables - angularjs

I have a page that has two instances of the same controller, the first one is loaded with the page and the second comes from an ajax request. Inside the controller I have a variable called filter, with initial value equal to "status". When I try to display this variable in the first instance it displays correctly. But when I show this variable in the second instance it looks undefined, it shows Filter: # {{mClerk.filter}}. The changeFilter function works in both instances.
My controller
app.controller('clerkCtrl', function ($scope, $http, $compile, $timeout, notifyService, DTOptionsBuilder, DTColumnBuilder) {
this.filter = 'status';
this.changeFilter = function (filter) {
this.filter = filter;
};
});
First Instance,
<div ng-controller="clerkCtrl as Clerk">
Filter: #{{Clerk.filter}} <!-- OK -->
<div class="card">
<div class="card-header">
<h2><i class="zmdi zmdi-accounts-list zmdi-hc-fw"></i> Lista</h2>
</div>
<div style="padding: 0px 20px;">
<div class="card-body table-responsive">
<table datatable="" dt-options="Clerk.dtOptions" dt-columns="Clerk.dtColumns"
class="table table-striped"></table>
</div>
</div>
</div>
</div>
Second instance, from a ajax request:
<div ng-controller="clerkCtrl as mClerk">
Filtro: #{{mClerk.filter}} <!-- FAILED -->
<button ng-click="mClerk.changeFilter('date')">Change filter</button>
</div>
Function that brings the page to the second instance,
$scope.loadPage = function (page) {
$http.post(page, [], $scope.config)
.then(function (response) {
$scope.modal.body = $sce.trustAsHtml(response.data.body);
$scope.modal.title = $sce.trustAsHtml(response.data.title);
$('#modalx').modal('show');
$scope.reloadPlugins();
setTimeout(function () {
$compile(angular.element('.modal-body').contents())($scope);
}, 0);
});
};

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.

ng-click inside ng-repeat doesnt work

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

post.length stays 0 due to loop - AngularJS

I'm trying to add pagination but I can't seem to figure out this last part.
Everything is setup, though my pagination isn't recording the amount of posts that are linked with the user.
Seeing that I'm doing a forEach and if loop and pushing the retrieved items into a empty collection, my 'posts.length' is returning 0.
Hence the pagination only showing page 1/1 and not 1/2 (for example).
Here is my full code:
profileCtrl.js
Here is the $http.get - I'm trying to get all the posts that the logged in user made doing this loop:
app.controller('profileCtrl', function($scope, auth, $http, $log) {
$scope.auth = auth;
$scope.date = auth.profile.created_at;
$scope.pageSize = 5;
$scope.posts= [];
$http.get('URL')
.then(function(result) {
angular.forEach(result.data, function(data, key) {
if(data.userId === auth.profile.user_id) {
$scope.posts.push(data);
}
});
});
});
profile.html
As you can see, I'm trying to get the length of post in posts using total-items="posts.length":
<div class="col-md-8 no-padding-right">
<div class="panel panel-primary">
<div class="list-group-item active text-center">
<h4 class="no-margin-top no-margin-bottom">Recent Activity</h4>
</div>
<a href="#" class="list-group-item" ng-repeat="post in posts| startFrom: (currentPage - 1) * pageSize | limitTo: pageSize | orderBy :'created_at':true">
<div class="row">
<div class="col-md-4">
<div class="thumbnail no-border no-margin-bottom">
<img src="https://placehold.it/150x150" alt="bird" width="150" height="150"/>
</div>
</div>
<div class="col-md-8">
<h4 class="no-margin-top no-margin-bottom"><strong>{{post.birdname}}</strong></
</div>
</div>
</a>
<uib-pagination total-items="posts.length" ng-model="currentPage" max-size="pageSize" boundary-link-numbers="true"></uib-pagination>
</div>
</div>
app.js
I also added a filter in app.js:
app.filter('startFrom', function() {
return function(data, start) {
return data.slice(start);
}
});
When I console.log(posts.length); I keep getting 0 and I'm guessing it's because of the $scope.posts = []; declared on top (profileCtrl.js).
Edit:
After doing a bit of debugging with console.log, I do get the value given when doing this:
$http.get('url')
.then(function(result) {
angular.forEach(result.data, function(data, key) {
if(data.userId === auth.profile.user_id) {
$scope.posts.push(data);
}
});
console.log($scope.posts.length);
});
How should I fix this?
If you're waiting for data to be returned before loading the collection (with pagination) either add a ng-if="posts.length" to the container, or initialise $scope.posts as being null and add ng-if="posts" if you want the list to show when the API returns 0 results. This will prevent Bootstrap's pagination directive being parsed until the data it needs is available.
Edit: After debugging, the following plunkr contains a working implementation: http://plnkr.co/edit/VQjNVK6gRKsCqxVb54nR?p=preview

Nested ng-repeat with different data

I am using AngularJS v1.5.0 in my web application. I have feedbacks of the users which are displayed using ng-repeat. Now whenever the executive clicks on the feedback, I make a server call and fetch the history of user and show it on the panel.
Once the call is successful, I assign the data to the feebackDetails variable which is now the controller scope variable. But I dont want it to have the controller scope, as each feedback will have different user and different data.
<div class="tab-pane fade in" ng-controller="controller-feedback" id="feedback">
<div class="col-md-12">
<div class="row">
<div ng-repeat="feedback in feedbacks track by $index" ng-click="getUserHistory(feedback.userId)" data-toggle="collapse" data-target="#feedback{{$index}}" aria-expanded="false" aria-controls="#feedback{{$index}}">
<div class="alert alert-info">
<div>{{feedback.feedback}}</div>
<div class="collapse" id="feedback{{$index}}">
<div class="well">
<div ng-repeat="feedbackDetail in feedbackDetails track by $index">
<span style="font-weight: bold">Question:</span> {{feedbackDetail.question}} <br>
<span style="font-weight: bold">Answer:</span> {{feedbackDetail.answer}} <br>
<span style="font-weight: bold">Helpful:</span> {{feedbackDetail.helpful}} <br>
<span style="font-weight: bold">Feedback:</span> {{feedbackDetail.feedback}} <br>
<span style="font-weight: bold">Executive:</span> {{feedbackDetail.executive}} <br>
<hr ng-hide="$last">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
This is my controller part.
app.controller('controller-feedback', function ($scope, $http, $rootScope) {
$scope.feedbacks = [];
$scope.feedbackDetails = [];
// This will get the feedbacks
$http
.get(phpUrl + 'test-feedback', {params:{doctorId:doctorId}})
.then(function success(response) {
$scope.feedbacks = response.data.data;
$rootScope.feedbackCount = response.data.data.length.toString();
}, function error(success) {
});
$scope.getUserHistory = function(userId) {
console.log(userId);
$http
.get(phpUrl + 'test-feedback-details', {params:{userId:userId}})
.then(function success(response) {
/**
* This variable $scope.feedbackDetails must be different for each ng-repeat
*/
$scope.feedbackDetails = response.data;
}, function error(success) {
});
};
});
change getUserHistory to accept feedback object.
$scope.getUserHistory = function(feedback) {
var userId = feedback. userId ....
and rather than
$scope.feedbackDetails = response.data;
assign
$scope.feedback.feedbackDetails = response.data;
Inner ng-repeat should use feedback.feedbackDetails.
You could create a directive called feedbackDetail that you put inside your ng-repeat which uses an isolate-scope and you pass in your feedbackDetail from the ng-repeat.
Then when you make the HTTP request from the directives controller and assign it to $scope it would be a scope for just that part of the repeat.

Resources