AngularJS: Splicing scope array removes entire ng-view - angularjs

I am writing an AngularJS app that contains a $scope array which I populate a ul with. Each li contains an "x" which when clicked calls a function in my Chatroom controller and removes the item from the $scope array.
What should happen is that the item is removed from the array and hence also from the view. But if I step through the debugger I can visually see the li being removed from the DOM, but afterwards my entire ng-view gets removed.
Images here:
Before clicking "x"
After clicking "x"
I can't seem to figure out why it's doing this. I shall post code below.
index.html
<!DOCTYPE html>
<html lang="en" ng-app="chatasticExtension" ng-controller="MainCtrl">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="css/bootstrap.min.css"> <!-- load bootstrap css -->
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<header>
</header>
<div>
<div ng-view></div>
</div>
</body>
<script src="js/vendors/bower_components/angular/angular.js"></script>
<script src="js/vendors/bower_components/angular-animate/angular- animate.js"></script>
<script src="js/vendors/bower_components/angular-bootstrap/ui-bootstrap.js"></script>
<script src="js/vendors/bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
<script src="js/vendors/bower_components/angular-socket-io/socket.min.js"></script>
<script src="js/vendors/bower_components/angular-route/angular-route.min.js"></script>
<script src="js/vendors/bower_components/socket-io-client/socket.io.js"></script>
<script src="js/vendors/bower_components/angular-socket-io/socket.min.js"></script>
<script src="js/app.js"></script>
<script src="js/routes.js"></script>
<script src="js/constants/configs.js"></script>
<script src="js/services/socket-factory.js"></script>
<script src="js/services/authentication.js"></script>
<script src="js/services/chrome-tabs-manager.js"></script>
<script src="js/controllers/main.js"></script>
<script src="js/controllers/chatroom.js"></script>
<script src="js/controllers/tabs.js"></script>
<script src="js/directives/header.js"></script>
<script src="js/directives/rooms-lists-tabs.js"></script>
<script src="js/directives/chat-window.js"></script>
<script src="js/directives/ng-enter.js"></script>
</html>
main.html (This is what is displayed in ng-view and what disappears)
<div class="container main-container" ng-controller="ChatroomCtrl" >
<div class="col-xs-4">
<rooms-lists-tabs></rooms-lists-tabs>
</div>
<div class="col-xs-8">
<chat-window ng-hide="!activeConversation"></chat-window>
</div>
</div>
rooms-lists-tabs.html (Note the ng-click="leaveRoom($index) on the second ng-repeat for convo in conversations)
<uib-tabset active="active">
<uib-tab index="0" heading="{{tabs[0].name}}">
<div class="scrollable-container list-group">
{{ room.title }}
</div>
</uib-tab>
<uib-tab index="1" heading="{{tabs[1].name}}">
<div class="scrollable-container list-group">
<a id="a-{{urlToId(convo.url)}}" href="" class="list-group-item" ng-repeat="convo in conversations" ng-click="switchConversation(convo.url)">
{{ convo.title }}
<span class="pull-right glyphicon glyphicon-remove" ng-click="leaveRoom($index)"></span>
</a>
</div>
</uib-tab>
Chatroom Controller (leaveRoom function only)
$scope.leaveRoom = function(index) {
var conversation = $scope.conversations[index];
socket.emit("leaveRoom", {
user: $scope.user,
room: conversation.url
});
$scope.conversations.splice(index, 1);
}
/**
* Sets the activeConversation to a conversation in the conversations array that matches the passed in url.
* #param url
*/
$scope.switchConversation = function(url) {
if($scope.activeConversation && $scope.activeConversation.url === url) {
return;
}
var conversation = null;
for(var i = 0; i < $scope.conversations.length; i++) {
if($scope.conversations[i].url === url) {
conversation = $scope.conversations[i];
break;
}
}
if(conversation) {
$scope.activeConversation = conversation;
}
else {
// If the conversation was not found, add the conversation and try to switch again
addNewConversation(url);
$scope.switchConversation(url); // Possible infinite loop here if addNewConversation fails somehow.
}
};
Please let me know if you wish to see other pieces of code.
Thanks
UPDATE: As per Gary's suggestion, I tried using pop(). pop() works until I pop the very last item in the list at which point the entire ng-view will disappear again.
UPDATE 2: If I switch to the first tab right after I splice, I never get the error.
UPDATE 3: Added switchConversation function

I had the same issue and I solved it by the following way which I will explain in your case.
Add $event to your ng-click function i.e., ng-click="leaveRoom($event,$index)"
and now your leave room function should be like below
$scope.leaveRoom = function(event,index) {
var conversation = $scope.conversations[index];
socket.emit("leaveRoom", {
user: $scope.user,
room: conversation.url
});
event.preventDefault();
event.stopPropagation();
$scope.conversations.splice(index, 1);
};
This will only remove whatever index you selected

Related

vis.js error in angular JS

I am trying to use vis.js library to visualize a simple network. My controller is as follows
var App = angular.module('App', ['ngRoute','ngVis']);
App.controller('visController', ['$scope','$http','$window','VisDataSet',function($scope,$http,$window,VisDataSet)
{
$scope.Nodes = [];
$scope.Edges = [];
$http.post('/getNetworkData').success(function(response)
{
$scope.Nodes = response.Nodes;
$scope.Edges = response.Edges;
console.log($scope.Edges);
console.log($scope.Nodes);
});
var network = null;
var destroy = function(){
if(network!=null)
{
network.destroy();
network = null;
}
}
var draw = function()
{
destroy();
var nodes = new vis.Dataset($scope.Nodes);
var edges = new vis.Dataset($scope.Edges);
var container = angular.element( document.querySelector('#mynetwork'));
console.log(container);
var data = {
nodes : nodes,
edges : edges
};
var options = {
interaction: {
navigationButtons: true,
keyboard: true
}
};
network = new vis.Network(container,data,options);
network.on('select', function(params) {
console.log("clicked");
});
}
draw();
}]);
and my html div is as follows
<!DOCTYPE html>
<html ng-app="App">
<head>
<meta charset="UTF-8">
<title>Visualization Page</title>
<link href='http://fonts.googleapis.com/css?family=Titillium+Web:400,300,600' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="css/bootstrap/normalize.css">
<link rel="stylesheet" href="css/bootstrap/style.css">
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet">
<link href="css/visualizationPage/style_vispage.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.2/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.2/angular-route.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<script src="css/bootstrap/jquery.min.js"></script>
<script src="css/bootstrap/bootstrap.min.js"></script>
<script src="js/ng-file-upload-shim.min.js"></script>
<script src="js/ng-file-upload.min.js"></script>
<script src="controllers/controller.js"></script>
<script src="js/ngDialog.js"></script>
<link rel="stylesheet" type="text/css" href="css/createNetwork/ngDialog.css">
<link rel="stylesheet" type="text/css" href="css/createNetwork/ngDialog-theme-default.css">
<script src="js/vis.js"></script>
<script src="js/exampleUtil.js"></script>
<link rel="stylesheet" type="text/css" href="css/bootstrap/vis.css">
<script src="js/googleAnalytics.js"></script>
</head>
<body ng-controller="visController">
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar">
<li class="active">Visualization<span class="sr-only">(current)</span></li>
<li>Data</li>
</ul>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<h2 class="page-header">Visualization</h2>
<div id="mynetwork">
</div>
</div>
</div>
</div>
</body>
</html>
I am getting an error Uncaught Error: [$injector:modulerr] when i try to load the page. Can someone tell me what I am missing in my code.
My error is as follows
Error: [$injector:modulerr] http://errors.angularjs.org/1.5.2/$injector/modulerr?p0=n...)
at Error (native)
at https://ajax.googleapis.com/ajax/libs/angularjs/1.5.2/angular.min.js:6:416
at https://ajax.googleapis.com/ajax/libs/angularjs/1.5.2/angular.min.js:40:60
at p (https://ajax.googleapis.com/ajax/libs/angularjs/1.5.2/angular.min.js:7:355)
at g (https://ajax.googleapis.com/ajax/libs/angularjs/1.5.2/angular.min.js:39:135)
at https://ajax.googleapis.com/ajax/libs/angularjs/1.5.2/angular.min.js:39:304
at p (https://ajax.googleapis.com/ajax/libs/angularjs/1.5.2/angular.min.js:7:355)
at g (https://ajax.googleapis.com/ajax/libs/angularjs/1.5.2/angular.min.js:39:135)
at db (https://ajax.googleapis.com/ajax/libs/angularjs/1.5.2/angular.min.js:43:164)
at c (https://ajax.googleapis.com/ajax/libs/angularjs/1.5.2/angular.min.js:20:463
First, this is unrelated to your issue, but there are several things that should be cleaned up here.
Eliminate the use of success in your call to $http.post and replace it with then. Success is depracated.
It looks like this should be a call to $http.get, not post.
Draw should be called from the then method of the call to $http.get rather than at the end of your controller initialization. The way this is currently written you cannot guarantee that this call is complete and the data is available before your draw function executes.
As for the missing module, it is most likely a pathing issue, spelling issue or an issue with the order that you are loading your javascript files. That code would of course be in your html.
You're loading jquery more than once. You only need it once. When you're using angular with Jquery always load jquery first. Angular will make use of it instead of JQLite in those circumstances. I don't see anything else, but it's possible that loading JQuery after Angular and then loading it again is stepping on something.

How to determine ons-modal shown state?

I'm using an ons-modal to show a loading text and spinner icon when the app is fetching some data.
The code is as follows:
<ons-modal var="loadingModal">
<ons-icon icon="ion-load-c" spin="true"></ons-icon>
<br><br>
Cargando...
<br>
</ons-modal>
I can correctly show an hide it using loadingModal.show(); and loadingModal.hide();
But how would I know in Angular if it is shown or hidden?
Update
Apparently my not-so-elegant solution is not not so elegant after all :D
Here is a pull request that shows the method isShown() that should be available
soon I guess
Internally the function looks similar to whats in this answer
isShown() {
return this.style.display !== 'none';
}
Not a super elegant solution, but it works
if( $scope.loadingModal._element.css('display') == 'none'){
// hidden now
}else{
// visible now
}
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="utf-8">
<title>Modal | Onsen UI</title>
<link rel="stylesheet" href="https://rawgit.com/OnsenUI/OnsenUI/master/demo/styles/app.css"/>
<link rel="stylesheet" href="https://rawgit.com/OnsenUI/OnsenUI/master/build/css/onsenui.css">
<link rel="stylesheet" href="https://rawgit.com/OnsenUI/OnsenUI/master/build/css/onsen-css-components.css">
<script src="https://rawgit.com/OnsenUI/OnsenUI/master/build/js/angular/angular.js"></script>
<script src="https://rawgit.com/OnsenUI/OnsenUI/master/build/js/onsenui.js"></script>
<script src="https://rawgit.com/OnsenUI/OnsenUI/master/demo/app.js"></script>
<script>
function check($el){
return $el.css('display') === 'none' ? 'hidden' : 'visible';
}
angular.module('myApp').controller('PageController', function($scope) {
$scope.open = function() {
$scope.app.modal.show();
alert(check($scope.app.modal._element));
setTimeout(function() {
$scope.app.modal.hide();
alert(check($scope.app.modal._element));
}, 2000);
};
});
</script>
</head>
<body ng-controller="PageController">
<ons-navigator>
<ons-modal var="app.modal">
<ons-icon icon="ion-load-c" spin="true"></ons-icon>
<br><br>
Cargando...
<br>
</ons-modal>
<ons-toolbar>
<div class="center">Modal</div>
</ons-toolbar>
<p style="text-align: center">
<ons-button modifier="light" ng-click="open();">Open Modal</ons-button>
</p>
</ons-navigator>
</body>
</html>

ui.bootstrap rating directive doesn't seem to load

I can't see the star rating input anywhere. Isn't it loaded? Am I using the directive wrong? Please help.
I have included the ui.bootstrao, JQuery, Bootstrapm and thought the directive should work right out of the box.
When I try to specify ui.bootstrap when defining the ng-app the ng-resource stop working.
var app = angular.module('mainApp', ['ngResource','ui.bootstrap']);
test.html:
<!doctype html>
<html data-ng-app="mainApp">
<head>
<title>Test app/title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap-theme.min.css">
</head>
<body role="document">
<div class="container">
<div class="page-header">
<h1>Test</h1>
</div>
<div ng-controller="PostIndexCtrl">
<div class="row">
<rating value="rate" max="5"></rating>
<div ng-repeat="item in items">
<div class="col-xs-1 col-md-3">
{{item.name}} <img class="thumbnail" src="{{item.photo}}" />
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript"
src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="components/angular-bootstrap/ui-bootstrap.js"></script>
<script
src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"></script>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular-resource.min.js"></script>
<script src="js/services.js"></script>
</body>
</html>
service.js:
var app = angular.module('mainApp', ['ngResource']);
app.factory("Post", function($resource) {
return $resource("/api/item");
});
app.controller("PostIndexCtrl", function($scope, Post) {
Post.get(function(data) {
// alert(data.items);
$scope.items = data.items;
});
});
This line in your html seems off to me:
<script src="components/angular-bootstrap/ui-bootstrap.js"></script>
Use:
angular-bootstrap/ui-bootstrap-tpls.js
or
angular-bootstrap/ui-bootstrap-tpls.min.js
instead of simply using:
angular-bootstrap/ui-bootstrap.js
which doesn't contain the templates.
If using bower, then install with:
bower install angular-bootstrap
Depending on your setup (and possibly .bowerrc file) the angular-bootstrap directory will, by default, be put in the directory:
bower_components/angular-bootstrap/ (or perhaps simply components/angular-bootstrap in your config)
where you can find the files mentioned above.

AngularJS ngInclude dynamically changing its location

I have a few partial templates where the location is changed based on user actions via ng-click:
<div ng-include="contentUrl"></div>
<button ng-click="contentUrl = '../partials/testScriptForm.html'">Add Test Script</button>
This works great unless I the button above is inside of the partial itself, so if testScriptForm.html has a button:
<button ng-click="contentUrl = '../partials/testScriptCase.html'">Add Test Case</button>
Then nothing happens.
This seems due to ng-include getting a new (inherited but not shared?) scope.
What I can't figure is how to get the included template (partial) to change its own location.
I did try a function to change the $scope.$parent.contentUrl, it does seem to change but not "propagate" the changes.
In coffeescript:
$scope.changeParentLocation = (location) ->
$scope.$parent.contentUrl = location
Also tried to $scope.$apply() and $scope.$parent.$apply() in there and get the error:
Error: [$rootScope:inprog]
http://errors.angularjs.org/1.2.0rc1/$rootScope/inprog?p0=%24apply
Maybe I'm just mis-using includes...
Escape the isolated scope with "dotted model" reference:
<html ng-app>
<head>
<script src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
<script src="script.js"></script>
<script type="text/ng-template" charset="utf-8" id="/partials/testScriptForm.html">
<h1>This is testScriptForm.html</h1>
<button ng-click="tpl.contentUrl = '/partials/testScriptCase.html'">Change to Test Case</button>
</script>
<script type="text/ng-template" charset="utf-8" id="/partials/testScriptCase.html">
<h1>This is testScriptCase.html</h1>
<button ng-click="tpl.contentUrl = '/partials/testScriptForm.html'">Change to Test Form</button>
</script>
</head>
<body>
<div ng-controller="Ctrl">
<fieldset>
<div ng-include="tpl.contentUrl"></div>
</fieldset>
</div>
</body>
</html>
function Ctrl($scope) {
$scope.tpl = {};
$scope.tpl.contentUrl = '/partials/testScriptForm.html';
}

How to jump to anchor in angular's partial view?

I have many items in a long listview. How can my users jump to (i.e. bookmark) to a specific item by visiting mypage.html#the_item_id ?
Actually, it can when I use inline view [Sample 1], but not when I use partial view [Sample 2]. Is there a bug in the latter case, or must I use any workaround?
Thanks in advance!
Sample 1: You can visit page.html#a100 to see item 100 ::
<!doctype html>
<html ng-app>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
<script>
function MainCtrl($scope){
$scope.items = [];
for(var i=0; i<200; i++){$scope.items.push({id: i})}
}
</script>
</head>
<body ng-controller='MainCtrl'>
<div>
<ul>
<li ng-repeat="i in items"><a id='a{{i.id}}'>{{i.id}}</a></li>
</ul>
</div>
</body>
</html>
Sample 2: Can NOT visit page2.html#a100 to see item 100, WHY? ::
<!doctype html>
<html ng-app>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
<script>
function MainCtrl($scope){
$scope.items = [];
for(var i=0; i<200; i++){$scope.items.push({id: i})}
}
</script>
</head>
<body ng-controller='MainCtrl'>
<div ng-include="'scroll_view.html'"><!-- MUST use "'...'" notation here --></div>
</body>
</html>
And this is the scroll_view.html needed by sample 2::
<div>
<ul>
<li ng-repeat="i in items"><a id='a{{i.id}}'>{{i.id}}</a></li>
</ul>
</div>
You have to use autoscroll attribute on ng-include.
Check the docs here: http://docs.angularjs.org/api/ng.directive:ngInclude
autoscroll(optional) – {string=} – Whether ngInclude should call $anchorScroll to scroll the viewport after the content is loaded.
If the attribute is not set, disable scrolling.
If the attribute is set without value, enable scrolling.
Otherwise enable scrolling only if the expression evaluates to truthy value.
So in your case:
<div ng-include="'scroll_view.html'" autoscroll></div>
I think html5Mode needs to be set to true, but I'm not certain. See if this works for you (it did for me, but I only tested on Chrome 23 loading the page using file:///...):
<!doctype html>
<html ng-app="app">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
<script src="app.js"></script>
<script type="text/ng-template" id="scroll_view.html">
<div>
<ul>
<li ng-repeat="i in items"><a id='a{{i.id}}'>{{i.id}}</a></li>
</ul>
</div>
</script>
<script>
function MainCtrl($scope){
$scope.items = [];
for(var i=0; i<200; i++){$scope.items.push({id: i})}
}
</script>
</head>
<body ng-controller='MainCtrl'>
<div ng-include="'scroll_view.html'"><!-- MUST use "'...'" notation here --></div>
</body>
</html>
app.js:
var app = angular.module('app', []).
config(function($locationProvider) {
$locationProvider.html5Mode(true);
})

Resources