AngularJS Controller Not Showing/Hiding Correctly - javascript

I am trying to get content to disappear on button click and then show a new set of content on that button click. I cannot quite get this to work. I commented what each section is doing. The first section doesn't disappear on button click. The second section works as expected and does disappear on button click and the third section doesn't show up on button click. Helps is greatly appreciated and I look forward to learning from this!
I thought by adding a controller it would all function together.
HTML
<!-- THIS DOESN'T DISAPPEAR ON BUTTON CLICK -->
<div ng-controller="EventCtrl" ng-hide="eventComplete">
<h2>Example that doesn't disappear on button click</h2>
</div>
<!-- THIS WILL DISAPPEAR ON BUTTON CLICK -->
<div ng-controller="EventCtrl" ng-hide="eventComplete">
<div>
<h2>Example</h2>
<md-button ng-click="eventFinish();">Finish</md-button>
</div>
<!-- THIS DOESN'T SHOW ON BUTTON CLICK -->
<div ng-controller="EventCtrl" ng-show="eventComplete">
<h2>Complete!</h2>
</div>
</div>
ANGULAR
.controller('EventCtrl', function($rootScope,$state,$scope,$timeout){
var self = this;
$scope.eventComplete = false;
$scope.eventFinish=function(){
console.log('eventFinish'); //This logs
$scope.eventComplete = true;
};
})

You wrapped the div you want to hide around the div you want to show. The following html should solve the issue:
<div ng-controller="EventCtrl">
<div ng-hide="eventComplete">
<h2>Example that doesn't disappear on button click</h2>
</div>
<div ng-hide="eventComplete">
<div>
<h2>Example</h2>
<md-button ng-click="eventFinish();">Finish</md-button>
</div>
</div>
<div ng-show="eventComplete">
<h2>Complete!</h2>
</div>
</div>
EDIT: Found an issue in controller as well. You're missing the closing } for eventFinish :
.controller('EventCtrl', function($rootScope,$state,$scope,$timeout){
var self = this;
$scope.eventComplete = false;
$scope.eventFinish = function() {
console.log('eventFinish');
$scope.eventComplete = true;
};
})

Try to avoid placing same controller inside each other. That will only lead to problems. Instead use Components.
But if you insist on using controllers you could solve it this way. (Code not tested)
HTML
<div ng-controller="EventCtrl">
<div ng-if="showExample(1)">
<h2>Example 1</h2>
<md-button ng-click="onClickExample(2);">Finish</md-button>
</div>
<div ng-if="showExample(2)">>
<h2>Example 2</h2>
<md-button ng-click="onClickExample(1);">Finish</md-button>
</div>
</div>
JS
.controller('EventCtrl', function($rootScope,$state,$scope,$timeout){
$scope.currentExample=1;
$scope.showExample = function(id){
return $scope.currentExample === id;
}
$scope.onClickExample = function(id){
$scope.currentExample = id;
}
});

Related

$anchorScroll after div is created

I've got a problem using $anchorScroll. I've got a div with an ng-ig condition that isn't visible at the beginning. I need use the $anchorScroll function to go in that div when a button is clicked. This button also make the div visible. Right now doesn't work because i think it fires the $anchorScroll before the div is created. This is the code:
<body ng-app="testApp" data-ng-controller="searchController as searchCtrl" >
<div id="scrollArea">
<form>
//HTML input elements
<div class="buttons">
<md-button class="md-primary md-raised" ng-click="searchCtrl.gotoTable('resultTable')">Start</md-button>
{{searchCtrl.test}}
</div>
</form>
</div>
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
<br/><br/><br/><br/><br/><br/><br/><br/><br/>
<br/><br/><br/><br/><br/><br/><br/><br/>
<br/><br/><br/><br/><br/><br/><br/><br/><br/>
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
<!-- Smart Table for displaying result -->
<div id="resultTable" class="rtable">
<div ng-if="searchCtrl.test == true" >
//table content
</div>
</div>
</body>
and the angular part
var app = angular.module('testApp', []);
app.controller("searchController", function($scope, $location, $anchorScroll, $timeout){
var self = this;
self.test = false;
self.gotoTable = function(resultTable)
{
self.test = true;
self.anchor(resultTable);
};
self.anchor = function(resultTable) {
$location.hash('resultTable');
$anchorScroll();
} ;
});
Plunker: http://plnkr.co/edit/HrdN2ZACDui61l1kPHEj?p=preview
Thanks
After playing around with your plunker, I've found that updating angularJs for the latest version fixed the problem.
I don't know if you can update your angular version?
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>

Retain scroll position when a button is toggled

I am working on an angularJS application which has a page where I display around 30 items using ng-repeat. In front of each item, there is a toggle button (enabled/disabled). With the current code that I have, I can toggle these items. But the problem is if I scroll down and toggle lets say item 25, then automatically it scrolls to the top of the page. If I now scroll down, I can see that the toggle actually took place.
So the requirement now is to make sure that the scroll position is retained after the toggle button is clicked.
Please see below the code that I have.
HTML
<div id="eventTypes" class="panel-body">
<div ng-if="!spinner" ng-repeat="item in items" class="panel-body">
<div class="row">
<div class="col-md-9">{{item.itemName)}}</div>
<div class="col-md-3">
<input id="toggleEnabled"
type="button"
ng-class="{'btn-primary': item.enabled}"
value="{{item.enabled ? 'enabled' : 'disabled'}}"
ng-click="toggleEnabled(item)">
</div>
</div>
</div>
<div ng-if="spinner" class="spinner">
<div class="spinner-container container1">
<div class="circle1"></div>
<div class="circle2"></div>
<div class="circle3"></div>
<div class="circle4"></div>
</div>
<div class="spinner-container container2">
<div class="circle1"></div>
<div class="circle2"></div>
<div class="circle3"></div>
<div class="circle4"></div>
</div>
<div class="spinner-container container3">
<div class="circle1"></div>
<div class="circle2"></div>
<div class="circle3"></div>
<div class="circle4"></div>
</div>
</div>
</div>
JS
(function () {
'use strict';
angular.module('myApp').controller('itemsController', function ($scope, itemsService) {
var serviceError = function (errorMsg) {
console.log(errorMsg);
$scope.turnOffSpinner();
};
$scope.items = [];
$scope.item = {};
$scope.spinner = true;
$scope.toggleEnabled = function (item) {
$scope.turnOnSpinner();
itemsService.toggleEnabled(item)
.then(function () {
$scope.loaditems();
});
};
$scope.loaditems = function () {
itemsService.getitems().then(function (response) {
$scope.items = response.data;
}, serviceError);
$scope.turnOffSpinner();
};
$scope.turnOnSpinner = function () {
$scope.spinner = true;
};
$scope.turnOffSpinner = function () {
$scope.spinner = false;
};
$scope.loaditems();
});
}());
How this works right now is, once I click the toggle button, a spinner is enabled. Meanwhile the controller will call the itemService.toggleEnabled() method which does an ajax call to the server to just change the status of the item(enabled to disabled or vice-versa) in the backend. On successful change of the status and when the ajax call returns, the $scope.loadItems() method is called in the controller. This method will then do another ajax call to fetch the items (now with the updated status of the item that was toggled). The spinner is disabled and the data is then displayed on the UI.
When all of this is done, the page is scrolled to the top. This is annoying when I want to toggle an item which is way down in the list.
I want the page to be present at the same position when I clicked the toggle button of the corresponding item and not scrolling up to the top.
I am new to AngularJS and any help in this regard would be really helpful.
It looks like your spinner scheme is what's causing you problems:
...
<div ng-if="!spinner" ng-repeat="item in items" class="panel-body">
...
<div ng-if="spinner" class="spinner">
...
Whenever you click your button, you are removing every single element in your ng-repeat from the DOM when you $scope.turnOnSpinner(). That's why it appears to jump to the top. It's not really jumping, there just aren't enough DOM elements to fill up the page, making the page so short that the scrollbar disappears (even if it's only for a second). Then when the spinner is done, your ng-repeat fills up the page with DOM elements again, resulting in your scroll position being lost.
So basically what you are trying to fix is a symptom of a less than ideal loading spinner implementation.
ng-if is a "brutal" way of hiding things in Angular. It's mostly meant to hide things for a longer period of time than "softer" directives like ng-show/ng-hide. One solution to your problem is to use ng-disabled on each one of your buttons to prevent the user from interacting with it while the spinner is active, rather than doing a hard removal of each element:
Before:
<div ng-if="!spinner" ng-repeat="item in items" class="panel-body">
<div class="row">
<div class="col-md-9">{{item.itemName)}}</div>
<div class="col-md-3">
<input id="toggleEnabled"
type="button"
ng-class="{'btn-primary': item.enabled}"
value="{{item.enabled ? 'enabled' : 'disabled'}}"
ng-click="toggleEnabled(item)">
</div>
</div>
</div>
After:
<div ng-repeat="item in items" class="panel-body">
<div class="row">
<div class="col-md-9">{{item.itemName)}}</div>
<div class="col-md-3">
<input id="toggleEnabled"
ng-disabled="spinner"
type="button"
ng-class="{'btn-primary': item.enabled}"
value="{{item.enabled ? 'enabled' : 'disabled'}}"
ng-click="toggleEnabled(item)">
</div>
</div>
</div>
Another solution, which I really like and use myself is this Angular module: https://github.com/darthwade/angular-loading
You can attach it to any element in the page and it will put a loading spinner over it and prevent you from interacting with it until your ajax or whatever is done.
If you don't like either of those, try putting your ng-repeat into a container that you can use to prevent interaction with your elements when the spinner is up:
<div class="container" ng-class="{'you-cant-touch-this': spinner}">
<div ng-repeat="item in items" class="panel-body">
<div class="row">
<div class="col-md-9">{{item.itemName)}}</div>
<div class="col-md-3">
<input id="toggleEnabled"
type="button"
ng-class="{'btn-primary': item.enabled}"
value="{{item.enabled ? 'enabled' : 'disabled'}}"
ng-click="toggleEnabled(item)">
</div>
</div>
</div>
</div>
Now you can style it in some way to prevent interaction without having to remove all those items from the DOM:
.you-cant-touch-this {
pointer-events: none;
}

Navigating through accordion menu items in angularjs

Could someone help me to implement prev/next buttons for navigating through accordion menu items? The accordion itself works ok when accordion header is clicked but I should also implement a prev/next button in addition to this basic functionality. I would basically just like to get the current active item and then go to next/previous item with the next/prev buttons. How should this be implemented in practice? I tried this so that I'm evaluating the isVisible property and check which box is active and then go to adjacent box but I couldn't get that working and the solution itself doesn't seem very clean. Should I have some kind of indexing for the items and build an array to loop the items in it? I have following fiddle:https://jsfiddle.net/cm70947/zq59tw7a/
HTML:
<body ng-app="myApp" ng-controller="myAppController">
<div class="Button" ng-click="animateBox('first box')">button</div>
<div class="Animated_Box" id="first" ng-show="isVisible == 'first box'">Animated Box 1</div>
<div class="Button" ng-click="animateBox('second box')">button</div>
<div class="Animated_Box" id="second" ng-show="isVisible =='second box'">Animated Box 2</div>
<div class="Button" ng-click="animateBox('third box')">button</div>
<div class="Animated_Box" id="third" ng-show="isVisible =='third box'">Animated Box3</div>
<div id="navigation">
<div class="naviButton" id="goUp" ng-click="prevItem()">PREV</div>
<div class="naviButton" id="goDown" ng-click="nextItem()">NEXT</div>
</div>
</body>
JS:
myApp = angular.module('myApp', []);
myApp.controller('myAppController', function($scope) {
$scope.isVisible = 'first box';
$scope.animateBox = function (box) {
if(box == $scope.isVisible){
$scope.isVisible = null;
}else{
$scope.isVisible = box;
}
}
$scope.prevItem = function () {
//?
};
});

How to solve opening & closing div bootstrap issue?

I'm rendering with handlebars a list of items and I'm using bootstrap to create a toggle div for every item. Handlebars works fine and also the toggle but not the closing-div function of bootstrap.
(I've already try to use bootstrap accordion but it was not working, so I would like solve the problem using jQuery)
Every time I open a new div it should close the other one (if there is one already open); This is not working, I can open more than one panel for time.
<section id="list-wrap">
<script id="list-items" type="text/x-handlebars-template">
{{#each cards}}
<div class="panel">
<div class="panel-heading grad">
<h4>
<span class="title-style">{{name}}</span>
<a data-toggle="collapse" href="#{{this.code}}">
<i class="chevron_toggleable indicator glyphicon glyphicon-chevron-right pull-left"></i>
</a>
<p class="apr title-style"> {{apr}} % APR </p>
</h4>
</div>
<div id="{{this.code}}" class="changeClass panel-collapse collapse">
<div class="panel-body">
<div>
<div class="img-div">
<img src="assets/{{code}}.png">
</div>
<div class="info-div"><p class="info-paragraf">{{information}}</p>
</div>
<div class="cashback-div">
<p class="cashback-paragraf-1">Cashback</p>
<p class="cashback-paragraf-2">{{cashback}}</p>
</div>
</div>
</div>
</div>
</div>
{{/each}}
</script>
</section>
this was working until I added a express server:
$('i').click(function () {
$('.changeClass').removeClass('in');
});
Try using jQuery UIs accordion as an option.
You just have to put the items in an accordion tag, wrap each item in a div as a child of the accordion div and then call the accordion method on the accordion div.
http://jqueryui.com/accordion/
I would use a jquery accordion function like this one. Fiddle
$('#accordion').accordion({
collapsible:true,
beforeActivate: function(event, ui) {
// The accordion believes a panel is being opened
if (ui.newHeader[0]) {
var currHeader = ui.newHeader;
var currContent = currHeader.next('.ui-accordion-content');
// The accordion believes a panel is being closed
} else {
var currHeader = ui.oldHeader;
var currContent = currHeader.next('.ui-accordion-content');
}
// Since we've changed the default behavior, this detects the actual status
var isPanelSelected = currHeader.attr('aria-selected') == 'true';
// Toggle the panel's header
currHeader.toggleClass('ui-corner-all',isPanelSelected).toggleClass('accordion-header-active ui-state-active ui-corner-top',!isPanelSelected).attr('aria-selected',((!isPanelSelected).toString()));
// Toggle the panel's icon
currHeader.children('.ui-icon').toggleClass('ui-icon-triangle-1-e',isPanelSelected).toggleClass('ui-icon-triangle-1-s',!isPanelSelected);
// Toggle the panel's content
currContent.toggleClass('accordion-content-active',!isPanelSelected)
if (isPanelSelected) { currContent.slideUp(); } else { currContent.slideDown(); }
return false; // Cancels the default action
}
});

Changing content in a div when a link is clicked

I am trying to change a div content box when a link above that box is clicked. I added a click function to the individual buttons then the corresponding dive containing the content for that particular box should be displayed.
When I apply the jQuery to the page, the content does not change according to the link that is clicked and the page displays awkwardly.
This is the jsfiddle I'm working with.
The webpage I'm working with is here.
Place where am i making the mistake.
The jQuery I'm working with so far looks like this
$(document).ready(function() {
$('.question1').click(function(){
$('.new_member_box_display').load('#answer1');
})
$('.question2').click(function(){
$('.new_member_box_display').load('#answer2');
})
$('.question3').click(function(){
$('.new_member_box_display').load('#answer3');
})
$('.question4').click(function(){
$('.new_member_box_display').load('#answer4');
})
});//end of ready
The HTML I'm working with looks like this :
<div class="new_member_box">
<h4>Vision</h4>
</div>
<div class="new_member_box">
<h4>Church History</h4>
</div>
<div class="new_member_box">
<h4>Becoming a Member</h4>
</div>
<div class="new_member_box">
<h4>Pastor-in-Training</h4>
</div>
</div>
<div class="clear" class="question"></div>
<div id="answer1">
1
</div>
<div id="answer2">
2
</div>
<div id="answer3">
3
</div>
<div id="answer4">
4
</div>
<div class="new_member_box_display" id="question">
Text will appear here when one of the tabs above is clicked
</div>
load() loads an external file with ajax, not elements on your page. You're probably just trying to hide and show elements :
$('[class^="question"]').on('click', function(e){
e.preventDefault();
var numb = this.className.replace('question', '');
$('[id^="answer"]').hide();
$('#answer' + numb).show();
});
FIDDLE
Here's a fiddle that does what I think you're looking to do. There are cleaner ways to do it, but this should work.
http://jsfiddle.net/PZnSb/6/
Basically you want to set the inner html of one element to the inner html of another, like this:
$('.question1').click(function(){
$('.new_member_box_display').html($('#answer1').html());
})
instead of this:
$('.question1').click(function(){
$('.new_member_box_display').load('#answer1');
})
It seems you just want the text to change when you click, try this.
$(document).ready(function() {
$('.question1').click(function(){
$('.new_member_box_display').text($('.answer1 ').text());
});
$('.question2').click(function(){
$('.new_member_box_display').text($('.answer2').text());
});
$('.question3').click(function(){
$('.new_member_box_display').text($('.answer3').text());
});
$('.question4').click(function(){
$('.new_member_box_display').text($('.answer4').text());
});
});
http://jsfiddle.net/cornelas/PZnSb/7/embedded/result/

Categories