I found many ways to update parent scope variable from child scope, which is The Dot, but in my case that didn't help.
I have ui-bootstrap accordion which open and collapse according to isOpen variable which I pass it from the backend.
angular.module('plunker', ['ui.bootstrap']);
function AccordionDemoCtrl($scope) {
$scope.currentPage = 1;
$scope.items = [{
label: 'Item 1',
open: true
}, {
label: 'Item 2',
open: true
}, {
label: 'Item 3',
open: true
}];
$scope.opened = false;
}
<accordion id="accordion_main">
<accordion-group ng-repeat="item in items" heading="{{item.label}}" is-open="item.open">
</accordion-group>
</accordion>
<span class="btn btn-default" id="toggle_all" ng-click="m.open=!m.open">Collapse All</span>
I want to make a collapse all button, which is outside of the scope of ng-repeat. If there is a better way to make a collapse all button please advice,
Change your ngClick to loop through your items and set them to false.
<span class="btn btn-default" id="toggle_all" ng-click="collapseAll()">Collapse All</span>
JS:
$scope.collapseAll = function(){
for(var x = 0; x < $scope.items.length; x++){
$scope.items[x].open = false;
}
};
you can use another var in scope like:
$scope.collapse_all = false;
And in page
<accordion id="accordion_main">
<accordion-group ng-repeat="item in items" heading="{{item.label}}" is-open="!collapse_all && item.open">
</accordion-group>
</accordion>
<span class="btn btn-default" id="toggle_all" ng-click="collapse_all=!collapse_all">Collapse All</span>
Related
I would like use isOpen attributes in angualr ui bootstrap accordion directive so it would open the first element of the first ng-repeat in the accordion. I have tried few things with no luck. can anyone advise on this?
//html
<div ng-show="accordionCtrl.isNotString(value);" class="accordionContainer" ng-repeat="(key, value) in accordionCtrl.lessons">
<div class="accordionStepBox">
<h4 class="accordionStepTitle">Step {{$index+1}}: {{value.title}}</h4>
<span>Summary: {{value.summary}}</span>
</div>
<div class="accordionCoursesBox">
<div class="accordionCoursesText">Courses</div>
<!-- accordion for suffles -->
<uib-accordion close-others="accordionCtrl.oneAtATime">
<!-- only give accordion to object vals -->
<div class="accordion" ng-show="accordionCtrl.isNotString(value);" ng-repeat="(key, value) in value">
<!-- <uib-accordion-group heading="{{value.title}}" is-open="accordionCtrl.firstIndex($index)"> -->
<uib-accordion-group heading="{{value.title}}">
<div ng-repeat="(key, value) in value">
<div ng-show="accordionCtrl.isNotString(value);" class="accordionSuffleBox">
{{$index+1}}. {{value.title}}
</div>
</div>
<br/>
<button ui-sref="lesson" class="btn btn-default accordionbutton">Start</button>
</uib-accordion-group>
</div>
</uib-accordion>
</div>
</div>
//controller
angular
.module('neuralquestApp')
.controller('AccordionCtrl', AccordionCtrl);
function AccordionCtrl(FirebaseUrl, $firebaseObject, $firebaseArray) {
var accordionCtrl = this;
var getLessons = getLessons;
accordionCtrl.oneAtATime = true;
accordionCtrl.init = init;
accordionCtrl.init();
accordionCtrl.isNotString = isNotString;
accordionCtrl.firstIndex = firstIndex;
/*======================================
= IMPLEMENTATION =
======================================*/
function init() {
getLessons();
}
function firstIndex(index) {
if(index === 0){
return true;
} else {
return false;
}
}
function getLessons() {
var ref = new Firebase(FirebaseUrl);
accordionCtrl.lessons = $firebaseObject(ref.child('NeuralNetwork'));
}
function isNotString(val) {
// console.log('val', val);
if(typeof val === "string"){
console.log('is string', val);
return false;
} else {
return true;
}
}
}
The is-open attribute is setup for 2 way binding with the controller, so you could do something like so:
<div ng-controller="AccordionDemoCtrl">
<uib-accordion>
<div ng-repeat="group in groups">
<uib-accordion-group heading="{{group.title}}" is-open="openIndex[$index]">
{{group.content}}
</uib-accordion-group>
</div>
</uib-accordion>
</div>
angular.module('ui.bootstrap.demo').controller('AccordionDemoCtrl', function ($scope) {
$scope.openIndex = [true];
$scope.groups = [
{
title: 'Group Header - 1',
content: 'Group Body - 1'
},
{
title: 'Group Header - 2',
content: 'Group Body - 2'
},
{
title: 'Group Header - 3',
content: 'Group Body - 3'
},
{
title: 'Group Header - 4',
content: 'Group Body - 4'
}
];
});
Example plunk. Also, the close-others attribute default value is true, so you don't need to explicitly set that to true.
I have a question about the AngularJS editable framework. When using an editable, two buttons appear: the save button and the cancel button. However, I want a third button (with a minus symbol) to delete the text in the editable. How can I add a third delete button?
Here is a fiddle of one of my previous questions:
Example with only two buttons
{{ user.name || 'empty' }}
Don't think they have support for this right now.
I've done it myself.
var app = angular.module("app", ["xeditable"]);
app.run(function (editableOptions) {
editableOptions.theme = 'bs3';
});
app.controller('Ctrl', function ($scope) {
$scope.user = {
name: 'awesome user'
};
$scope.showClear = function () {
$scope.show = true;
}
$scope.empty = function () {
$scope.user.name = "";
$scope.show = false;
}
});
<h4>Angular-xeditable Text (Bootstrap 3)</h4>
<div ng-app="app" ng-controller="Ctrl">
<span href="#" editable-text="user.name" ng-click="showClear()">{{ user.name || 'empty' }}</span>
<button class="btn btn-default" ng-click="empty()" ng-show="show">
-
</button>
</div>
Hope this will help :).
I am using AngularJS UI Bootstrap's accordion. I am using ng-repeat with filter inside the accordion. Now the problem is that I cannot access the filtered variable outside <accordion>. If I ditch the accordion and use simple HTML it works fine.
<div ng-app="myApp">
<div ng-controller="AccordionCtrl">
<div>outside accordion{{filteredCampaigns}}</div>
<input type="text" ng-model="q" placeholder="filter" />
<accordion class="accordion" close-others="true">
<div>inside accordion{{filteredCampaigns}}</div>
<accordion-group ng-repeat="campaign in filteredCampaigns=(campaigns | filter:q)">
<accordion-heading>
<h3>{{campaign.title}}</h3>
</accordion-heading>
<p>{{campaign.content}}</p>
</accordion-group>
</accordion>
</div>
</div>
<script>
var app = angular.module('myApp', ['ui.bootstrap']);
app.controller('AccordionCtrl', ['$scope',
function ($scope) {
$scope.campaigns = [{
title: "Test1",
content: "file1.html"
}, {
title: "Test2",
content: "file2.html"
}, {
title: "Test3",
content: "file3.html"
}];
}]);
</script>
I have also created the fiddle here
This is becuase the sub-directives comes with angular-ui have child scopes that does not reflect this new filteredCampaigns variable to the controller's scope.
1st option - Hack it,
1) define an object to contain the filteredCampaigns dynamic variable
$scope.context = {};
2) change your accordion-group to:
<accordion-group ng-repeat="campaign in context.filteredCampaigns=(campaigns | filter:q)">
http://jsfiddle.net/Lryuvm9m/1/
<script>
var app = angular.module('myApp', ['ui.bootstrap']);
app.controller('AccordionCtrl', ['$scope',
function ($scope) {
$scope.context = {};
$scope.campaigns = [{
title: "Test1",
content: "file1.html"
}, {
title: "Test2",
content: "file2.html"
}, {
title: "Test3",
content: "file3.html"
}];
}]);
</script>
<div ng-app="myApp">
<div ng-controller="AccordionCtrl">
<div>outside accordion {{context.filteredCampaigns}} </div>
<input type="text" ng-model="q" placeholder="filter" />
<accordion class="accordion" close-others="true">
<div>inside accordion{{context.filteredCampaigns}}</div>
<accordion-group ng-repeat="campaign in context.filteredCampaigns=(campaigns | filter:q)">
<accordion-heading>
<h3>{{campaign.title}}</h3>
</accordion-heading>
<p>{{campaign.content}}</p>
</accordion-group>
</accordion>
</div>
</div>
2nd option - use Controller as,
i would recommend using Controller as since it will give you more controll of what's defined on the controller's scope and what's not
http://jsfiddle.net/gntt6h5b/
<script>
var app = angular.module('myApp', ['ui.bootstrap']);
app.controller('AccordionCtrl', ['$scope',
function () {
var vm = this;
vm.campaigns = [{
title: "Test1",
content: "file1.html"
}, {
title: "Test2",
content: "file2.html"
}, {
title: "Test3",
content: "file3.html"
}];
}]);
</script>
<div ng-app="myApp">
<div ng-controller="AccordionCtrl as vm">
<div>outside accordion {{vm.filteredCampaigns}} </div>
<input type="text" ng-model="q" placeholder="filter" />
<accordion class="accordion" close-others="true">
<div>inside accordion{{vm.filteredCampaigns}}</div>
<accordion-group ng-repeat="campaign in vm.filteredCampaigns=(vm.campaigns | filter:q)">
<accordion-heading>
<h3>{{campaign.title}}</h3>
</accordion-heading>
<p>{{campaign.content}}</p>
</accordion-group>
</accordion>
</div>
</div>
<accordion> creates an isolated scope and the filteredCampaigns are only available in that scope, not in the parent scope. To add it to the parent scope, you use $parent:
ng-repeat="campaign in $parent.filteredCampaigns=(campaigns | filter:q)">
Another solution would be creating an creating an object an object in the parent scope with a parameter for the filteredCampaings:
// AccordionCtrl
$scope.filteredValues = {
filteredCampaings: []
};
ng-repeat="campaign in filteredValues.filteredCampaigns=(campaigns | filter:q)"
Since you're using it outside of the ng-repeat, you can't see that variable as ng-repeat creates its own scope.
You can use the controller and use the filter manually to retrieve the filtered objects:
$scope.getFilteredCampaings = function () {
$scope.filteredCampaigns = $filter('filter')($scope.campaigns, $scope.q);
return $scope.filteredCampaigns;
}
Fiddle
I am trying to get the accordions to toggle correctly through the directive ng-click. If I have Item one open how do I get it to expand all the accordions? Item two and Item three will continue to expand and collapse but Item one stays stagnant.
Plunker
alternately you can adjust your buttons so that they just loop through the children.
html:
<div ng-controller="AccordionDemo">
<div >
<div class="stuff_in_the_middle" >
<div ng-repeat="m in results" ng-click="m.open = !m.open" style="margin-bottom:20px">
<div heading="{{m.label}}" is-open="m.open" style="background-color:#d2d2d2; cursor:pointer" >
{{m.label}}
</div>
<div ng-show="m.open" style="padding:10px">
contents
</div>
</div>
<span class="btn btn-default" ng-click="toggle(false)">Collapse All</span>
<span class="btn btn-default" ng-click="toggle(true)">Expand All</span>
</div>
<hr />
</div>
</div>
JS:
var module = angular.module('plunker', []);
module.controller('AccordionDemo', ['$scope',
function ($scope) {
$scope.results = [
{label: 'Item 1', open: false},
{label: 'Item 2', open: false},
{label: 'Item 3', open: false}
];
$scope.toggle = function(state) {
$scope.results.forEach(function(e) {
e.open = state;
});
}
}
]);
see it working here: http://plnkr.co/edit/T6iv7mSoat9SQBwSIFJP
You have a problem with scopes. Simple rule is to never set variable value from ng-click or similar directives if you gonna use this variable outside - in parent.
It is caused by ng-repeat, which creates scope and if you will try to define new variable within it(you are doing it, because you have used name plunker, instead of opened), it will be defined only to current item in repeat.
You can use setter function to set it to right scope. So, here we go: http://plnkr.co/edit/h3MtKywiOaIQhpnAzWLT?p=preview
I would like to create a collpase/expand all button for a set of accordions. I am using angular.js v1.2.6 and angular-bootstrap-ui 0.9.0. I haven't been able to find an example of how to do a collpase/expand all. I'm an angular novice and any advice or suggestions is appreciated.
ALSO, I should add that the solution in this post doesn't work in the newer version of angular.js (v1.0.8 vs v1.2.6) that I am using (which is a latest version at the time of this posting). In the newer version it throws an error about trying to create two isolated scopes.
Markup:
<div ng-controller="myCtlr">
<accordion close-others="oneAtATime">
<button class="btn" ng-click="section.isCollapsed = !section.isCollapsed">Toggle Sections</button>
<accordion-group ng-repeat="section in sections" is-open="section.isOpen">
<accordion-heading>
<div class="accordion-heading-content" ng:class="{collapsed: section.isOpen}">
{{section.name}}
</div>
</accordion-heading>
Section content
</accordion-group>
</accordion>
</div>
JS:
var theapp = angular.module('myApp', [
'ui.bootstrap'
])
function myCtlr ($scope) {
$scope.isCollapsed = false;
$scope.sections = [
{'id': '353','otherid': '7','name': 'One','Sequence': '1'},
{'id': '354','otherid': '8','name': 'Two','Sequence': '1'},
{'id': '355','otherid': '9','name': 'Three','Sequence': '1'}
];
}
I've taken Blowsie's plunkr comment and built my own answer that solves the use case that I'm reading into here.
I've moved the close-others attribute to the accordion element where the docs say it goes. I've also added extra buttons to openAll, closeAll or toggleAll.
If you click the header for a single item, only that item opens. If you click a 'All' button, all are affected.
Thanks to Blowsie for the inspiration. I think using the item.open here was the ticket. After that, it's just a matter of affecting all item.open values.
http://plnkr.co/edit/WUKEfcBrSf3XrIQAik67?p=preview
HTML
<div ng-controller="AccordionDemoCtrl">
<accordion close-others="false">
<accordion-group ng-repeat="item in items" is-open="item.open" heading="{{item.name}}">
{{item.open}}
<p>The body of the accordion group grows to fit the contents</p>
</accordion-group>
</accordion>
<button ng-click="openItem(0)">Open 1</button>
<button ng-click="openItem(1)">Open 2</button>
<button ng-click="openItem(2)">Open 3</button>
<button ng-click="openAllItems()">Open All</button>
<button ng-click="closeAllItems()">Close All</button>
<button ng-click="toggleAllItems()">Toggle All</button>
<br/>
{{items |json}}
</div>
JS
angular.module('plunker', ['ui.bootstrap']);
function AccordionDemoCtrl($scope) {
$scope.oneAtATime = true;
$scope.items = [
{name: 'Item 1', open: false},
{name: 'Item 2', open: false},
{name: 'Item 3', open: false}
];
$scope.addItem = function() {
var newItemNo = $scope.items.length + 1;
$scope.items.push('Item ' + newItemNo);
};
$scope.openItem = function(idx) {
console.log(idx);
$scope.items[idx].open = true;
};
$scope.openAllItems = function(){
$scope.items.map(function(item){
item.open = true;
});
};
$scope.closeAllItems = function(){
$scope.items.map(function(item){
item.open = false;
});
}
$scope.toggleAllItems = function(){
$scope.items.map(function(item){
item.open = !item.open;
});
}
}
Here is a method using the is-open attribute.
http://plnkr.co/edit/MliW41xGJAF0Mnw4Rgu7?p=preview