Angular select with ngModel gives me [$rootScope:inprog] error - javascript

I am not very good with angular, but i know some basics. Now, I have access points and want to assign them to a building. I can select this building with a <select>. I have written a simple controller, but it wont work. What am I making wrong, i cant find a solution.
Edit 1:
I can see the option fields (they are 3). But after I select one of these, my browser console throws the exception
Edit 2: Plunkr -> https://plnkr.co/edit/EIPs8yVlTSaYQ0EuZLTb (i hope, this url works) .. When you click on "Neuer Access-Point" the error will occur, after you select something on "Gebäude"
Select field
<select ng-model="$ctrl.input.building">
<option ng-repeat="building in $ctrl.buildings" ng-value="building.id" ng-bind="building.name"></option>
</select>
Controller
(function () {
function createController(Building) {
var ctrl = this;
ctrl.buildings = null;
ctrl.input = {
host: '',
desc: '',
web: '',
building: ''
};
ctrl.$onInit = function () {
Building.getAll().then(function (res) {
if (res.status >= 200 && res.status < 300) {
ctrl.buildings = res.data;
}
});
};
}
angular.module('app').controller('CreateController', createController)
})();
Error
angular.js:14791 Error: [$rootScope:inprog] http://errors.angularjs.org/1.6.8/$rootScope/inprog?p0=%24apply
at angular.js:88
at p (angular.js:18897)
at m.$digest (angular.js:18319)
at m.$apply (angular.js:18640)
at Object.$$debounceViewValueCommit (angular.js:29394)
at Object.$setViewValue (angular.js:29372)
at angular.js:33596
at m.$eval (angular.js:18533)
at m.$apply (angular.js:18632)
at HTMLSelectElement.<anonymous> (angular.js:33595)

Here is the issue with your code.
<div id="wrapper" ng-app="accessPoints" ng-controller="RootController **as $root**">
Change it to simply
ng-controller="RootController as anythingButNot$root"
or even just ng-controller="RootController"
Declaring any controller as $root is creating mess for you. $root is the root level controller of your application. Inside your html if you declare any controller as $root, it tries to overwrite $root, creating trouble with digest cycle, hence you are getting the error.
Link to updated plunk => https://plnkr.co/edit/UcJHVmekMqWMXJBnv2Cu?p=preview

Related

Getting error while including AngularJS multiple chosen directive

I'm getting the following error while implementing the AngularJS chosen directive.
Error:
TypeError: a.map is not a function
at nh.q.writeValue (angularjslatest.js:307)
at Object.e.$render (angularjslatest.js:328)
at angularjslatest.js:310
at angularjslatest.js:146
at m.$digest (angularjslatest.js:147)
at m.$apply (angularjslatest.js:150)
at l (angularjslatest.js:102)
at XMLHttpRequest.s.onload (angularjslatest.js:108)
I'm explaining my code below:
<select chosen
multiple
class="form-control oditek-form"
name="category"
id="category"
ng-model="category"
ng-options="s.value as s.name for s in listOfCategory">
</select>
My controller code is given below.
$scope.listOfCategory = [{
name: 'Select Category',
value: ''
}];
$scope.category = $scope.listOfCategory[0];
var fileURL = '';
var url1 = '../service/admin/vechile/service/service.php?action=getAllCategoryData';
var method = 'GET';
var data1 = '';
DataService.connectToServerSideScript(method, url1, data1).then(function(response) {
if (response.length > 0) {
angular.forEach(response, function(obj) {
var cdata = {
'name': obj.category_name,
'value': obj.id
};
$scope.listOfCategory.push(cdata);
})
}
}, function(error) {
});
Here I'm getting all the data but those error is coming in browser console. Here I need to clear those error.
You need to setup your selected model as an array:
$scope.category = angular.isDefined($scope.listOfCategory[0]) ?
[$scope.listOfCategory[0]] : [];
Please note that this also fixes the following error message:
Error: TypeError: a.forEach is not a function
I was facing a same kind of 'chosen multiple select' related problem, which was getting 3 times same error. Error log is like below. The solution may help others.
TypeError: a.map is not a function
at mh.q.writeValue (angular.js:30513)
at e.$render (angular.js:33327)
at Object.c.$render (angular-chosen.min.js:7)
at angular.js:29306
at m.$digest (angular.js:18253)
at b.$apply (angular.js:18531)
at HTMLButtonElement. (angular.js:27346)
at HTMLButtonElement.dispatch (jquery.min.js:3)
at HTMLButtonElement.q.handle (jquery.min.js:3)
To bypass the issue, I have maintained 2 variables, one for a model variable and other for a saving variable. e.g.
In controller,
$scope.x = {
draftVar: []
};
$scope.save = function(){
$scope.x.savingVar = $scope.x.draftVar.join(',');
save();
}
<select ng-model="x.draftVar" chosen multiple chosen-updater ng-options="xx.id as xx.name for xx in XXs">
<option value=""></option>
</select>

Conflicts when working with scopes and controllers in AngularJS

I have a simple website that uses AngularJS with a NodeJS backend.
It has multiple pages, like a homepage, a login/register page, etc.
I'd like to implement a "Chat" page where you could send messages to other clients using socket.io. I already got that part working, using a local controller (by local, I mean active on a single page - the Chat page).
The problem is, I would like the chat system to be global (i.e. client can receive messages while being on the homepage, but they'll still only be displayed when going back on the Chat page).
I'm having an issue when setting the Chat controller global (active on all pages).
Here's how I'm including it:
<body ng-controller="AppCtrl"> <!-- include main controller -->
<div ng-include="'header.tpl.html'"></div>
<div ng-controller="ChatCtrl" class="page"> <!-- include global Chat controller -->
<div ng-view class="container"></div>
</div>
<div ng-include="'footer.tpl.html'"></div>
<!-- ...etc. -->
</body>
This works pretty well, but it seems like I can't access a value from my Chat page, though. Functions declared from the Chat controller can still be called, but the "$scope.message" value (which contains the message that's being typed) is always empty.
Here's my Chat controller (which is actually called TravelCtrl)
angular.module('base').controller('TravelCtrl', //['$scope', 'security',
function($rootScope, $scope, security, NgMap, $geolocation, socket){
$scope.messages = [];
// Socket listeners
// ================
socket.on('init', function (data) {
$scope.name = data.name;
$scope.users = data.users;
});
socket.on('send:message', function (message) {
$scope.messages.push(message);
});
socket.on('change:name', function (data) {
changeName(data.oldName, data.newName);
});
socket.on('user:join', function (data) {
$scope.messages.push({
user: 'Server',
text: 'User ' + data.name + ' has joined.'
});
$scope.users.push(data.name);
});
// add a message to the conversation when a user disconnects or leaves the room
socket.on('user:left', function (data) {
$scope.messages.push({
user: 'chatroom',
text: 'User ' + data.name + ' has left.'
});
var i, user;
for (i = 0; i < $scope.users.length; i++) {
user = $scope.users[i];
if (user === data.name) {
$scope.users.splice(i, 1);
break;
}
}
});
// Private helpers
// ===============
var changeName = function (oldName, newName) {
// rename user in list of users
var i;
for (i = 0; i < $scope.users.length; i++) {
if ($scope.users[i] === oldName) {
$scope.users[i] = newName;
}
}
$scope.messages.push({
user: 'Server',
text: 'User ' + oldName + ' has been authenticated as ' + newName + '.'
});
}
// Methods published to the scope
// ==============================
$scope.changeName = function () {
socket.emit('change:name', {
name: $scope.newName
}, function (result) {
if (!result) {
alert('There was an error changing your name');
} else {
changeName($scope.name, $scope.newName);
$scope.name = $scope.newName;
$scope.newName = '';
}
});
};
$scope.sendMessage = function () {
socket.emit('send:message', {
message: $scope.message
});
// add the message to our model locally
$scope.messages.push({
user: $scope.name,
text: $scope.message
});
// clear message box
$scope.message = '';
};
// ================
var init = function () {
$scope.newName = security.currentUser.username;
$scope.changeName();
}
if ($rootScope.hasLoaded() && $scope.name != security.currentUser.username) {
init();
} else {
$rootScope.$on('info-loaded', init);
}
}
//]
);
As well as the Chat page itself. The strange thing is that connected users and messages display correctly, but the controller can't seem to retrieve the typed message.
<div class='col'>
<h3>Users</h3>
<div class='overflowable'>
<p ng-repeat='user in users'>{{user}}</p>
</div>
</div>
<div class='col'>
<h3>Messages</h3>
<div class='overflowable'>
<p ng-repeat='message in messages' ng-class='{alert: message.user == "chatroom"}'>{{message.user}}: {{message.text}}</p>
</div>
</div>
<div class='clr'>
<form ng-submit='sendMessage()'>
Message: {{message}}<br/>
<input size='60', ng-model='message'/>
<input type='submit', value='Send as {{name}}'/>
</form>
</div>
When pressing the "Send" button, AngularJS successfully calls the sendMessage function, but retrieves the "message" value as an empty string, leading it to send an empty socket.io message.
I'm quite new to AngularJS, so my approach might be totally ridiculous. I'm convinced I'm missing something obvious but after re-reading the docs again, I really can't seem to find what.
Is this a proper way to organise an AngularJS app?
Thanks in advance for your help.
Having recently built a large scale Angular/Socket.IO application, I strongly suggest that you put all of your Socket implementation into a Service. This service will maintain all of your socket state, and allow you to inject it into any required controllers. This will allow you to have a main page for Chat, however still be able to display notifications, chat user information, etc in other areas of your application.
It's not about your problem, but I saw something I suspect to be wrong.
When you use another library with angularjs, you should use a bridge to it (angular-socket-io for example).
When you do an $http call with angular, it updates $scope correctly in the callback and the changes are seen in the view.
In your code:
socket.on('send:message', function (message) {
$scope.messages.push(message);
});
There is a problem: "socket" isn't a library included in angularjs, so when the callback is called, your "$scope" modification isn't correctly noticed to angularjs.
You have to do use $scope.$apply(function() { code here which modifies $scope });
Example:
socket.on('send:message', function (message) {
$scope.$apply(function() {
$scope.messages.push(message);
});
});
EDIT:
I would like the chat system to be global (i.e. client can receive messages while being on the homepage, but they'll still only be displayed when going back on the Chat page).
Either store the datas in a global variable, or use $rootScope which is the parent scope of all the $scope you use in the application.
EDIT 2:
In fact it should solve your problem ;)
Another things:
1) use $rootScope instead of $scope for global variables (or a global variable). In any $scope you will access $rootScope variables ($scope is a copy of either $rooScope or a parent $scope).
2) register socket.io only once. Currently, if you change pages, you will register new callbacks at EACH page change.

How to create own angular service with XHR properly?

I am very new about AngularJS things. Need to do file upload with other datas in form, I found some scripts and angular plugins but I am using my own service calls $xhr. I was able to send file but i got error, bug(not real error-bug, i just named like that) or i can not use AngularJS properly. Here it is:
.
JS
var app = angular.module('ngnNews', []);
app.factory('posts', [function () {...}]); // I reduced the codes
app.factory('$xhr', function () {
var $xhr = { reqit: function (components) { ... //My Xml HTTP Request codes here }}
return $xhr;
});
app.controller('MainCtrl', ['$http','$scope','$xhr','posts',
function ($http, $scope, $xhr, posts) {
$scope.posts = posts.posts;
$scope.files = [];
var newPost = { title: 'post one', upvotes: 20, downvotes: 5 };
$scope.posts.push(newPost);
$scope.addPost = function () {
$xhr.reqit({
form: document.getElementById('postForm'),
callbacks: {
success: function (result) {
if (result.success) {
console.log($scope.posts); //[FIRST OUT]
$scope.posts.push(result.post);
$scope.title = '';
console.log($scope.posts); //[SECOND OUT]
}
}
},
values: { upvotes: 0, downvotes: 0 },
files: $scope.files
});
...
}
}]);
.
HTML
<form action="/Home/FileUp" id="postForm" method="post" enctype="multipart/form-data">
<div class="form-group input-group">
<span class="input-group-addon">Post Title</span>
<input name="title" class="form-control" type="text" data-ng-model="title" />
</div>
<ul>
<li ng-repeat="file in files">{{file.name}}</li>
</ul>
<button class="btn btn-primary" type="button" data-ng-click="addPost()">Add New</button>
</form>
SCREEN
Sample post displayed in list
.
PROBLEMS
When I click first time Add New button everything works well until $scope.posts.push(result.post);. In console, [SECOND OUT] is here:
First object has $$hashKey but second object which sent from server(added by $scope.posts.push(result.post); function) doesn't have. I want to know why is this happening? But it's not only weird thing, when I second time click Add New button, everything completed successfully (No new logs in console, adding new post to list shown screen image above).
MAIN PROPLEM
I pushed returned value from the server but post list(in screen) is not affected when first click.
QUESTIONS
- What is happening? or
- What am I doing wrong? Thanks for any explanation.
You are doing nothing wrong with respect to $$hashkey if that is your concern. When you use ng-repeat with array of objects angular by default attaches a unique key to the items which is with the property $$hashkey. This property is then used as a key to associated DOM elements with the corresponding item in the array by identity. Moving the same object in array would move the DOM element in the same way in the DOM. You can avoid this (addition of additional property on the object by angular) by using track by with ng-repeat by providing a unique key on the object or a mere $index. So with that instead of creating a unique key and attaching it to $$haskey property angular will use the unique identifier you have provided to associate the DOM element with the respective array item.
ng-repeat="post in posts track by $index"
or (id you have a unique id for each of the object in the array, say id then)
ng-repeat="post in posts track by post.id"
And since you say you are using my xml http request code here, i am assuming it is not within the angular context so you would need to manually perform the digest cycle by using $scope.$apply() is on of those ways.
$scope.addPost = function () {
$xhr.reqit({
form: document.getElementById('postForm'),
callbacks: {
success: function (result) {
if (result.success) {
$scope.posts.push(result.post);
$scope.title = '';
$scope.$apply();//<-- here
}
}
},
But ideally you could wrap your xhr implementation with a $q and if you pass $q promise from your api, you wont need to perform a manual $scope.$apply() everywhere. Because $q promise chaining will take care of digest cycle invocation.

Meteor template isn't rendering properly

I'm building a notifications page, where the user can see which posts have comments, and I want to display the date of each post, but it's not working.
Here is the code:
<template name="notification">
<li>Someone commented your post, {{postDate}} </li>
</template>
Template.notification.helpers({
notificationPostPath: function() {
return Router.routes.PostPage.path({_id: this.postId});
},
post: function () {
return Post.findOne({_id: this.postId});
},
postDate: function() {
return moment(post.submitted).format('dddd, MMMM Do');
}
});
The console prints this: Exception from Deps recompute: ReferenceError: post is not defined.
Thanks in advance
I assume the error is being flagged on the following line:
return moment(post.submitted).format('dddd, MMMM Do');
Note that you can't refer to helpers from within other helpers like that (and anyway, post is a function) - you need too add another line at the start of the postDate helper like this:
var post = Post.findOne({_id: this.postId});

knockout.js binding error within foreach block

I am new to knockout.js and am having a problem with binding within a foreach section. I am receiving the error:
Uncaught Error: Unable to parse bindings.
Message: ReferenceError: hideSearchElements is not defined;
Bindings value: click: hideSearchElements
Here is an exert of the html:
<div id="searchResults" data-bind="visible: searchIsVisible">
<label id = "lblSearchResults">select a template:</label>
<div data-bind="foreach: titles">
<div data-bind="text: Title"></div>
<div data-bind="click: hideSearchElements">hide</div>
</div>
And an exert from the viewModel:
var viewModel = function () {
this.searchIsVisible = ko.observable(true);
this.showSearchElements = function () {
this.searchIsVisible(true);
};
this.hideSearchElements = function (
this.searchIsVisible(false); }
}
return new viewModel();
I have both showSearchElements and hideSearchElements working fine outside of the foreach block but when inside it, I get the error.
If I add $parent.hideSearchElements I can bind but then get an error saying:
Uncaught TypeError: Object # has no method 'searchIsVisible'
.
I have probably have two distinct issues but thought the detail may help :)
I'm keen to understand what's going on here? Can anyone help please?
A link to the relevant page in the documentation would also be very helpful - I'm reading through that now.
Thanks
You was right when use $parent.hideSearchElements because hideSearchElements function is in a parent context. You got exception because when knockout calls your function this has another context. You have to use closure to store this pointer. Update your view model as follow:
var viewModel = function () {
var self = this;
self.searchIsVisible = ko.observable(true);
self.showSearchElements = function () {
self.searchIsVisible(true);
};
self.hideSearchElements = function (
self.searchIsVisible(false); }
}

Categories

Resources