Angular JS: ng-include by function - javascript

I am having strange experience while using ng-include. I am trying to get template path by triggering a function defined in controller which is returning path as per parameter passed. Here is my code-
<tr ng-repeat="detail in Ctrl.details" ng-include="Ctrl.getTemplate(object)"></tr>
Contoller-
self.getTemplate = function (obj) {
if (<condition>) {
return 'view1';
} else return 'view2';
};
This is working really fine, but I observed really strange behavior while debugging the code. In my table row I am having 3 buttons and I applied Bootstrap tooltip on them. Whenever I hover them tooltip comes up & on mouse left getTemplate() gets called. Do anybody know why this is happening?

This is expected behaviour.
Have a look at this article. Angular needs to check whether the expression for ng-include has changed or not. In order to do that it needs to evaluate Ctrl.getTemplate(object) on every digest loop because there's no other way to find out whether its return value has changed and therefore it needs to pass a new value to ng-include.

Related

indexOf for angular typescript

I am trying to write a similar true/false statement, Similar to how the .selected() method would work in Angular typescript.
The idea is to show an image if the calculation or *ngIf statement evaluates to True. The code is written in the app.html side
Code:
<img src="/project/x.png" *ngIf="selectedimage.indexOf(v) !== -1"><a href="{{i['link']}}" target="blank" (click)="update_viewed(z)">
There is a *ngFor statement right before this code, with ;let z = index at the end. The overall code creates a # of rows dynamically depending on how many elements exist in an array. (code not provided for this here.) Then, if the user clicks on the href link, the index value is passed into a method, which pushes it to an array. in console.log(this.selectedimage) I can see the values being added in each time I click the href reference. Not sure why the ngIf logic isn't working.
All help much appreciated.
You cannot use indexOf with *ngIf on the template, I would recommend you to create a function that returns true/false based on the logic. Use indexOf within the function and call it with *ngIf

Why is a function in ng-repeat called several times?

I want to supply a ng-repeat element by a controller function as follows:
<div ng-repeat="picture in allPictures(data.pictures)"></div>
$scope.allPictures = function(pictures) {
alert("function called");
//return... extract all pictures and return as array
}
Result: my allPictures function is called several times, even though I'd expect it to be called only once and then iterate over the results.
Why? And moreover: how can I prevent this and really call the method only once for picture supply?
I would really avoid calling a function insides a ngRepeat attribute, since it will give errors and unexpected behaviour.
But to be honest I dont think that you would need to call a function inside a ngRepeat. I would suggest to do the following:
<div ng-repeat="picture in allPictures"></div>
$scope.getPictures = function(pictures) {
alert("function called");
//return... extract all pictures and return as array
};
$scope.allPictures = $scope.getPictures();
This way the $scope.getPictures function will get called and the $scope.allPictures will be created. ngRepeat can call that collection instead of a function.
See also my Fiddle: https://jsfiddle.net/ABr/w6kc8qyh/1/
a little bit about the digest cycle:
we need to check every time something in the application changes - what was effected by that change, and re-evaluate all the places that might depend on that change,
so that is why the function in the ng-repeat was called multiple times - it had to check wheter the repeated list is the same after some changes happend in the application
read more about the digest cycle and two-way data binding:
http://blog.bguiz.com/post/60397801810/digest-cycles-in-single-page-apps/

JavaScript not starting function

I have search for some sort of way to do this, but i can't make it work. It may seem simple but i don't know much about javascript so I have found very little information on the problem and I don't know were is the error at so i'll just briefly explain what it's supposed to do. First here is my code:
function showI(a){
$("#show").fadeIn();
$("#show .load").fadeIn();
$("#show").load("ajax/showi.php?id="+a,$("#show .load").fadeOut("fast"))
}
$('.List-item').on('click touchstart',function(){
var id=$(this).attr("id").split("list-").join();
showI(id);
})
So there's like a button with the class list-item which when click should open a new window with the showI function, but it doesn't(I used before the attribute onClick, but it didn't work on mobile so I changed it to .on(click touchstart))
Any help would be appreciate. (Don't know if this is replicated because i can't find a word to describe the problem)
This line:
$("#show").load("ajax/showi.php?id="+a,$("#show .load").fadeOut("fast"))
calls $("#show .load").fadeOut("fast") and then passes its return value into load as a second argument, exactly the way foo(bar()) calls bar and then passes its return value into foo.
If you're looking for a completion callback, you need to wrap that in a function:
$("#show").load("ajax/showi.php?id="+a, function() {
$("#show .load").fadeOut("fast");
});

How to Update a Parent Scope Variable from within ng-repeat Expression?

I've run into a situation where I had to update a scope variable from within an ng-repeat expression (ng-class call within ng-repeat in fact).
<div id="page" ng-controller="MainCtrl">
.
<section id="body" ng-init="countMis = {num:0}">
.
<tbody>
<tr ng-repeat="upperCaseLetter in alphabet.specialsUpper" ng-controller="letterController">
<td>{{upperCaseLetter}}</td>
<td>{{filteredLetter=(upperCaseLetter | lowercase)}}</td>
<td>{{alphabet.specialsLower[$index]}}</td>
<td><span ng-class="lowercaseEqual($index,filteredLetter)"></span></td>
</tr>
</tbody>
and in app.js:
function letterController($scope)
{
$scope.lowercaseEqual = function(indx,letter)
{
var returnStr="";
if(letter == $scope.alphabet.specialsLower[indx])
{
returnStr = "glyphicon glyphicon-ok";
}
else
{
$scope.countMis.num = $scope.countMis.num + 1;
returnStr = "glyphicon glyphicon-remove";
}
return returnStr;
};
}
Ng-repeat is in a child controller and data I want to update is in its parent controller. I know you've read same question many times, please keep reading and see JsFiddle example.
So expression function checks filtered data of that particular ng-repeat iteration, if it doesn't match with some other corresponding parent scope variable it returns a class but also it should update parent scope counter.
As those expressions don't evaluate just once, but at least once (because of dirty check in digest) it results counter being incremented more than once for each case.
I've solved my problem by counting classes applied to that particular data after ng-repeat (you'll see in the JsFiddle example).
However I think I may run into same problem in the future, so I want to learn how to update parent scope within an expression of ng-repeat iteration.
I've put a second JsFiddle, I've tried using a factory on both parent controller and child, hoping to update a common variable for both controllers. When I log (consol.log) each iteration and factory method variable, it shows iteration times + 1 (still false value), but it's not reflected on page expression...doesn't make sense at all.
I'd appreciate any help. Thanks.
JsFiddle 1 JsFiddle 2
Since watched expressions (like the one implicitely set up by the ng-class directive) "can execute multiple times per $digest() and should be idempotent".
You should understand what the $watch() and $digest() functions do and look for resources regarding Angular's digest cycle.
So, incrementing a counter inside a watchExpression is not a good idea.
Counting classes seems ok though.
BTW, the factory-approach will help you share data across scopes, but it won't help with the "multiple executions per digest cycle" problem. Furthermore, it might be redundant in your situation, since the child- and parent-scopes can share data directly.
In any case, for the results to be displayed you have to "bind" the factory's getMismatchCount() function with the getMismatchCount() function that you use in your HTML (in Mismatches Count from Factory: {{getMismatchCount()}}):
function mainCtrl($scope, $window, repeatFactory)
...
$scope.getMismatchCount = repeatFactory.getMismatchCount;

AngularJS Directive - re-run link function on scope parameter change

I have a directive that builds a set of nested <ul> elements representing a folder structure. I used the link function to create the new DOM elements and append them to the directive instance element:
function link(scope, iElement, iAttr) {
var rootElement = buildChildElement(scope.tree);
iElement.append(rootElement);
}
Elements within the <ul> tree are wired with jQueryUI's drag/drop interactions that call a function on the Controller housing the directive to update the scope parameter based on the drag & drop events.
I would like the <ul> tree to automatically update when there is a change to the scope parameter. I have tried a watch function within my link function:
scope.$watch('tree', function(newTree, oldTree) {
var newRoot = buildChildElement(newTree);
iElement.contents().remove();
iElement.append(newRoot);
}
This works to a certain extent, but the call to remove() fires off the $watch() method a second time which ends up reverting my Controller changes. If I comment out the remove(), I can see that a new <ul> tree is written that properly reflects the changes to the parameter made in the Controller.
The double firing $watch() makes me think I'm going about this wrong. Without it, my parameter is properly updating but my <ul> doesn't update (the dropped element stays where it was dropped).
What's the correct way to make sure your directive is refreshed on a change in one of the scope parameters?
Should I be using the compile function and building the <ul> tree based on the attributes array instead of using the link function?
Your approach is very jQuery-style. I think you'll find that you're working against Angular in this case. sh0ber is right with his/her question; you should post a demo or something, or at least some sample code so you can have an effective answer.
I think you want to make a recursive tree directive. Check out this SO answer for some interesting approaches to this. The main idea is that watch is unnecessary. Simply change the object and Angular will take care of the rest. The most efficient thing is to change the specific node objects directly rather than replacing the whole object, but that will work too.
scope.$watch('tree', function(newTree, oldTree) {
var newRoot = buildChildElement(newTree);
iElement.contents().remove();
iElement.append(newRoot);
},**true**)
I think you can have a try and reference the watch API for more information
Here is another artical
http://www.bennadel.com/blog/2566-scope-watch-vs-watchcollection-in-angularjs.htm

Categories

Resources