Angular-bootstrap pagination - not getting variable in 'scope' - angularjs

HTML
<table ng-show="ecu.dtcs">
<tbody>
<tr>
<td colspan="4">
<uib-accordion close-others="true">
<uib-accordion-group ng-repeat="dtc in dtcList track by $index">
...
</uib-accordion-group>
</uib-accordion>
</td>
</tr>
</tbody>
</table>
<uib-pagination
total-items="{{ totalItems }}"
ng-model="{{ currentPage }}"
items-per-page="{{ itemsPerPage }}"
boundary-link-numbers="true"
ng-change="setPagingData(currentPage)">
</uib-pagination>
JS (directive)
link: function (scope) {
scope.showEcuDetails = function (ecu) {
// For pagination
scope.itemsPerPage = 20;
scope.totalItems = ecu.dtcs.length;
scope.currentPage = 1;
scope.setPagingData = function(currentPage) {
var start = (currentPage - 1) * scope.itemsPerPage,
end = currentPage * scope.itemsPerPage;
scope.dtcList = ecu.dtcs.slice(start, end);
};
scope.setPagingData(scope.currentPage);
};
}
ecu.dtcs: Holds all the data that I want
dtcList: Holds the sliced list each 20 elements
I have used the Angular-Bootstrap documentation and have also referenced this source (except have used ng-change to alter the list instead of a $watch on the scope)
I have debugged the application thoroughly and I can see that dtcList is becoming populated wiht the first 20 elements. However, going into the HTML, there is nothing displayed
I have also tried to write a .filter (like is shown here) but because the variables are being set in the scope of the directive and not a controller, my filter, cannot see the variables
N.B: For the pagination settings, to reference the scope variables (i.e. totalItems), I have used Angular expressions like displayed in this question and also basic strings as in the documentation (i.e. "totalItems")
I think the concepts and the way I have it written at the moment should work, it's just the HTML cannot see dtcList for some reason
EDIT
It may help to know that this is in an angular-ui-bootstrap modal. Below the function call to setPagingData(), I have this:
$uibModal.open({
templateUrl: "ecuDialog.html",
size: "lg",
windowClass: "modalDialogs networkTest__largeContentDialog",
scope: scope
});
The templateUrl is that of which I've written in the HTML section of this question. It crossed my mind whether this was the issue but my colleagues have assured me that it would not impact the result in any way.
EDIT 2
Unfortunately, 1 of our colleagues who specialises in AngularJS has viewed the code along with the documentation and cannot see any errors within it. Could it be an Angular bug or is it the combination of multiple elements from angular-ui-bootstrap that it doesn't like?
Things I am combining are:
ui-modal
ui-accordion
ui-pagination

Solution:
My code was correct. My colleague had an attempts at it and just took the code I had written into another directive. We were both unsure why this was the case, maybe Angular doesn't like overcrowded directives?

Related

Closing modal in Angular UI Bootstrap while erasing array of 1000 items, takes a significant time

I've an Angular application where I pass an empty array to Angular-UI Bootstrap Modal. Depends on user choice it may be filled or not with items, up to 1000 objects. All them I display in a list with ng-repeat. All items in a list are pre-generated, so I can't retrieve them asynchronously for example by using plugin like smart-table.
When user press ok button all generated values returned to controller from which it was called. Technically all them already there because I pass $scope.items by reference:
resolve: {
items: function () {
return $scope.items;
}
}
But when I press cancel button I'm erasing all items in an array by invoking $scope.items.length=0; My problem that it takes a while. In my plunker example below it's barely noticeable but noticeable but in my actual application it's much more noticeable and unacceptable.
My guess that It's due to Angular's two-way binding, it takes a time to clean all watchers but I don't know how to solve this issue, if it can be solved.
My MCVE at plunker here: http://plnkr.co/edit/JwanDxBzh3a7ilEX58z8?p=preview
UPDATE: Tried to use one-time binging, plunker here: http://plnkr.co/edit/PjzHRYiuXFHE1M1Pap6U?p=preview
<li ng-repeat="item in items">
{{ ::item | date:'yyyy-MM-dd' }}
</li>
as described here: https://stackoverflow.com/a/18791503/947111 it didn't help.
Ok, I was right and I was almost there with Angular's one-time binding I just used it in a wrong place, and should be used in a ng-repeat in a such way:
<li ng-repeat="item in ::items">
{{::item | date:'yyyy-MM-dd' }}
</li>
Thanks to this answer: https://stackoverflow.com/a/23903690/947111
Working plunker located here: http://plnkr.co/edit/XWi6Z0eCXveV58WJfpo6?p=preview

How can I change text elsewhere using AngularJS function with parameters?

Alright so I found out what might be my solution for a task I am trying to accomplish: to use some sort of toggle feature which I have seen represented a couple of different ways in other questions here on Stack Overflow. However, mine is a bit of a special case that I can't seem to figure out how to adjust to get it to work.
Here is the accordion where the text needs to appear:
<div ng-controller="Ctrl">
<accordion id="myID">
<accordion-group heading="My Heading">
{{toggleText}}
</accordion-group>
</accordion>
</div>
The text that needs to appear depends on what is clicked however:
<area ng-repeat="x in Object" alt="{{x.name}}" title="" ng-click="thisClick(x.name,x.address);toggle = !toggle" shape="poly" coords="{{x.htmlcoords}}" />
I have an image that has hot spots on it. I used ng-click="thisClick(x.name,x.address)" to easily capture the data from my Object and I was able to alert it in my thisClick(name,address) function. This part of the HTML is in a div that separately calls the same controller as the one above, I don't know if that would be relevant. I couldn't get my code working before trying this toggle stuff unless I kept the controller where it was and just called it again. Anyway, now to apply the toggle feature I tried changing the ng-click to what is shown above and the function to:
$scope.thisClick = function(name,address){
$scope.toggle = true;
$scope.$watch('toggle',function(){
$scope.toggleText = $scope.toggle ? '' : name+address;
});
};
Ultimately the name and address won't be squished together but this is for testing purposes only.
When I run the code, simply put nothing happens.
Either there is a way to clean this up or a way to approach this entirely differently? I hope I provided enough information.
I wish it were as simple as:
<area ng-repeat="x in building" alt="{{x.name}}" title="" ng-click="thisBuilding = x.name+x.address" shape="poly" coords="{{x.htmlcoords}}" />
$scope.buildingName = name;
$scope.buildingAddress = address;
$scope.thisBuilding = function(){
return $scope.buildingName + " " + $scope.buildingAddress;
};
};
:
{{thisBuilding()}}
This sounds quite a lot like a scope issue. As ng-repeat creates a new child scope for each item, most likely you end up setting the $scope.toggleText property in "wrong" scope, i.e. not in the scope of accordion-group where you are trying to display the property.
You might want to play around a bit with some Angular inspector tool, for example ng-inspector to verify this.
If the problem indeed is with toggleText property ending up in the wrong child scope, one possible workaround could be introducing a container object for the property in the root scope. The reason why this could work is that objects are passed as references for child scopes, not as copies like primitive properties are. A whole lot more on this topic can be read from Understanding Scopes article in AngularJS wiki.
So, something along these lines, starting from controller Ctrl:
// inside the controller code:
// initialize the toggleContainer object
$scope.toggleContainer = {};
Then in html template:
<div ng-controller="Ctrl">
<accordion id="myID">
<accordion-group heading="My Heading">
{{toggleContainer.text}}
</accordion-group>
</accordion>
</div>
And finally in the thisClick function, store the value you want to property toggleContainer.text instead of simple toggleText:
$scope.thisClick = function(name,address){
$scope.toggle = true;
$scope.$watch('toggle',function(){
$scope.toggleContainer.text = $scope.toggle ? '' : name+address;
});
};

Access javascript built-ins inside an angularjs expression

I'm a beginner at Angularjs and I would like to know how can I use javascript built-ins like Date or Array in expressions. It seems weird to add them like this $scope.Array = Array in every controller.
Edit: In response to your comment below, I would just add a little change to the way you have done it as I personally like to remove as much logic as possible from the view.
JavaScript
$scope.numItems = 5;
$scope.range = Array($scope.numItems);
HTML
<tr ng-repeat="i in range track by $index">
<td align="center">{{$index}}</td>

How to set a boolean flag to collapse/expand a row with ng-repeat

I have this plunker code.
What I'm trying to do, is to display the gray box one time per row.
To achieve this, I thought to modify the partition filter in order to return a JSON to add it a new property by row to know if the gray box is expanded or not.
But, I could Not successfully return a JSON.
Do you know how to modify the filter to return a JSON or a better way to show the gray box by row?
Related questions:
Push down a series of divs when another div is shown
Update 1
The issue could be easily resolved by using the correct scope for the ng-repeat for the row without modifying the filter, thanks to #m59.
http://plnkr.co/edit/eEMfI1lv6z1MlG7sND6g?p=preview
Update 2
Live Demo
If I try to modify the item, it seems the ng-repeat would be called again losing the props values.
<div ng-repeat="friendRow in friends | partition:2"
ng-init="props = {}">
<div ng-repeat="item in friendRow"
ng-click="collapse(item)"
ng-class="{myArrow: showArrow}">
{{item.name}} {{item.age}} years old.
<div>{{item.name}}</div>
</div>
<div collapse="!props.isExpanded">
some content
<br/>
<input type="text" ng-model="currentItem.name">
</div>
</div>
js
$scope.collapse = function(item){
this.props.isExpanded = !this.props.isExpanded;
this.showArrow = !this.showArrow;
$scope.currentItem = item;
};
This causes the gray box to collapse each time the item is modified. Any clue?
I've updated my code/answer regarding partitioning data. It's important to fully understand all of that before deciding on an approach to your project.
The problem you have in your plnkr demo is that you're modifying the parent $scope and not the scope of the ng-repeat for that row.
Just set a flag on the row and toggle it when clicked:
Live Demo
<div
class="row"
ng-repeat="friendRow in friends | partition:2"
ng-init="isExpanded = false"
ng-click="isExpanded = !isExpanded"
>
<div ng-repeat="item in friendRow">
{{item.name}} {{item.age}} years old.
</div>
<div collapse="!isExpanded">
some content
</div>
</div>
To access the correct scope within a function in the controller, you can use the this keyword instead of $scope. this will refer to the scope the function is called from, whereas $scope refers to the scope attached to the element with ng-controller (a parent of the ng-repeat scopes you want to target).
<div
class="row"
ng-repeat="friendRow in friends | partition:2"
ng-click="collapse()"
>
JS:
$scope.collapse = function() {
this.isExpanded = !this.isExpanded;
};
If you want to keep the ng-click directive on the item element instead of putting it on the row element as I have done, then you're dealing with another child scope because of that inner ng-repeat. Therefore, you will need to follow the "dot" rule so that the child scope can update the parent scope where the collapse directive is. This means you need to nest isExpanded in an object. In this example, I use ng-init="props = {}", and then use props.isExpanded. The dot rule works because the children share the same object reference to props, so the properties are shared rather than just copied, just like in normal JavaScript object references.
Live Demo
<div
class="row"
ng-repeat="friendRow in friends | partition:2"
ng-init="props = {}"
>
<div ng-repeat="item in friendRow" ng-click="collapse()">
{{item.name}} {{item.age}} years old.
</div>
<div collapse="!props.isExpanded">
some content
</div>
</div>
JS:
$scope.collapse = function(){
this.props.isExpanded = !this.props.isExpanded;
};
Update
We keep going through more and more issues with your project. You really just need to experiment/research and understand everything that's going on on a deeper level, or it will just be one question after another. I'll give it one last effort to get you on the right track, but you need to try in the basic concepts and go from there.
You could get past the issue of props reinitializing by putting $scope.expandedStates and then passing the $index of the current ng-repeat to your function (or just using it in the view) and setting a property of expandedStates like $scope.expandedStates[$index] = !$scope.expandedStates[$index]. With the nested ng-repeat as it is, you'll need to do $parent.$index so that you're associating the state with the row rather than the item.
However, you'll then have another problem with the filter: Using my old partition code, the inputs inside the partitions are going to lose focus every time you type a character. Using the new code, the view updates, but the underlying model will not. You could use the partition filter from this answer to solve this, but from my understanding of that code, it could have some unexpected behavior down the road and it also requires passing in this as an argument to the filter. I don't recommend you do this.
Filters are meant to be idempotent, so stabilizing them via some kind of memoization is technically a hack. Some argue you should never do this at all, but I think it's fine. However, you definitely should ONLY do this when it is for display purposes and not for user input! Because you are accepting user input within the partitioned view, I suggest partitioning the data in the controller, then joining it back together either with a watch (continuous) or when you need to submit it.
$scope.partitionedFriends = partitionFilter($scope.friends, 2);
$scope.$watch('partitionedFriends', function(val) {
$scope.friends = [].concat.apply([], val);
}, true); // deep watch

ng-Grid Pagination - AngularJS

<div ng-app="myapp">
<div ng-controller="Ctrl">
<table ng-grid >
<tr ng-repeat="todo in todos">
<td >{{todo.name}}</td>
<td >{{todo.estimate | number}}</td>
<td width="50">{{todo.done }}</td>
<td title="Create At">{{todo.created_at | date}}</td>
</tr>
</table>
</div>
</div>
And the javascript code is in http://jsfiddle.net/dalcib/J3fjc/ . I am new to AngularJS, I can understand the above html. Would someone please explain me what's really done there in the javascript?
It's one of the answers for How to do paging in AngularJS?.
From and Angular perspective, this is probably the most interesting javascript line:
angular.module('ngGrid', ['ngSkip']).directive('ngGrid', function() {
The line is doing two things. First defining an Angular module, and second, defining a new directive inside that module (named ngGrid). (As a side note, there is another very big project named ngGrid, and this does not seem to be related).
You'll see that inside the directive, an object is being built up and returned (direc). This defines the configuration of the directive. Once you read the Angular Directive documentation, the structure of the code should start to make sense.
I must say, you've picked a pretty sketchy example to start to understand. Perhaps, it would be better to start with the Angular Tutorial and build up from there.
A quick definition from the docs of what a directive is:
At a high level, directives are markers on a DOM element (such as an
attribute, element name, or CSS class) that tell AngularJS's HTML
compiler ($compile) to attach a specified behavior to that DOM element
or even transform the DOM element and its children.
Angular comes with a set of these directives built-in, like ngBind,
ngModel, and ngView. Much like you create controllers and services,
you can create your own directives for Angular to use. When Angular
bootstraps your application, the HTML compiler traverses the DOM
matching directives against the DOM elements.

Resources