Having trouble with ons-list and angular ng-repeat - angularjs

I declared a very simple component called betroomList:
(function () {
'use strict';
angular.module('betfriends')
.component('betroomList', {
templateUrl: 'src/home/betroomList.template.html',
controller: BetroomListController,
bindings: {
betrooms: '<'
}
});
function BetroomListController() {
var ctrl = this;
ctrl.betrooms = ['item1', 'item2', 'item3'];
}
})();
As you can see, its controller contains a table of strings. In the template HTML, I would like to create an ons-list with the content of this table. Here is what contains betroomList.template.html:
<ons-list>
<ons-list-item ng-repeat="item in $ctrl.betrooms">
<h1>{{item}}</h1>
</ons-list-item>
</ons-list>
My problem is that while it works fine in Google Chome it does not it Firefox. Indeed, in Chrome it correctly displays the list with 3 rows "item1", "item2" and "item3" but in Firefox I only have a list of one element "{{item}}".
In addition, the Firefox console contains an error:
Error: node is undefined
compositeLinkFn#http://localhost:63342/BetFriends/www/lib/angular.js:8623:13
nodeLinkFn#http://localhost:63342/BetFriends/www/lib/angular.js:9330:24
compositeLinkFn#http://localhost:63342/BetFriends/www/lib/angular.js:8620:13
publicLinkFn#http://localhost:63342/BetFriends/www/lib/angular.js:8500:30
It might be worth noting that if I replace respectively ons-list with ul and ons-list-item with li, everything works as expected.
Does anyone have an idea why this doesn't work?
Thanks a lot in advance.

You might have to take it up with Onsen UI.
The only thing I can see is that they recommend using 3 classes for the item, basically to be left, centre and right:
<ons-list-item>
<div class="left">Left</div>
<div class="center">Center</div>
<div class="right">Right</div>
</ons-list-item>
You are using a H1 tag around your {{item}}. Maybe you should try using just a div, and see what it does.

Related

ng-click not working on a programmatically added buttons

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>

How to hide <img> using md-switch

angular-material newbie here. I'm trying to hide some elements in our site using a switch, as instructed by our client. This led me to angular-material's md-switch.
So I tried incorporating it like so...
<md-switch md-no-ink aria-label="switchView" ng-model="switchView">{{switchView}}</md-switch>
And called the value of the switch in my element like this:
<img ng-src="{{photoPath}}" class="profilePic" ng-hide="switchView"/>
After testing it though, it didn't hide my <img> even though my switchView changed its value. Am I missing something here?
Other methods I've tried:
Adding ng-change to my md-switch, which called a function that would equate another variable (e.g. $scope.toggleView = $scope.switchView) with switchView's value. $scope.toggleView would then be used in my ng-hide.
ng-hide = "switchView == true".
Any advice would be very much appreciated. Thank you.
UPDATE 1: To test it, I tried hiding the <div> beside my <md-switch> and it worked perfectly. However it's still not working with my <img>.
Further checking revealed that it was inside a <nav> element. However they're both using the same controller. I wonder if that's the problem? I assumed that it shouldn't be a problem because of this.
The structure is like this:
<nav ng-controller="MainController">
<!-- other navigation elements here -->
<img ng-src="{{photoPath}}" class="profilePic" ng-hide="switchView"/>
</nav>
<div ng-controller="MainController">
<div>Toggle Switch</div>
<md-switch md-no-ink aria-label="switchView" ng-model="switchView">{{switchView}}</md-switch>
</div>
UPDATE 2: I've added the following code in my JS file because there are plans to hide elements in other pages. It still didn't work.
$scope.onChange = function(value) {
$scope.$broadcast("view_mode", $scope.switchView);
}
$scope.$on("view_mode", function(event, switchValue) {
$scope.viewThis= switchValue;
});
My HTML now looks like this:
<img ng-src="{{photoPath}}" class="profilePic" ng-hide="viewThis"/>
As for the controller, ngMaterial was called in a separate JS file (our main), together with all our dependencies and configs. Hence, this wasn't called inside MainController.
mainApp.js
var app = angular.module('myAppModule', [
// other references here
'ngMaterial'
]);
mySample.js
angular
.module('myAppModule')
.controller('MainController', ['$scope', function ($scope) {
// functions etc. go here
Hope this helps clear things up. Thank you.
Try something like this, its a trivial example but hope it helps. Here's a link to working codepen.
There seems to be a couple ways you could handle this according to the docs:
Angular Material- MD-Switch
function exampleController($scope) {
$scope.secondModel = false;
$scope.onChangeEvent = function(value) {
$scope.imgSource = (value) ? 'http://www.fillmurray.com/300/200' : 'http://www.fillmurray.com/g/155/300';
};
// alternatively: you could set the ternary to empty string value here.
}
angular
.module('BlankApp', ['ngMaterial'])
.controller('exampleController', exampleController);
<md-switch ng-model="switchValue" ng-change="onChangeEvent(switchValue)">
<img ng-src="{{imgSource}}" alt="" />
</md-switch>
<md-switch ng-model="secondModel">
<img src="http://www.fillmurray.com/300/200" alt="" ng-hide="secondModel" />
</md-switch>
Thank you to everyone who gave their inputs. After some research, I managed to solve this problem using factories (source).
I'm sharing my solution so that it may help others who experience the same problem.
HTML:
<nav ng-controller="MainController">
<!-- other navigation elements here -->
<img ng-src="{{photoPath}}" class="profilePic" ng-hide="switchView"/>
</nav>
<div ng-controller="MainController">
<div>Toggle Switch</div>
<md-switch md-no-ink aria-label="switchView" ng-model="switchView" ng-change="onChange(switchView)">{{switchView}}</md-switch>
</div>
JS:
angular
.module('myAppModule')
.controller('MainController', ['$scope', 'ViewModeFactory', function ($scope, ViewModeFactory) {
// functions etc. go here
// For client presentation mode
$scope.onChange = function(value) {
ViewModeFactory.setViewMode($scope.switchView);
}
$scope.$watch(function (){
$scope.switchView = ViewModeFactory.getViewMode();
});
}])
.factory('ViewModeFactory', function () {
var data = {isViewMode: ''};
return {
getViewMode: function () {
return data.isViewMode;
},
setViewMode: function(value) {
data.isViewMode = value;
}
};
});
I used factories so that other controllers in our site can use the value passed by md-switch.
Hope this helps.

AngularJS Communication between ng-repeat and controller

I've just started using AngularJS, and as a project I decided to build a simple app for managing bookmarks. I want to be able to maintain a list of bookmarks and add/remove items. I'm using Django with Django REST framework, and Angular.
So far I've written a service to grab the bookmarks from the database, and I can print them to the console from my controller, but ng-repeat doesn't seem to be seeing them.
Here's my code for the service:
.factory('BookmarkService', ["$http", function ($http) {
var api_url = "/api/bookmarks/";
return {
list: function () {
return $http.get(api_url).then(function (response) {
return response.data
})
}
}
}]);
And for the controller:
.controller("ListController",
["$scope", "BookmarkService", function ($scope, BookmarkService) {
$scope.bookmarks = BookmarkService.list();
console.log($scope.bookmarks);
}]);
And here's the HTML:
<div ng-controller="ListController as listCtrl">
<md-card>
<md-card-content>
<h2>Bookmarks</h2>
<md-list>
<md-list-item ng-repeat="bookmark in listCtrl.bookmarks">
<md-item-content>
<div class="md-tile-content">
<p>{[{ bookmark.name }]} - {[{ bookmark.url }]}</p> // used interpolateProvider to allow "{[{" instead of "{{"
</div>
<md-divider ng-if="!$last"></md-divider>
</md-item-content>
</md-list-item>
</md-list>
</md-card-content>
</md-card>
</div>
When I print to the console from the controller I can see a promise object but ng-repeat isn't repeating:
image of promise object
I'd really appreciate if someone could help me to find my mistake and to understand why it is happening. I'm still not entirely comfortable with how all these parts fit together.
Thanks for your time!
There's two problems that I see with the code in question.
The first is that using the controller as syntax (ng-controller="ListController as listCtrl") requires properties to be bound to the controller instance and not to the scope if you address them using the controller name. In your case,
.controller("ListController",
["BookmarkService", function (BookmarkService) {
this.bookmarks = BookmarkService.list();
console.log(this.bookmarks);
}]);
The second is that you are assigning a promise to your $scope.bookmarks property. The repeater is expecting an array of objects to iterate over. You really want to assign the value resolved by the promise to $scope.bookmarks.
Instead of this
$scope.bookmarks = BookmarkService.list();
Do this
BookmarkService.list().then(function(result){
this.bookmarks = result;
});
The final version of your controller should look something like this
.controller("ListController",
["BookmarkService", function (BookmarkService) {
this.bookmarks = BookmarkService.list();
console.log(this.bookmarks);
}]);
This is simple. Ng-repeat is not working with promises. So, you can go with two ways:
BookmarkService.list().then(function(responce){
$scope.bookmarks = responce.data;
});
Another way is to create own repiter ^)
Ng-repeat doc

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.

Resources