error with an angularjs filter - javascript

Based on this jsfiddle : http://jsfiddle.net/2ZzZB/56/
i add the filter solution for my app : http://www.monde-du-rat.fr/zombieReport/popup.html#/ratousearch (ctrl : http://www.monde-du-rat.fr/zombieReport/resources/js/controllers/RatousearchCtrl.js )
//We already have a limitTo filter built-in to angular,
//let's make a startFrom filter
app.filter('startFrom', function() {
return function(input, start) {
start = +start; //parse to int
return input.slice(start);
}
});
Look console, error is getting at the started :
TypeError: undefined is not a function
at http://www.monde-du-rat.fr/zombieReport/resources/js/baseChrome.js:368:22
at http://www.monde-du-rat.fr/zombieReport/resources/libs/angularjs/angular-1.3.0-rc.0/angular.min.js:180:297
at B.| (http://www.monde-du-rat.fr/zombieReport/resources/libs/angularjs/angular-1.3.0-rc.0/angular.min.js:169:248)
at B.constant (http://www.monde-du-rat.fr/zombieReport/resources/libs/angularjs/angular-1.3.0-rc.0/angular.min.js:179:161)
at B.| (http://www.monde-du-rat.fr/zombieReport/resources/libs/angularjs/angular-1.3.0-rc.0/angular.min.js:169:253)
at B.constant (http://www.monde-du-rat.fr/zombieReport/resources/libs/angularjs/angular-1.3.0-rc.0/angular.min.js:179:161)
at Object.c (http://www.monde-du-rat.fr/zombieReport/resources/libs/angularjs/angular-1.3.0-rc.0/angular.min.js:101:146)
at m.$digest (http://www.monde-du-rat.fr/zombieReport/resources/libs/angularjs/angular-1.3.0-rc.0/angular.min.js:114:386)
at m.$apply (http://www.monde-du-rat.fr/zombieReport/resources/libs/angularjs/angular-1.3.0-rc.0/angular.min.js:118:12)
at k (http://www.monde-du-rat.fr/zombieReport/resources/libs/angularjs/angular-1.3.0-rc.0/angular.min.js:76:374)
The line is
return input.slice(start);
what's wrong ? i didn't see error in the jsfiddle example
HTML :
<div id="results" ng-show="successLordZR">
<p class="myTitle">{{ 'TRS_CTRL3_TEXT1' | translate }} :</p>
<ul class="list-group">
<li class="list-group-item" ng-repeat="post in posts | startFrom:currentPage*pageSize | limitTo:pageSize">
<div class="myClearfix">
<p style="float: left;"><span ng-class="{'girl' : post.sex == 'F', 'boy' : post.sex == 'M'}">#</span> {{post.prefixe}} {{post.name}} ({{post.idLord}})</p>
<p style="float: right;"><i class="fa fa-link"></i> fiche</p>
</div>
</li>
</ul>
<div id="pagination">
<button type="button" class="btn btn-primary btn-xs" ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1"><i class="fa fa-arrow-circle-o-left"></i> {{ 'TRS_CTRL3_PREV' | translate }} </button>
<span> {{currentPage+1}}/{{numberOfPages()}} </span>
<button type="button" class="btn btn-primary btn-xs" ng-disabled="currentPage >= posts.length/pageSize - 1" ng-click="currentPage=currentPage+1"> {{ 'TRS_CTRL3_NEXT' | translate }} <i class="fa fa-arrow-circle-o-right"></i></button>
</div>
</div>
Data json example from laravel : http://www.monde-du-rat.fr/lordrest/public/posts_jsonforced
EDIT 2 , filter in controller:
// populate scope
$scope.posts = response;
$scope.posts = $filter('startFrom')($scope.currentPage*$scope.pageSize);
filter is here : http://www.monde-du-rat.fr/zombieReport/resources/js/filtersZR.js

Seems the posts model in scope comes from RatousearchCtrl? In that controller the scope.posts was initially set to be an empty object and never updated.
That seems to be the reason why post as in ng-repeat 'post in posts' is undefined, which is the value got passed to the startFrom filter.
Can you re-check how posts are supposed to be loaded?

Input might not exist at that exact moment, just change your code to this:
app.filter('startFrom', function() {
return function(input, start) {
start = +start; //parse to int
return (typeof input == 'string') ? input.slice(start) : "";
}
});

Related

Typing in a input text is very slow using angular 4

When I type in text to search something, displaying one character in text is very slow.
What is the problem ?
I have display 50 products with ngFor as below , if I display more than 50 products 100 or 150 typing in text is more slow.
what should I do to fix this problem ?
<div class="width_products products-animation " *ngFor="let product of productsService.products ; trackBy: $index" [ngClass]="{ 'width_products_open_menu':productsService.status_menu }" >
<span class="each_width_product" >
<div class="title_products more_detail_product" (click)="set_router({ path:product['company'].company_name+'/'+product.product_title , data:product.product_id , relative:true })">
{{product.product_title }}
<span class="glyphicon glyphicon-chevron-down"></span><br>
<div class=' glyphicon glyphicon-time'></div> {{product.product_date}}
</div>
<div class="image_product_primary " (click)="set_router({ path:product['company'].company_name+'/'+product.product_title , data:product.product_id , relative:true })">
<img class="image_product" src="../../assets/images/products_image/{{product.product_image}}">
</div>
<button (click)="product.product_in_wishList='true'; productsService.add_wish_list( product )" mat-button class="wish_list notCloseDropdawnFavorite notCloseDropdawnCard">
<span class="write_add_wish">{{dataservices.language.add_wishlist}}</span>
<mat-icon *ngIf="product.product_in_wishList == 'false' " class="notCloseDropdawnFavorite notCloseDropdawnCard">favorite_border</mat-icon>
<mat-icon *ngIf="product.product_in_wishList == 'true' " class="hearts_div_hover notCloseDropdawnFavorite notCloseDropdawnCard">favorite</mat-icon>
</button>
<div class="footer_products">
<span matTooltip="Views!">
<div class="button_footer_products">
<span class="glyphicon glyphicon-eye-open icon_eye"></span>
<div class="both_write ">
12889
</div>
</div>
</span>
<span matTooltip="Add to your card" class="notCloseDropdawnCard notCloseDropdawnFavorite " (click)="product.product_in_cartList='true'; productsService.add_cart_list( product )">
<div class="button_footer_products">
<span *ngIf="product.product_in_cartList=='false'" class="glyphicon glyphicon-plus icon_eye notCloseDropdawnCard notCloseDropdawnFavorite" ></span>
<span *ngIf="product.product_in_cartList=='true'" class="glyphicon glyphicon-ok icon_eye notCloseDropdawnCard notCloseDropdawnFavorite" ></span>
<div class="both_write ">
Cart
</div>
</div>
</span>
<span matTooltip="See Details!">
<div (click)="set_router({ path:product['company'].company_name+'/'+product.product_title , data:product.product_id , relative:true })" class="button_footer_products" >
<span class=" glyphicon glyphicon-option-horizontal icon_eye"></span>
<div class="both_write ">
More
</div>
</div>
</span>
</div>
<div class="prise_products">
Price:<del>$2500</del> $3500
</div>
<div class="plus_height"></div>
</span>
</div>
In header component I have a input type text as below :
<input type="text" class="kerkim" name="search" [(ngModel)]="typing_search" placeholder="
{{dataservices.language.searchproducts}}">
Debouce effect, e.g. do not run search immediately.
class Coponent {
private _timeoutId: number;
//to be called on search text changed
search(){
clearTimeout(this._timeoutId);
this._timeoutId = setTimeout(() => {
//do search stuff
}, 500) //play with delay
}
}
Cache prev results using search keyword.
When kyeword changes like so ["k","ke","key"] you do not need to refilter whole array.
class Search {
private _keywordChanges:string[] = [];
private _prevFilterResults: any[] = [];
private _allData: any[] = [];
search(keyword:string){
let prevKeyword = this.getPrevKeyword(),
toBeFiltered: any[];
if(keyword.match(keyword)){ //if it was "ke" and now it is "key"
//filter prev results only
toBeFiltered = this._prevFilterResults;
} else {
//filter prev results or even make cache for keyword
toBeFiltered = this._allData;
}
let results = toBeFiltered.filter(() => {});
this._prevFilterResults = results;
}
private getPrevKeyword(){
return this._keywordChanges[this._keywordChanges.length - 1];
}
Use for with break instead of Array.filter(), in some cases it may be helpfull. For example you have sorted array ["a","apple","b","banana"] and keyword "a".
function search(array:any[], keyword:string) {
//so
let results = [];
for(let i = 0; i < array.length; i++){
let item = array[i];
if(item.toString().startsWith(keyword)){
results.push(item);
} else {
break; //as b and banana left
}
}
return results;
}
Take a look at binary search. How to implement binary search in JavaScript
and hash table Hash table runtime complexity (insert, search and delete)
From my issue: every input field is slow due to many data. so i add "changeDetection: ChangeDetectionStrategy.OnPush" at where data reloaded, then everything work normal.
#Component({
selector: 'app-app-item',
templateUrl: './app-item.component.html',
styleUrls: ['./app-item.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
})

AngularJS with WebAPI, WebAPI Get method getting skipped

I have two applications, one is a WebAPI application that just serves data. The other is an AngularJS application that gets that data. I'm having a problem with my WebAPI GET method.
I'm trying to use URL parameters to call the correct GET method. From what I understand is that AngularJS will create the URl query for me, and the WebAPI will look for the correct GET method with the correct parameters.
What I'm having trouble with is that my correct GET method is getting skipped by the WebAPI because it doesn't think that the parameters in the URL query match up to the request. It essentially removes/ignores the parameters in the GET method, but I already have a blank GET method. So I'm getting a Multiple actions were found that match the request: error.
This is how my WebAPI looks. Note that the second GET method needs an SearchParameters object which just has two fields. SearchVariable which is what to search for, and SearchTerm which is what field(column) to search for the variable in.
public class ProductsController : ApiController
{
// GET: api/Products
public IEnumerable<Product> Get()
{
var productRepository = new ProductRepository();
return productRepository.Retrieve();
}
// GET: api/Products
public IEnumerable<Product> Get(SearchParameters search)
{
var productRepository = new ProductRepository();
var products = productRepository.Retrieve();
if(search.SearchField == null)
{
return products;
}
IEnumerable<Product> result = null;
// Search on a certain field for the search variable
switch(search.SearchType)
{
case "Product Code":
result = products.Where(p => p.ProductCode.ToUpper().Contains(search.SearchField.ToUpper()));
break;
case "Product Name":
result = products.Where(p => p.ProductName.ToUpper().Contains(search.SearchField.ToUpper()));
break;
}
return result;
}
}
This is my Controller where I'm calling the WebAPI.
(function () {
"use strict";
var app = angular.module("productManagement")
var ProductListCtrl = function($scope, productResource) {
var vm = this;
vm.searchCriteria = {
searchVariable: null,
searchTerm: "Product Name"
};
productResource.query(function (data) {
vm.products = data;
});
$("#searchBtn").click(function () {
productResource.query({ search: vm.searchCriteria }, function (data)
{
vm.products = data;
});
});
$("#searchCategories li").click(function (e) {
e.preventDefault();
var selText = $(this).text();
vm.searchCriteria.searchTerm = selText;
$scope.$apply();
});
}
app.controller("ProductListCtrl", ["$scope", "productResource", ProductListCtrl]);
}());
I set my searchCriteria object from a search box on the UI. This is how the UI looks like.
<div class="panel panel-primary"
ng-controller="ProductListCtrl as vm">
<div class="panel-heading"
style="font-size:large">
<div class="span5" style="display: inline;">
Product List
</div>
<div class="span2 pull-right" style="display: inline;">
<label id="searchTerm" for="searchItem" style="font-size: large; font-weight: normal;">
{{ vm.searchCriteria.searchTerm }}
</label>
<input id="searchItem" type="search" style="color: #337ab7;" ng-model="vm.searchCriteria.searchVariable"/>
<div class="btn-group btn-group-sm" style="vertical-align: top;">
<button id="searchBtn" type="button" class="btn btn-default">Search</button>
<button id="searchCategoryBtn" class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" role="button" >
<i class="caret"></i>
</button>
<ul id="searchCategories" class="dropdown-menu" role="menu">
<li>Product Code</li>
<li>Product Name</li>
</ul>
</div>
</div>
</div>
<div class="panel-body">
<table class="table">
<thead>
<tr>
<td><b>Product</b></td>
<td><b>Code</b></td>
<td><B>Available</b></td>
<td><B>Price</b></td>
</tr>
</thead>
<tbody>
<tr ng-repeat="product in vm.products">
<td>{{ product.productName}}</td>
<td>{{ product.productCode }}</td>
<td>{{ product.releaseDate | date }}</td>
<td>{{ product.price | currency }}</td>
</tr>
</tbody>
</table>
</div>
Why is it that my WebAPI is skipping over my second GET method when the search parameter is clearly getting put on the URL in my Controller? The problem isn't about defining duplicate methods with the same name but different parameters. It's about the WebAPI skipping over the second GET method because it doesn't recognize the search parameter when looking to see if the search parameter in the URL matches with a search parameter in the methods.
The moment I change public IEnumerable<Product> Get(String search) to public IEnumerable<Product> Get(SearchParameters search), the error appears.

Angular select all visible in list

Say you have the following ng-repeat:
<ul class="list-group">
<ng-user-item
ng-repeat="user in users | filter:search" user="user" ng-if="assigned.indexOf(user.id) < 0"
ng-click="selectFunction(user);"></ng-user-item>
</ul>
Now as you can see this has a filter.
when the filter is not null i have the following button:
<button class="btn btn-default" ng-if="search.division != null" style="margin-bottom: 10px;">Select all</button>
when this button is pressed i want a list of all ng-user-item that is visible.
How can this be done with angular?
I think if you change your ng-repeat to this it should work:
ng-repeat="user in filteredUsers = (users | filter:search)"
You can now use filteredUsers as a normal scope variable.

How can I prevent Angular from re-sorting my list while editing?

I created a little application using Angular to manage Todolists. Each list has a number of todos. Each todo has attributes name, value1 and value2.
Each list should be sorted automatically by Angular, so I used ng-repeat="todo in selectedList.todos | orderBy: todoOrderFilter":
<ul class="list-group">
<li class="list-group-item" ng-repeat="todo in selectedList.todos | orderBy: todoOrderFilter">
<div>
<span>{{todo.name}} (Value1: {{todo.value1}}, Value2 {{todo.value2}})</span>
<button type="button" class="btn btn-warning btn-xs" ng-click="editTodo(todo)"><i class="icon-trash"></i> Edit</button>
<button type="button" class="btn btn-danger btn-xs floatright" ng-click="deleteTodo(todo)"><i class="icon-trash"></i> Delete</button>
</div>
</li>
</ul>
In my controller I defined my order filter like this:
$scope.todoOrderFilter = function (todo) {
return todo.value1 * todo.value2;
};
This works well so far until I tried to make each row editable. To accomplish this, I added an additional <div> with input elements to edit the values inside each <li> and also added ng-hide="todo.editing" and ng-show="todo.editing" to be able to turn on/off edit mode by simply setting todo.editing=true or false;
Full HTML looks like this:
<ul class="list-group">
<li class="list-group-item" ng-repeat="todo in selectedList.todos | orderBy: todoOrderFilter">
<div ng-hide="todo.editing">
<span>{{todo.name}} (Value1: {{todo.value1}}, Value2 {{todo.value2}})</span>
<button type="button" class="btn btn-warning btn-xs" ng-click="editTodo(todo)"><i class="icon-trash"></i> Edit</button>
<button type="button" class="btn btn-danger btn-xs floatright" ng-click="deleteTodo(todo)"><i class="icon-trash"></i> Delete</button>
</div>
<div ng-show="todo.editing">
<input id="todoname" ng-model="todo.name" ng-enter="updateTodo(todo)" type="text" class="form-control marginBottom" placeholder="Todo speichern" aria-describedby="basic-addon2"></input>
Value1: <input ng-model="todo.value1" ng-enter="updateTodo(todo)" type="text" class="form-control marginBottom" placeholder="Value1" aria-describedby="basic-addon2"></input>
Value2: <input ng-model="todo.value2" ng-enter="updateTodo(todo)" type="text" class="form-control marginBottom" placeholder="Value2" aria-describedby="basic-addon2"></input>
<button type="button" class="btn btn-default" ng-click="updateTodo(todo)">Save</button>
<button type="button" class="btn btn-danger" ng-click="cancelUpdateTodo(todo)">Cancel</button>
</div>
</li>
</ul>
Edit button handler:
$scope.editTodo = function(todo) {
todo.editing = true;
};
This kinda works but while I edit input fields for value1 or value2 my sort function is automatically triggered which causes the <li> elements to jump up and down which is really bad.
So what I basically want is that my auto sort filter is disabled while todo.editing=true.
So far I found these similar questions on SO but they weren't really helpful:
Disabling orderBy in AngularJS while editing the list (Don't understand how to apply the answer there to my code)
https://stackoverflow.com/questions/30845516/stop-ng-repeat-auto-sorting-your-objects-while-editing-text-box-and-checkbox-in (No answer)
Question: How can I prevent Angular from resorting the todo list while todo.editing=true?
The solution was to edit a copy of the object instead of directly editing it. Then, replace the original object with the copy when the user finished editing.
I firmly believe in utilising code that has already been written for us by the Angular team, and building upon it. As such, I think this is a perfect scenario for decorating the built-in orderBy filter to accept a fourth argument (ignore).
I haven't tested this myself very thoroughly but it should do the trick;
app.config(function ($provide) {
$provide.decorator('orderByFilter', function ($delegate) {
// Store the last ordered state.
var previousState;
return function (arr, predicate, reverse, ignore) {
// If ignore evaluates to a truthy value, return the previous state.
if (!!ignore) {
return previousState || arr;
}
// Apply the regular orderBy filter.
var order = $delegate.apply(null, arguments);
// Overwrite the previous state with the most recent order state.
previousState = order;
// Return the latest order state.
return order;
}
});
});
Usage:
<div ng-repeat="d in data | orderBy:predicate:reverse:ignore">
<!-- in your case -->
<div ng-repeat="todo in selectedList.todos | orderBy:todoOrderFilter:false:todo.editing>
I hope all of that makes sense (maybe even just works™).

Handle open/collapse events of Accordion in Angular

If I have this code:
<accordion-group heading="{{group.title}}" ng-repeat="group in groups">
{{group.content}}
</accordion-group>
Using AngularJS, angular-ui and Twitter Bootstrap, is it possible to make the accordion call some action when opened? I know I can't simply add ng-click, because that is already used after it's "compiled" to HTML for opening/collapsing of the group.
Accordion groups also allow for an accordion-heading directive instead of providing it as an attribute. You can use that and then wrap your header in another tag with an ng-click.
<accordion-group ng-repeat="group in groups" heading="{{group.title}}" is-open="group.open">
<accordion-heading>
<span ng-click="opened(group, $index)">{{group.content}}</span>
</accordion-heading>
</accordion-group>
Example: http://plnkr.co/edit/B3LC1X?p=preview
Here's a solution based on pkozlowski.opensource solution.
Instead of adding a $watch on each item of the collection, you can use a dynamically defined Property. Here, you can bind the IsOpened property of the group to the is-open attribute.
<accordion-group ng-repeat="group in groups" heading="{{group.title}}" is-open="group.IsOpened">
{{group.content}}
</accordion-group>
So, you can dynamically add the IsOpened property on each item of the collection in the controller :
$scope.groups.forEach(function(item) {
var isOpened = false;
Object.defineProperty(item, "IsOpened", {
get: function() {
return isOpened;
},
set: function(newValue) {
isOpened = newValue;
if (isOpened) {
console.log(item); // do something...
}
}
});
});
Using properties instead of watches is better for performances.
There is the is-open attribute on the accordion-group which points to a bindable expression. You could watch this expression and execute some logic when a given accordion group is open. Using this technique you would change your markup to:
<accordion-group ng-repeat="group in groups" heading="{{group.title}}" is-open="group.open">
{{group.content}}
</accordion-group>
so that you can, in the controller, prepare a desired watch expression:
$scope.$watch('groups[0].open', function(isOpen){
if (isOpen) {
console.log('First group was opened');
}
});
While the above works it might be a bit cumbersome to use in practice so if you feel like this could be improved open an issue in https://github.com/angular-ui/bootstrap
Here's a solution inspired by kjv's answer, which easily tracks which accordion element is open. I found difficult getting ng-click to work on the accordion heading, though surrounding the element in a <span> tag and adding the ng-click to that worked fine.
Another problem I encountered was, although the accordion elements were added to the page programmatically, the content was not. When I tried loading the content using Angular directives(ie. {{path}}) linked to a $scope variable I would be hit with undefined, hence the use of the bellow method which populates the accordion content using the ID div embedded within.
Controller:
//initialise the open state to false
$scope.routeDescriptors[index].openState == false
function opened(index)
{
//we need to track what state the accordion is in
if ($scope.routeDescriptors[index].openState == true){ //close an accordion
$scope.routeDescriptors[index].openState == false
} else { //open an accordion
//if the user clicks on another accordion element
//then the open element will be closed, so this will handle it
if (typeof $scope.previousAccordionIndex !== 'undefined') {
$scope.routeDescriptors[$scope.previousAccordionIndex].openState = false;
}
$scope.previousAccordionIndex = index;
$scope.routeDescriptors[index].openState = true;
}
function populateDiv(id)
{
for (var x = 0; x < $scope.routeDescriptors.length; x++)
{
$("#_x" + x).html($scope.routeDescriptors[x]);
}
}
HTML:
<div ng-hide="hideDescriptions" class="ng-hide" id="accordionrouteinfo" ng-click="populateDiv()">
<accordion>
<accordion-group ng-repeat="path in routeDescriptors track by $index">
<accordion-heading>
<span ng-click="opened($index)">route {{$index}}</span>
</accordion-heading>
<!-- Notice these divs are given an ID which corresponds to it's index-->
<div id="_x{{$index}}"></div>
</accordion-group>
</accordion>
</div>
I used an associative array to create a relationship between the opened state and the model object.
The HTML is:
<div ng-controller="CaseController as controller">
<accordion close-others="controller.model.closeOthers">
<accordion-group ng-repeat="topic in controller.model.topics track by topic.id" is-open="controller.model.opened[topic.id]">
<accordion-heading>
<h4 class="panel-title clearfix" ng-click="controller.expand(topic)">
<span class="pull-left">{{topic.title}}</span>
<span class="pull-right">Updated: {{topic.updatedDate}}</span>
</h4>
</accordion-heading>
<div class="panel-body">
<div class="btn-group margin-top-10">
<button type="button" class="btn btn-default" ng-click="controller.createComment(topic)">Add Comment<i class="fa fa-plus"></i></button>
</div>
<div class="btn-group margin-top-10">
<button type="button" class="btn btn-default" ng-click="controller.editTopic(topic)">Edit Topic<i class="fa fa-pencil-square-o"></i></button>
</div>
<h4>Topic Description</h4>
<p><strong>{{topic.description}}</strong></p>
<ul class="list-group">
<li class="list-group-item" ng-repeat="comment in topic.comments track by comment.id">
<h5>Comment by: {{comment.author}}<span class="pull-right">Updated: <span class="commentDate">{{comment.updatedDate}}</span> | <span class="commentTime">{{comment.updatedTime}}</span></span></h5>
<p>{{comment.comment}}</p>
<div class="btn-group">
<button type="button" class="btn btn-default btn-xs" ng-click="controller.editComment(topic, comment)">Edit <i class="fa fa-pencil-square-o"></i></button>
<button type="button" class="btn btn-default btn-xs" ng-click="controller.deleteComment(comment)">Delete <i class="fa fa-trash-o"></i></button>
</div>
</li>
</ul>
</div>
</accordion-group>
</accordion>
The controller snippet is:
self.model = {
closeOthers : false,
opened : new Array(),
topics : undefined
};
The 'topics' are populated on an AJAX call. Separating the 'opened' state from the model objects that are updated from the server means the state is preserved across refreshes.
I also declare the controller with ng-controller="CaseController as controller"
accordion-controller.js
MyApp.Controllers
.controller('AccordionCtrl', ['$scope', function ($scope) {
$scope.groups = [
{
title: "Dynamic Group Header - 1",
content: "Dynamic Group Body - 1",
open: false
},
{
title: "Dynamic Group Header - 2",
content: "Dynamic Group Body - 2",
open: false
},
{
title: "Dynamic Group Header - 3",
content: "Dynamic Group Body - 3",
open: false
}
];
/**
* Open panel method
* #param idx {Number} - Array index
*/
$scope.openPanel = function (idx) {
if (!$scope.groups[idx].open) {
console.log("Opened group with idx: " + idx);
$scope.groups[idx].open = true;
}
};
/**
* Close panel method
* #param idx {Number} - Array index
*/
$scope.closePanel = function (idx) {
if ($scope.groups[idx].open) {
console.log("Closed group with idx: " + idx);
$scope.groups[idx].open = false;
}
};
}]);
index.html
<div ng-controller="AccordionCtrl">
<accordion>
<accordion-group ng-repeat="group in groups" is-open="group.open">
<button ng-click="closePanel($index)">Close me</button>
{{group.content}}
</accordion-group>
<button ng-click="openPanel(0)">Set 1</button>
<button ng-click="openPanel(1)">Set 2</button>
<button ng-click="openPanel(2)">Set 3</button>
</accordion>
</div>
You can do it w/ an Angular directive:
html
<div uib-accordion-group is-open="property.display_detail" ng-repeat="property in properties">
<div uib-accordion-heading ng-click="property.display_detail = ! property.display_detail">
some heading text
</div>
<!-- here is the accordion body -->
<div ng-init="i=$index"> <!-- I keep track of the index of ng-repeat -->
<!-- and I call a custom directive -->
<mydirective mydirective_model="properties" mydirective_index="{% verbatim ng %}{{ i }}{% endverbatim ng %}">
here is the body
</mydirective>
</div>
</div>
js
app.directive("mydirective", function() {
return {
restrict: "EAC",
link: function(scope, element, attrs) {
/* note that ng converts everything to camelCase */
var model = attrs["mydirectiveModel"];
var index = attrs["mydirectiveIndex"];
var watched_name = model + "[" + index + "].display_detail"
scope.$watch(watched_name, function(is_displayed) {
if (is_displayed) {
alert("you opened something");
}
else {
alert("you closed something");
}
});
}
}
});
There are some idiosyncrasies about my setup there (I use Django, hence the "{% verbatim %}" tags), but the method should work.

Categories

Resources