Angular JS: binding in ng-show not working - javascript

I have a directive and a controller:
app.directive('responseBox', function(){
return {
restrict: 'E',
transclude: true,
templateUrl: 'responseBox.html',
link: function(scope, element, attrs) {
element.bind("click", function () {
scope.toggle();
})
}
}});
and a controller:
app.controller('responseBoxCtrl', function($scope) {
$scope.opened = false;
$scope.toggle = function() {
$scope.opened = !$scope.opened;
console.log($scope.opened);
}});
responseBox.html:
<div class="promptBlockResponse" ng-transclude>
<div class="btn-toolbar" style="text-align: right;">
<div class="btn-group" ng-show="opened">
<a class="btn btn-link" href="#"><i class="icon-pencil icon-white"></i></a>
<a class="btn btn-link" href="#"><i class="icon-remove icon-white"></i></a>
</div>
</div>
And in the main html file:
<response_box ng-controller="responseBoxCtrl"></response_box>
I want the btn-group to show when the opened variable is true. When I click the responseBox I can see the variable toggling, but the btn-group does not show/hide. What am I missing?

So repeating what Josh and I said in the comments above, the click handler runs "outside" of Angular, so you need to call scope.$apply() to cause Angular to run a digest cycle to notice the change that was made to scope (and then it will update your view):
$scope.toggle = function() {
$scope.opened = !$scope.opened;
console.log($scope.opened);
$scope.$apply();
}});
The link function can be eliminated by using ng-click in the template:
<div class="promptBlockResponse" ng-transclude ng-click="toggle()">

With Angular 1.3 and 1.2 the following snippet from an HTML template for a custom element directive:
<div ng-click="toggle($event)"></div>
<div ng-show="data.isOpen"></div>
And a snippet from the controller for that custom directive:
$scope.toggle = function ($event, destinationState) {
....
data.isOpen = true; //this is in scope and a digest cycle is already running
//calling $scope.$apply will cause an error
demonstrates an in scope scenario where you do need to use $apply.
I came across this SO question because I was using double brackets in my
<div ng-show="{{data.isOpen}}">
Changing to
<div ng-show="data.isOpen"></div>
got my binding working when I thought at first I had a scope issue.
So in angular 1.2 and 1.3 ng-click is not "outside" of Angular, at least using the signature I used for my toggle function and is explained here:
$apply already in progress error
I discovered my double bracket ng-show issue that I initially thought was a scope issue thanks to this SO:
why doesn't ng-show remove class ng-hide

Related

uibCollapse nested in an directive element won't work,

When I click my-dir, collapsed elements won't expand.
I have set size in css for .btn class to ensure actually click event.
// index.html
<body ng-controller="ctrl">
<p>{{hello}}</p>
<div class="btn" my-dir></div>
</body>
// app.js
var app = angular.module('app', ['ui.bootstrap']);
app.controller('ctrl', function($scope){
$scope.hello = 'hello';
});
app.directive('myDir', function() {
return {
restrict: 'A',
replace: false,
templateUrl: './tmpl.html',
link: function (scope, element, attrs) {
scope.isCollapsed = true;
element.bind('click', function (e) {
scope.isCollapsed = !scope.isCollapsed;
})
}
};
});
// tmpl.html
<div class="dir-box">
<div uib-collapse="isCollapsed">
<p>Hello World!</p>
<p>Nice to meet You!</p>
<p>: )</p>
</div>
</div>
Why uibCollapse does not work in this situation ?
Any ideas ?
plunker here
There are 2 things that you need to fix:
1.Change click binding to ng-click. By default angular does not run its $digest cycle on jquery events. You can fix that by adding $scope.$apply() or $timeout in your code, but I recommend using ng-click as a more proper way.
<div class="btn" my-dir ng-click="isCollapsed = !isCollapsed"></div>
By your original code I should add it inside your directive template, but your dir-box position does not overlap with the blue area...
2.Use overflow:hidden on uib-collapse element.
<div uib-collapse="isCollapsed" style="overflow: hidden;">
The contents were still showing when the parent height is 0
plunker

how to assign array element value to ng-click in angular js

In my program I have an Array which consists of header name and function name.
I am using ng-repeat in a div which consists of a span tag. I want to add different functionality for each iterated span so I stored function name in array.
my html code is:
<div ng-repeat="header in header" ng-init="head=header">
<h4 class="headers">{{ header.name}}</h4>
<div class="arrow-up" ng-show={{ header.arrowup}} ng-click={{header.close}}> </div>
</div>
my angular code is:
$scope.header=[{"name":"Subsection Header #1","arrowup":"arrowup","close":"close()"}];
$scope.close = function() {
console.log(hello);
};
I want to assign close() to the ng-click and arrowup to ng-show. How can I assign them to ng-click and ng-show
change:
<div class="arrow-up" ng-show={{ header.arrowup}} ng-click={{header.close}}>
To:
<div class="arrow-up" ng-show="header.arrowup" ng-click="this[header.close]()">
<button>
CLOSE ME
</button>
</div>
DEMO= http://jsfiddle.net/Lvc0u55v/1788/
ng-click is the problem as i see,
<div class="arrow-up" ng-show="header.arrowup" ng-click="header.close"></div>
we need not give {{}} to ng-click and ng-show.
hope it helps.
First you need to pass a real functions to array:
function arrowUp(){
// ng-show needs to receive true or false
return true;
}
function close(){
// do something here
}
$scope.headers=[{"name":"Subsection Header #1","arrowup":arrowUp,"close":close}];
The bind them in a view:
<div ng-repeat="header in headers">
<h4 class="header">{{ header.name}}</h4>
<div class="arrow-up" ng-show="header.arrowup()" ng-click="header.close()"> </div>
</div>
You need to use the $eval method, you can read more about it here
$scope.$eval Executes the expression on the current scope and returns the result. Any exceptions in the expression are propagated (uncaught). This is useful when evaluating Angular expressions
HTML:
<div ng-controller="TestController">
<div ng-repeat="header in headers">
<h4>{{ header.name}}</h4>
<button type="button" ng-click="onExecuteFunctionFromString(header.close)">Click Here</button>
</div>
</div>
JS:
var myApp = angular.module('myApp', []);
myApp.controller('TestController', ['$scope', function($scope) {
$scope.headers = [{
"name": "Subsection Header #1",
"arrowup": "arrowup",
"close": "close()"
}];
$scope.close = function() {
console.log('hello');
};
$scope.onExecuteFunctionFromString = function(stringFunction) {
$scope.$eval(stringFunction)
};
}]);
Please see working example here

ng-click does not work on my directive of a slider

I use the structure provided by the yeoman with angular-generator.
The ng-click does not work in my directive, of a slider show, when I put the html directly in main.html (It only works when I put in the directive an templateurl, linked to the main.html , but this causes delay to load).
Html, that is inserted directly into main.html
<div images="images" class="slider" id="mauseOnOut">
<div class="slide" ng-repeat="image in images" ng-show="image.visible">
<a ng-href="{{image.url}}"><img ng-src="{{image.src}}" width="444" height="250"/>
<p class="texto">{{image.texto}}</p>
</a>
</div>
<ul class="minimagem" ng-show="images.length">
<li ng-repeat="image in images"><a ng-click="returner($index)"><img ng-src="{{image.src}}" width="70" height="56"/></a></li>
</ul>
<div class="arrows">
<img src="http://s5.postimg.org/qkfwdwi7n/right_arrow.png"/>
</div>
</div>
Main part of the directive (in jsFiddle have it complete)
myApp.directive('images', function ($timeout) {
return {
restrict: 'AE',
scope:{
images: '='
},
link: function (scope) {
scope.currentIndex=0;
scope.returner = function(index){
scope.currentIndex = index;
};
scope.next=function(){
scope.currentIndex<scope.images.length-1?scope.currentIndex++:scope.currentIndex=0;
};
scope.prev=function(){
scope.currentIndex>0?scope.currentIndex--:scope.currentIndex=scope.images.length-1;
};
scope.$watch('currentIndex',function(){
scope.images. forEach(function(image){
image.visible=false;
});
scope.images[scope.currentIndex].visible=true;
});
},
};
});
Put an example in jsFiddle ; when use angular 1.1, on jsFiddle, operate normally, with 1.2 or higher does not work. In my application I use the angular 1.3.10 .
How could make it work in my application? It could be to ' compile ' or in some other way , the important thing is the click staying active in the image thumbnails and arrows .
Edited: I came back with the best known directive , best to understand.

How to create a dialog in AngularJS, but load only the dialog content from the server

I know it might be a simple question, but I'm frustrated here, and I can't make it work. I'm new to AngularJS, and I'm trying to implement a modal dialog (or find one) with these conditions:
Dialog content might come from anywhere—a string template, a script template, or a template from a URL
Dialog title and actions will come from the caller, not the callee. In other words, the parent scope decides the title and which action buttons should exist in the modal dialog (many dialogs I found encapsulate the title and action buttons in the template itself, for example this one)
Content of the template should be totally independent from parent scope (caller). In fact, it might not even be written in AngularJS. It might use jQuery.
In case the loaded template is in AngularJS, it should encapsulate its controller. For example, ng-include doesn't like <script> tags.
There is a workaround for it (here, here and here) but the idea of decorating a script tag with text/javascript-lazy is very smelly and dirty, let alone that I want the content HTML to be self-contained and executable in case it's not loaded as the content of an AngularJS modal dialog.
Communication between the parent scope and the content should be done via a common contract (JavaScript events come to my mind)
I've tried ngDialog, but the problem is that the container should pass the controller to the loaded template. That's not what I want.
In Bootstrap dialog also it seems that you have to pass the controller from the parent scope to the dialog content. This breaks the very notion of encapsulation. It's not desirable. Also, it's dependent on dialog result, which is not desirable either.
I recommend use Angular-UI library. You can easy create any dialog a-la "Twitter Bootstrap":
Include js in your page head.
<script src="/desktop/libs/angular-bootstrap/ui-bootstrap.js"></script>
<script src="/desktop/libs/angular-bootstrap/ui-bootstrap-tpls.js}"></script>
Include modules at app initialization.
var Application = A.module('MyApp', [ 'ui.bootstrap', 'ui.bootstrap.modal' ]);
Inject in jour controller $modal:
(function (A){
"use strict";
A.module("MyApp").controller("OpenDlg", [ "$scope", "$modal", function($scope, $modal){
$scope.openDlg = function(){
$modal.open({
controller : 'CategoryAddController',
templateUrl : '/admindesktop/templates/category/add/'
}).result.then(function(modalResult){
console.log(modalResult);
});
};
} ]);
}(this.angular));
For example, simple template for dialog:
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title text-center">Создание новой категории</h4>
</div>
<form class="modal-body form-horizontal" name="categoryForm">
<div class="form-group">
<label for="name" class="control-label col-xs-3">Название</label>
<div class="col-xs-9">
<input name='name' type="text" class="form-control" ng-model="category.name" maxlength=50 required ng-required="true"/>
</div>
<div class="row has-error" ng-show="errors.name">
<p ng-repeat="error in errors.name">{{ error }}</p>
</div>
</div>
<div class="container-fluid" ng-show="errors.length > 0">
<div class="row">
<p class="text-center text-danger" ng-repeat="error in errors">{{ error }}</p>
</div>
</div>
</form>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="save()" ng-disabled="categoryForm.$invalid">Сохранить</button>
<button class="btn btn-default" ng-click="cancel()">Отмена</button>
</div>
</div>
Main: controller for modal window:
(function(A) {
"use strict";
var app = A.module('MyApp');
app.controller('CategoryAddController', [ '$scope', '$modalInstance', 'Category', 'growl', function($scope, $modalInstance, Category, growl) {
$scope.save = function() {
var category = new Category($scope.category);
category.$save(function() {
growl.success('Категория успешно создана');
$modalInstance.close(true);
}, function(response) {
$scope.errors = response.data;
});
};
$scope.cancel = function() {
$modalInstance.close(false);
};
} ]);
}(this.angular));
I use Service for data changing between modal controller and parent scope:
(function(A){
"use strict";
A.module("MyApp").service('Storage', function(){
return {
storedData: undefined
};
});
}(this.angular));
In parent scope:
Storage.storedData = ...; //For example, selected row of table
In modal controller:
$scope.item = Storage.storedData; //Selected row of table
Also angular have special module type, value.

$parent variable corrupted when set from separate controller on ng-include

I am having a hard time understanding why the following code stops working altogether. Basically when I try to hide and show an ng-include using ng-show from the $parent scope it works fine. For instance in the attached plnkr if you hit "Create New" then "Cancel", or just "Toggle" the visibility of the ng-include is appropriately set.
However, if I try to set the visibility from the child scope Cancel, it works once, and then it fails completely. When you hit the "Cancel" button that is in the ng-include, the variable "createItemVisible" from the parent scope that controls visibility is somehow corrupted.
I've read through many other posts on $parent scope but I never saw a mention of where it works once and then not again.
The plnkr is here http://plnkr.co/edit/1tNpTzEBnTRHgvx6o0dc?p=preview
Here is the index.html code:
<div ng-controller="MainCtrl">
<h3>Items</h3>
<button type="button" class="btn btn-primary" ng-click="createItem()">Create New Item</button>
<button type="button" class="btn btn-alert" ng-click="createItemVisible=!createItemVisible">Toggle</button>
<button type="button" class="btn btn-danger" ng-click="createItemVisible=false">Cancel</button>
<div ng-include="'inlineform.html'" ng-show="createItemVisible"></div>
</div>
Here is the inlineform.html code:
<div ng-controller="ItemDetailCtrl">
<button type="button" class="close" ng-click="cancelItemDetail()">×</button>
<h3>New Item</h3>
<button ng-click="cancelItemDetail()" class='btn btn-danger'>Cancel</button>
</div>
Here is the script:
var myApp = angular.module('myApp', []);
myApp.controller('MainCtrl', function($scope) {
$scope.createItemVisible = false;
$scope.createItem = function() {
$scope.createItemVisible = true;
};
});
myApp.controller('ItemDetailCtrl', function($scope) {
$scope.cancelItemDetail = function() {
$scope.$parent.createItemVisible = false;
}
});
From a comment by Ilan Frumer I was able to better understand that you should not use primitives on the scope. I created a namespace to hold my variable.
var main = {
createItemVisible : false
};
Then I registered this namespace with the scope.
$scope.main = main;
I then updated any references accordingly.
Here is the updated plnkr http://plnkr.co/edit/1tNpTzEBnTRHgvx6o0dc?p=preview

Categories