How to forbideen updating model in Angular JS? - angularjs

I have nested controller like:
<div ng-controller="MainController">
<div ng-controller="ChildController"></div>
</div>
Inside Child controller there is function:
$scope.isStatus = function() {
return function(item) {
}};
This function is used in ng-repeat.
So, problem is when I modify model of controller MainController it reload model of ChildController, It means that function isStatus() works again and reload model. How to fix it?

You can use controllerAs syntax to avoid this
<div ng-controller="MainController as main">
<div ng-controller="ChildController as child">
<ul ng-repeat ="item in child.item">
</div>
</div>
And in js
function ChildController (){
var vm = this;
vm.isStatus = function() {
return function(item) {
}};
}
you can see more infomation in here
https://github.com/johnpapa/angular-styleguide/tree/master/a1#controllers

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.

Is it possible to call a function of outer AngularJS controller on page?

Let's say controller named cOne has a function
function showAlert() {
alert("Hello, people!");
}
And I have a situation like shown below
<div id='parent' ng-controller='cOne'>
<span ng-click='cOne.showAlert()'>Click For Alert</span>
<div id='child' ng-controller='cTwo'>
<span ng-click='$parent.cOne.showAlert()'>Click For Parent Controller Alert </span>
</div>
</div>
Can I somehow call the function of that cOne controller from the "child" div? I tried to use $scope, $rootScope and $parent. How do I do it right?
You just need to assign showAlert to $scope of cOne controller., and from child just call via $parent.
angular.module('myApp', [])
.controller('cOne', function($scope) {
$scope.showAlert = function() {
alert('cOne alert');
};
})
.controller('cTwo', function($scope) {});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="cOne">
<span ng-click='showAlert()'>Click For Alert-1</span>
<div ng-controller="cTwo">
<span ng-click='$parent.showAlert()'>Click For Alert-2</span>
</div>
</div>
You should bind your function to either scope or controller instance. If it is bound to scope
$scope.showAlert = function () {
alert("Hello, people!");
}
you'll be able to call it like
<span ng-click='$parent.showAlert()'>Click For Parent Controller Alert </span>
If you bind it to controller instance, you'll be able to call it in a way, you've just posted.

Can't access the variable from newly created filter variable of ng-repeat in angular

I have a ng-repeat like this :
<a class="item item-avatar" ng-click="loadProfile({{student.pi_id}})"
ng-repeat="student in filteredStudents = (students | filter:model.txtSearch)" >
<img src="./img/man.png">
<h2>{{student.pi_name}}</h2>
<p>{{student.pi_hp}}</p>
</a>
the question is how can I access the filteredStudents variable? as I can't access it by using $scope.filteredStudents; in the controller
When you are applying the filter in view the filtered value is not accessible in controller.
If you need the filtered data in your controller you need to do filtering in your controller like:
$scope.filteredData = $filter('filter')($scope.filteredStudents, $scope.model.txtSearch)
Inject filter in your controller
function YourController($scope, $filter)
{
}
then, use the filter in controller itself
$scope.filteredStudents = $filter('yourFilterName')(model.txtSearch);
then in html,
<a class="item item-avatar" ng-click="loadProfile({{student.pi_id}})" ng-repeat="student in filteredStudents" >...</a>
Check this SO question
Controllers execute before the view is rendered, which is why the controller function $scope is empty when it executes. You could access the $scope.filteredStudents from within an event handler, such as ngClick:
var app = angular.module('app',[]);
app.controller('ctrl', function($scope) {
$scope.test = function() {
alert ($scope.items);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<input type="text" ng-model="name" /> {{name}}
<div ng-repeat="i in items = ([1,2,3,4] | filter:name )">
{{i}}
</div>
<button ng-click="test()">Hey</button>
</div>
Alternatively, you can access $scope.filteredStudents by using $timeout:
app.controller('ctrl', function($scope,$timeout) {
$timeout(function() {
// $scope.filteredStudents is accessible here...
alert($scope.filteredStudents);
});
});
This works because the $timeout hander executes after the view is rendered. The javascript enclosure allows the $timeout handler to execute, and still access the controller's function context.

Expressions inside ng-bind-html not rendering

We have dynamic templates. Inside ng-repeat I am using ng-bind-html which calls a method that returns HTML, but the expressions in that HTML were not rendering. Any idea why?
<div ng-bind-html="vm.getSimpleLayoutHtml(item)" class="ng-binding ng-scope">
<div class="col-xs-4" ng-if="item.Account.Name">{{ item.Account.Name }}</div>
</div>
Here is the controller:
function TestController($scope, resource,$sce)
{
var vm= this;
vm.getSimpleLayoutHtml = function (item) {
var result = resource.getLayout(item.attributes.type, "Simple");
return $sce.trustAsHtml(result.Html);
};
};
Give a try to ng-HtmlCompiler #https://github.com/rroxysam/ng-htmlCompiler

How do I run a function after angularjs has re-rendered the template triggered by $scope.$watch?

Here's an example:
<div my-directive my-attr="someNumericValueFromParentScopeVariable">
<div class="container">
<div ng-repeat="item in items">
{{item.id}}
</div>
</div>
</div>
angular.directive( "myDirective", function(){
return {
scope : {
myAttr : "="
},
controller : function($scope){
$scope.$watch( "myAttr", function( newValue, oldValue, scope ) {
var newItems = []; // contruct new array with n-size
scope.items = newItems;
// After this, the template will be rerendered.
// I want to recompute the new width of the div container and store it in the scope.
}
}
};
});
If my-attr points to a scope variable and gets modified, the scope.items will be replaced and the <div ng-repeat="item in items"> will be re-rendered. How do I compute the new width of the element <div class="container"> after <div ng-repeat="item in items"> has rendered its elements? I need to perform this computation after $scope.$watch has made its changes to the model and after the template has re-rendered due to changes in the model.
And is there a better way of doing this?
You can call a function after the list has rendered by using the $timeout service:
$scope.$watchCollection( 'things', function(){
$timeout( function(){
// calculate width here
});
});
Fiddle

Resources