ng-click not working on a programmatically added buttons - angularjs

I'm trying to add some buttons (ons-button) dynamically. The buttons appear as expected but the ng-click related to them never gets fired. It seems to be a problem with my understanding of how DOM manipulation works under AngularJS.
I have tried to read related questions, but most of them are trying to use directives, so I would like to use the simple way here.
Here is the code:
var module = angular.module("my-app", ["onsen"]);
module.controller("ListenButtonController", [
"$http",
"$scope",
function($http, $scope) {
var myList = [];
$scope.onButtonClick = function(message){
alert('Button clicked, with the message '+message);
}
$scope.onAddButtons = function(newList){
myList = [...newList];
}
}
}
]);
Here is the HTML:
<ons-page ng-controller="ListenButtonController">
<ons-button modifier="large" ng-click="onAddButtons(['a','b','c'])">
Click to test
</ons-button>
<ons-button ng-repeat="element in myList" ng-click="onButtonClick(element)"></ons-button>
</ons-page>

Related

ng-repeat is not refreshed when ng-click method called from directive

I am working on creating reusable directive which will be showing composite hierarchical data .
On first page load, Categories like "Server" / "Software"/ "Motherboard" (items array bound to ng-repeat) would be displayed . If user clicks on "Server" then it would show available servers like "Ser1"/"Ser2"/"Ser3".
html :
<div ng-app="myApp" ng-controller="myCtrl" ng-init="init()">
<ul>
<li ng-repeat="item in items">
<div my-dir paramitem="item"></div>
</li>
</ul>
</div>
Now first time Items are loading, but clicking on any item is not refreshing ng-repeat. I have checked ng-click, "subItemClick" in below controller, method and it is being fired. However the items collection is not getting refreshed.
http://plnkr.co/edit/rZk9cbEJU90oupVgcSQt
Controller:
var myApp = angular.module('myApp', []);
myApp.controller('myCtrl', ['$scope', function($scope) {
$scope.init = function() {
$scope.items = [{iname: 'server',subItems: ['ser1', 'ser2','ser3']}
];
};
$scope.subItemClick = function(sb) {
if (sb.subItems.length > 0) {
var zdupitems = [];
for (var i = 0; i < sb.subItems.length; i++) {
zdupitems.push({
iname: sb.subItems[i],
subItems: []
});
}
$scope.items = zdupitems;
}
};
}])
.directive('myDir', function() {
return {
controller: 'myCtrl',
template: "<div><a href=# ng-click='subItemClick(paramitem)'>{{paramitem.iname}}</a></div>",
scope: {
paramitem: '='
}
}
});
I am expecting items like ser1/ser2 to be bound to ng-repeat on clicking "Server" but it is not happening .
Any help?
I think that onClick is screwing up the method's definition of $scope. In that context, the $scope that renders the ngRepeat is actually $scope.$parent (do not use $scope.$parent), and you're creating a new items array on the wrong $scope.
I realize the jsfiddle is probably a dumbed down example of what you're dealing with, but it's the wrong approach either way. If you need to use a global value, you should be getting it from an injected Service so that if one component resets a value that new value is reflected everywhere. Or you could just not put that onClick element in a separate Directive. What's the value in that?

onsen dialog have limited access to parent scope

I'm looking for some help on using onsen-ui's dialog component. It seems to be a scope issue.
In my HTML file, I have a dialog template looks like this.
<ons-template id="report.html">
<ons-dialog var="dlg" cancelable>
<h1>{{clicked.name}}</h1>
<ons-button onclick="clickButton()">Click</ons-button>
</ons-dialog>
</ons-template>
And in my controller, I have
$scope.clickButton = function(){
dlg.hide();
}
$scope.dialogs = {};
$scope.show = function(dlg) {
$scope.clicked = {'name':"bar"};
if (!$scope.dialogs[dlg]) {
ons.createDialog(dlg, {parentScope: $scope}).then(function(dialog) {
$scope.dialogs[dlg] = dialog;
dialog.show();
});
} else {
$scope.dialogs[dlg].show();
}
}
The weird part is that I can access "clicked" in the dialog scope, but not "reportButton", they are in the same scope, or at least I can tell.
Here is the codepen link
Thanks in advance!
To call the function from controller scope, you should be ng-click instead of onclick.
<ons-button ng-click="reportButton()">click</ons-button>
Working Codepen

No instance found for handle for md-sidenav inside ng-repeat

I'm trying to create a stack of multiple, dynamically created md-sidenavs. I can push their IDs onto a stack, but can not toggle them while using ng-repeat.
Using:
app.controller('MenuCtrl', ['$scope', '$mdSidenav', function($scope, $mdSidenav) {
var stack = [];
$scope.getStack = function() {
return stack;
}
$scope.add = function(id) {
stack.push(id);
}
$scope.toggle = function(id) {
$mdSidenav(id).toggle();
}
}]);
with:
<md-button ng-click="add('test')" class="menuBtn">Add</md-button>
<md-button ng-click="toggle('test')" class="menuBtn">Toggle</md-button>
<md-sidenav class="md-sidenav-left md-whiteframe-z2" md-component-id="{{id}}" ng-repeat="id in getStack()">
{{id}}
</md-sidenav>
Clicking on the Add button produces the md-sidenav element in the doc as expected. However, the Toggle button does nothing besides produce a "No instance found for handle test" error in the console. Declaring a static md-sidenav with md-component-id="test" works fine. I'm using AngularJS 1.3.15 and Angular Material 0.8.3.
From the `Sidenav' directive source it doesn't seem to accept dynamic value for md-component-id

How to hide popover in onsen ui

I have been following Drop down option menu using onsen UI, but I want to cancel the popover when tapping on it. Any idea.
Usually, you can just hide it with popover.hide(), but it seems there is a bug when you try to use again the same controller with a popover containing a list (the browser gets stuck and the CodePen sample get bugged). That's why you need to create another controller to hide the popover and a service to share the popover between the two controllers (Here you can find a working CodePen).
var app = ons.bootstrap();
app.controller('DropdownController', function($scope, myService) {
ons.createPopover('popover.html').then(function(popover) {
$scope.popover = popover;
myService.setPopover($scope.popover);
});
});
app.controller('MyController', function($scope, myService) {
$scope.destroyPopover = function() {
$scope.popover = myService.getPopover();
$scope.popover.hide();
}
});
app.service("myService", function(){
var sharedPopover
var setPopover = function(pop){
sharedPopover = pop;
};
var getPopover = function(){
return sharedPopover;
};
return {
setPopover: setPopover,
getPopover: getPopover,
};
});
After, just add the controller in the new popover.html template, and the ng-click="destroyPopover()" directive to the ons-list-item. Doing that, the popover will be hidden every time you click the list element.
<ons-template id="popover.html" >
<ons-popover direction="down" cancelable >
<ons-list ng-controller="MyController">
<ons-list-item modifier="tappable" ng-click="hidePopover()">Option 1</ons-list-item>
<ons-list-item modifier="tappable" ng-click="hidePopover()">Option 2</ons-list-item>
<ons-list-item modifier="tappable" ng-click="hidePopover()">Option 3</ons-list-item>
</ons-list>
</ons-popover>
</ons-template>
EDIT
There is another way to hide the popover without using an AngularJS Service.
Since Onsen UI 1.3 release it's possible to pass scope for dialogs and popover, when creating them with ons.createDialog(), ons.createPopover() and ons.createAlertDialog(). (source).
When creating a dialog the following syntax can be used:
ons.createDialog('dialog.html', {parentScope: $scope}).then(function(dialog) {
$scope.dialog = dialog;
});
and use
<ons-list-item modifier="tappable" ng-click="popover.hide()">Option 1</ons-list-item>
You can find a working CodePen example HERE.

$sce.trustAsHtml not working

Alright, so the following below that I've tested in code works:
javascript:
var string = '<p>hello</p>';
$scope.html = $sce.trustAsHtml(string);
html:
<span ng-bind-html="html"></span>
The thing I'm really trying to do is this: Pulling a bunch of items from the server in JSON form. One of the JSON's key is called "description", which is just a long string of html code.
I'm pushing these items into a $scope array so I can show them on the page through an ng-repeat directive. The code below summarizes how it's being done:
javascript:
$.getJSON('someURL', function(data) {
$scope.items = [];
for (var i = 0; i < data.count; i++) {
var item = {};
item.description = $sce.trustAsHtml(data.item[i].description);
$scope.items.push(item);
}
});
html:
<p ng-repeat="item in items">
<span ng-bind-html="item.description"></div>
</p>
This isn't producing any output for some reason. Something I read was that whatever variable you bind the $sce.trustAsHtml() to must be a $scope variable. In this case, I'm setting it to a regular variable "item.description" and then adding it to a $scope.item array.
I suspect this may be why it's not working, but I don't know how to go about fixing it.
Can anyone help with this?
Thanks!
Try <span ng-bind-html="'{{item.description}}'"></span>.
Something like this worked for me.
With AngularJS 1.4.9 it worked for me like this:
Controller:
.controller('MyCtrl', ['$scope', '$sce',
function($scope, $sce) {
$scope.infoText = $sce.trustAsHtml('My <strong>HTML text</strong>');
}
])
Template:
<span ng-bind-html="infoText"></span>

Resources