How does service variable change update to controllers - angularjs

I have a small problem and I don't understand this thing:
When I add an item to TestiFactorys arr - array it does update to both controllers
On the other hand why does not TestiFactorys arr_len update to both controllers. And in TestiController why do I have to "manually" update TestControllers list1_length to make it update to view but I don't have to update TestiContollers list1 to make it update to view
I am assuming that my poor Javascript or Javascript variable scope understanding is causing this but i just don't see it.
I am using AngularJS version 1.2.16
<!DOCTYPE html>
<html ng-app="TestiApp">
<head>
<title></title>
</head>
<body>
<div ng-controller="TestController">
List items from controller: {{list1}}<br>
List item count:{{list1_length}}
<input type="text" ng-model="param"><br>
<button ng-click="list1_add(param)">asd</button>
</div>
<br><br>
<div ng-controller="TestController2">
List items from controller2{{list2}} <br>
List items count in from controller2: {{list2_length}}
</div>
<script src="scripts/angular.min.js"></script>
<script src="scripts/app.js"></script>
</body>
</html>
And this is my app.js:
var TestiApp = angular.module('TestiApp', [])
TestiApp.factory('TestiFactory',function() {
var arr = ['abx','cbs'];
var arr_len = arr.length;
return {
list : function() {
return arr;
},
add_to_arr : function(n) {
arr.push(n);
},
arr_len : function() {
arr_len = arr.length;
return arr_len;
}
}
}
);
TestiApp.controller('TestController', function($scope, TestiFactory) {
$scope.list1 = TestiFactory.list();
$scope.list1_length = TestiFactory.arr_len();
$scope.list1_add = function (d) {
TestiFactory.add_to_arr(d);
$scope.param = '';
$scope.list1_length = TestiFactory.arr_len();
}
});
TestiApp.controller('TestController2', function($scope, TestiFactory) {
$scope.list2 = TestiFactory.list();
$scope.list2_length = TestiFactory.arr_len();
});
EDIT WITH SOLUTION
Here is working solution. Based to comments I decided to do more studying on Javascripts basics which
is of course the thing I should have done before trying to use this complex framework which uses Javascript. So now I have some basic understanding how to use references in Javascript and what primitive data types are. And based on that here is working version:
<!DOCTYPE html>
<html ng-app="TestiApp">
<head>
<title></title>
</head>
<body>
<div ng-controller="TestController">
List items from controller: {{list1()}}<br>
List item count:{{list1_len()}}
<input type="text" ng-model="param"><br>
<button ng-click="list1_add(param)">asd</button>
</div>
<br><br>
<div ng-controller="TestController2">
List items from controller2{{list2()}} <br>
List items count in from controller2: {{list2_length()}}
</div>
<script src="scripts/angular.min.js"></script>
<script src="scripts/app.js"></script>
</body>
</html>
And app.js:
var TestiApp = angular.module('TestiApp', [])
TestiApp.factory('TestiFactory',function() {
var arr = ['abx','cbs'];
return {
list : function() {
return arr;
},
add_to_arr : function(n) {
arr.push(n);
},
arr_len : function() {
return arr.length;
}
}
}
);
TestiApp.controller('TestController', function($scope, TestiFactory) {
$scope.list1 = TestiFactory.list;
$scope.list1_add = TestiFactory.add_to_arr;
$scope.list1_len = TestiFactory.arr_len;
});
TestiApp.controller('TestController2', function($scope, TestiFactory) {
$scope.list2 = TestiFactory.list;
$scope.list2_length = TestiFactory.arr_len;
});

I've ran into this many times. Factories and services in angular are not like scopes...they work using references. The reason the array updates in your controllers is because the original reference was updated. The length is not updating because the number type is primitive.
This should work:
TestiApp.controller('TestController', function($scope, TestiFactory) {
$scope.list1 = TestiFactory.list();
$scope.$watch('list1', function(list1) {
$scope.list1_length = list1.length;
});
$scope.list1_add = function (d) {
TestiFactory.add_to_arr(d);
$scope.param = '';
};
});
TestiApp.controller('TestController2', function($scope, TestiFactory) {
$scope.list2 = TestiFactory.list();
$scope.$watch('list2', function(list2) {
$scope.list2_length = list2.length;
});
});

Related

pass date between two controllers using rootscope angular js

i am using $rootScope to share my values. please check my code
user controller (user.js)
var app = angular.module('myApp', []);
app.controller('user', function($scope,$rootScope) {
$rootScope.test = "TEST";
});
customer controller (customer.js)
app.controller('customer', function($scope,$rootScope) {
$scope.value = $rootScope.test;
alert($scope.value);
});
this code seems to be ok. but result is undefined. i need to keep all data inside the controller also.
my result
how i pass this value correctly
Instead of user $rootScope to share some data between those controllers, you could also use a service. Here is an example :
(function () {
'use strict';
angular.module('app', []);
angular.
module('app')
.service('contextService', [function () {
var context = {
"foo": "bar",
"hello": "boys"
};
var service = {
getContext: function () {
return context;
},
getContextValue: function (key) {
return context[key];
},
setContextValue: function (key, value) {
context[key] = value;
}
}
return service;
}])
.controller('userController', ['$scope', 'contextService', function ($scope, contextService) {
var vm = this;
vm.context = contextService.getContext();
contextService.setContextValue("foo", "baz");
}])
.controller('customerController', ['$scope', 'contextService', function ($scope, contextService) {
var vm = this;
vm.context = contextService.getContext();
vm.updateContext = function (key, value) {
contextService.setContextValue(key, value);
}
}])
})();
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<meta charset="utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
<script src="module.js"></script>
</head>
<body>
<div ng-controller="userController as vm">
<strong>userController</strong>
<pre>
foo = {{ vm.context.foo }}
hello = {{ vm.context.hello }}
</pre>
</div>
<hr />
<div ng-controller="customerController as vm">
<strong>customerController</strong>
<pre>
foo = {{ vm.context.foo }}
</pre>
<button ng-click="vm.updateContext('hello', 'guys')">
Update context <strong>hello</strong> value
</button>
</div>
</body>
</html>

Initially checked checkboxes value does not pass in controller in AngularJS

I have a list of subjects coming from the controller which are populating checkboxes. Some of the checkboxes are checked intially, I have a edit button. When I click the update button, an alert should pop up, and the alert should show the checked subject's ids. But the problem is, when I load the page , and then if I click the update button, the initially checked subject ids do not pass to the controller. It works only if I uncheck those check boxes and recheck them. Here is my code snippet.
<html>
<head>
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script>
var myApp2=angular.module("myModule2",[]);
myApp2.controller("controller2",function($scope){
$scope.message="test";
$scope.Subjects=[
{SubjectId:1,SubjectName:"Bangla"},
{SubjectId:2,SubjectName:"English"},
{SubjectId:3,SubjectName:"Math"},
{SubjectId:4,SubjectName:"Science"},
{SubjectId:5,SubjectName:"BGS"},
{SubjectId:6,SubjectName:"Religious"},
];
$scope.SubjectIds=[2,5];
$scope.subjectModel = {};
$scope.edit = function () {
var checkedSubjects = [];
for (var k in $scope.subjectModel) {
if ($scope.subjectModel.hasOwnProperty(k) && $scope.subjectModel[k]) {
checkedSubjects.push(k);
}
}
//do your stuff with the ids in `checkedBooks`
alert(checkedSubjects);
}
});
</script>
</head>
<body ng-app="myModule2" ng-controller="controller2">
Subjects:
<div ng-repeat="subj in Subjects">
<input type="checkbox" ng-model="subjectModel[subj.SubjectId]" ng-checked="SubjectIds.indexOf(subj.SubjectId)!=-1"/>{{subj.SubjectName}}
</div>
<div>
<input type="button" class="btn btn-warning" value="Update" id="updt" ng-click="edit()"/>
</div>
</body>
</html>
The problem was improper use of ng-model.
First: To work ng-model properly you have to create proper instances of the variables which is created by the code and sets its value true or false depends on whether the ids exists in the variable $scope.Subjects[i]:
$scope.subjectModel = [];
for(var i = 0; i < $scope.Subjects.length; i++) {
$scope.subjectModel[$scope.Subjects[i].SubjectId]
= $scope.SubjectIds.indexOf($scope.Subjects[i].SubjectId) > -1 ? true:false;
}
Second: the edit function is changed whether the checkbox is checked or not by using the following modified code:
for (var k = 0; k < $scope.subjectModel.length; k++) {
if ($scope.subjectModel[k]) {
checkedSubjects.push(k);
}
}
Third: To load data dynamically, you need to call $scope.updateChkBox(); after $scope.Subjects = pl.data; The modified loadSubjects function given below:
function loadSubjects() {
var promiseGet = crudService.getSubjects();
promiseGet.then(function (pl) {
$scope.Subjects = pl.data;
$scope.updateChkBox();
},
function (errorPl) {
$log.error('failure loading Subjects', errorPl);
});
}
Please check the final working code snippet:
<html>
<head>
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script>
var myApp2=angular.module("myModule2",[]);
myApp2.controller("controller2",function($scope){
$scope.message="test";
$scope.Subjects=[
{SubjectId:1,SubjectName:"Bangla"},
{SubjectId:2,SubjectName:"English"},
{SubjectId:3,SubjectName:"Math"},
{SubjectId:4,SubjectName:"Science"},
{SubjectId:5,SubjectName:"BGS"},
{SubjectId:6,SubjectName:"Religious"},
];
$scope.SubjectIds=[2,5];
$scope.subjectModel = [];
$scope.updateChkBox = function (){
for(var i = 0; i < $scope.Subjects.length; i++) {
$scope.subjectModel[$scope.Subjects[i].SubjectId]
= $scope.SubjectIds.indexOf($scope.Subjects[i].SubjectId) > -1 ? true:false;
}
}
$scope.updateChkBox();
function loadSubjects() {
var promiseGet = crudService.getSubjects();
promiseGet.then(function (pl) {
$scope.Subjects = pl.data;
$scope.updateChkBox();
},
function (errorPl) {
$log.error('failure loading Subjects', errorPl);
});
}
$scope.edit = function () {
var checkedSubjects = [];
for (var k = 0; k < $scope.subjectModel.length; k++) {
if ($scope.subjectModel[k]) {
checkedSubjects.push(k);
}
}
//do your stuff with the ids in `checkedBooks`
alert(checkedSubjects);
}
});
</script>
</head>
<body ng-app="myModule2" ng-controller="controller2">
Subjects:
<div ng-repeat="subj in Subjects">
<input type="checkbox" ng-model="subjectModel[subj.SubjectId]" ng-checked="SubjectIds.indexOf(subj.SubjectId)!=-1"/>{{subj.SubjectName}}
</div>
<div>
<input type="button" class="btn btn-warning" value="Update" id="updt" ng-click="edit()"/>
</div>
</body>
</html>

Angular.js remove item from collections using filter

I need some help with this code, i'm storing "activities" and removing it.
For now it's storing but not removing. (I just need a simple solution to this code).
Angular.js
angular
.module("TodoList",["LocalStorageModule"])
.factory("TodoService", function(localStorageService){
var todoService = {};
todoService.key = "angular-todolist";
if(localStorageService.get(todoService.key)){
todoService.activities = localStorageService.get(todoService.key);
} else {
todoService.activities = [];
}
todoService.add = function(newActv){
todoService.activities.push(newActv);
todoService.updaLocalStorage();
};
todoService.updaLocalStorage = function(){
localStorageService.set(todoService.key, todoService.activities);
};
todoService.clean = function(){
todoService.activities = [];
todoService.updaLocalStorage();
};
todoService.getAll = function(){
return todoService.activities;
};
toDoService.removeItem = function (item) { **Creating function for remove**
toDoService.activities = toDoService.activities.filter(function (activity) {
return activity !== item;
});
toDoService.updateLocalStorage();
return toDoService.getAll();
};
return toDoService;
})
.controller("TodoListCtrl", function($scope, todoService){
$scope.todo = todoService.getAll();
$scope.newActv = {};
$scope.addActv = function(){
todoService.add($scope.newActv);
$scope.newActv = {};
}
$scope.removeActv = function (item) { **Scope for remove**
$scope.todo = ToDoService.removeItem(item);
}
$scope.clean = function(){
todoService.clean();
}
});
Html
<!DOCTYPE html>
<html ng-app = "TodoList">
<head>
<meta charset="utf-8">
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
<script src = "todoctrl.js"></script>
<script src = "angular-local-storage.min.js"></script>
</head>
<body ng-controller = "TodoListCtrl">
<ul>
<li ng-repeat = "actividad in todo">
{{actividad.descripcion}} -
{{actividad.fecha | date: 'short'}} -
x **Where is removing**
</li>
</ul>
<form ng-submit = "addActv()">
<input type="text" ng-model = "newActv.descripcion">
<input type="datetime-local" ng-model = "newActv.fecha">
<input type="submit" value = "Guardar">
</form>
<button ng-click = "clean()">Limpiar</button>
</body>
</html>
update your ToDoService.removeItem(item);function. if you want to remove an item from activity you use array.splice(item) to remove an item from an array. you want to remove an item ToDoService.removeItem(item); function but your function is not doing this. it is returning a value that is either true or false. change your function body.

Foreach with firebase

var myApp = angular.module("myApp", ["firebase"]);
var nick = prompt("Anna nimesi");
myApp.controller("MyController", ["$scope", "$firebaseArray",
function($scope, $firebaseArray) {
var msgref = new Firebase("https://(myapp).firebaseio.com/messages");
var usrref = new Firebase("https://(myapp).firebaseio.com/users");
$scope.messages = $firebaseArray(msgref);
$scope.users = $firebaseArray(usrref);
console.log($scope.users);
console.log($scope.messages);
var taken = false;
angular.forEach($scope.users, function(value, key) {
console.log(value, key);
if (value.username == nick) {
taken = true;
}
// put your code here
});
if (taken == false) {
$scope.users.$add({
username: nick
});
};
$scope.addMessage = function(e) {
if (e.keyCode == 13 && $scope.msg) {
var name = nick || "anynomous";
$scope.messages.$add({
from: name,
body: $scope.msg
});
$scope.msg = "";
}
}
}
])
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
<title>title</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script src="https://cdn.firebase.com/js/client/2.2.4/firebase.js"></script>
<script src="https://cdn.firebase.com/libs/angularfire/1.1.1/angularfire.min.js"></script>
</head>
<body ng-controller="MyController">
<div class="example-chat l-demo-container">
<ul id="example-messages" class="example-chat-messages">
<li ng-repeat="msg in messages">
<strong class="example-chat-username">{{msg.from}}</strong>
{{msg.body}}
</li>
</ul>
</div>
<input class="input" autofocus="true" ng-model="msg" ng-keydown="addMessage($event)" type="text" id="messageInput" placeholder="kirjota">
<script src="script.js"></script>
</body>
</html>
Im doing a chat, and i want to check if there is already a user with same name, so that there could not be two users with same nick. This does not work, and i can add many same nicks.
var usrref = new Firebase("https://(myapp).firebaseio.com/users");
$scope.users = $firebaseArray(usrref);
var taken = false;
for (var usr in $scope.users) {
if(usr.username == nick){
taken = true;
};};
if(taken == false){
$scope.users.$add({username:nick});
};
My messages is showing perfectly with ng-repeat in html, but i cant get this work. Its obviously something simple, but i have struggled too long with this.
Since you're using Angular, I'd suggest using Angular's forEach method. Here's an example of what you could do:
angular.forEach($scope.users, function(value, key) {
// put your code here
});
Edited
This should work for you. It checks your data after it has been loaded from Firebase. If the username is present, then it doesn't add the name. If it is not present, then it's added. You could check out the AngularFire API for $loaded().
var addNameToArray = true;
$scope.users.$loaded().then(function(data) {
angular.forEach(data, function(value, key) {
if(value.username === "John") {
addNameToArray = false;
}
});
if(addNameToArray) {
$scope.users.$add({username: "John"});
}
});
A for (var ... in ...) iterates over object keys. So if $scope.users is an array you'll get indexes: 0, 1, 2, ... in the usr variable. To iterate over an array you can use for example following construct:
$scope.users.forEach(function(user) {
});
But maybe instead of using an array it would be better to use an object indexed by username. Then you'll get immediate information if given user exists (users[nick] !== undefined) and you'll also avoid some other trouble. See Best Practices: Arrays in Firebase.

Use scope from multiple controllers on page

So i've split out my UI into subcomponents but then i realise that one of the components requires to be react to a dropdown change which is caught by the parent controller.
I can create a shared service for the variables and i have been able to inject the sub controller so that i can kick off functions BUT.
how do i then use the scope within the sub controller?
var ctrl1= $scope.$new();
$controller('ctrl', { $scope: ctrl1});
ctrl1.GetData();
this works fine. I can see data coming back in the console. BUT my ui doesnt change. What am i missing?
I've edited the post to illustrate what i'm attempting to do more clearly.
The drop down on change is caught by the parent controller but i then require the child controller to run away and get some data and update the UI.
It's an attempt to split out the components. Is this possible? Or have a split the components out too far?
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular.min.js"></script>
<script>
angular.module('app2', [])
.controller('ctrl2', ['$scope', '$http', function($scope, $http){
$scope.getdata = function(){
$http.post(WebServiceURL)
.success(function(data){
$scope.app2Data = "test2 data";
});
}
}]);
angular.module('app1', ['app2'])
.controller('ctrl1', ['$scope','$controller',function($scope, $controller){
$scope.name = 'Controller 1';
//just something to put in the ddp
$scope.data = [
{id:1, name: "test"},
{id:2, name: "test2"}
]
$scope.makeChanged = function(id){
//ddp has changed so i refresh the ui with some other data which is in got by ctrl2.
var cl2 = $scope.$new();
$controller('ctrl2', { $scope: cl2 });
cl2.getdata();
}
}]);
</script>
</head>
<body ng-app="app1">
<div ng-controller="ctrl1">
<p>here is: {{name}}</p>
<select ng-model="d" ng-options="d as dat.name for dat in data track by dat.id" ng-change="makeChanged(d.id)"></select>
<div>
{{app2Data.text}}
</div>
</div>
</body>
</html>
for anyone interested here's how i got round this.
I created a shared service between the two controllers. and created a callback on the service. i registered the call back on ctrl2 so when the shared variable changed the controller2 will do what i want it to and scope is freshed.
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular.min.js"></script>
<script>
angular.module('app1', ['app2'])
.controller('ctrl1', ['$scope', '$controller', 'appointmentSharedProperties',
function($scope, appointmentSharedProperties) {
$scope.name1 = 'Controller 1';
console.log('ctrl1');
//just something to put in the ddp
$scope.data = [{
id: 1,
name: 'test'
}, {
id: 2,
name: 'test2'
}];
$scope.makeChanged = function(value) {
//ddp has changed so i refresh the ui with some other data which is in got by ctrl2.
appointmentSharedProperties.setDetail(value);
console.log('in makeChanged: ' + value);
}
}
]).service('appointmentSharedProperties', function() {
var test = '';
var __callback = [];
return {
getDetail: function() {
return test;
},
setDetail: function(value) {
test = value;
if (__callback.length > 0) {
angular.forEach(__callback, function(callback) {
callback();
});
}
},
setCallback: function(callback) {
__callback.push(callback);
}
};
});
angular.module('app2', [])
.controller('ctrl2', ['$scope', 'appointmentSharedProperties',
function($scope, appointmentSharedProperties) {
$scope.name2 = 'Controller 2';
console.log('ctrl2');
var getdata = function() {
console.log('in getdata');
$scope.app2Data = appointmentSharedProperties.getDetail();
}
appointmentSharedProperties.setCallback(getdata);
}
]);
</script>
</head>
<body ng-app="app1">
<div ng-controller="ctrl1">
<p>here is: {{name1}}</p>
<p>here is: {{name2}}</p>
<select ng-model="d" ng-options="d as dat.name for dat in data track by dat.id" ng-change="makeChanged(d.name)"></select>
<div>
{{app2Data}}
</div>
</div>
</body>
</html>
General example of how to pass variables from one controller to other
<html>
<head>
<meta charset="ISO-8859-1">
<title>Basic Controller</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js">
</script>
</head>
<body ng-app="myApp">
<div ng-controller="ctrl1">
{{greeting}}
</div>
<div ng-controller="ctrl2">
{{dataToHtml2}}
</div>
</body>
</html>
This is the javascript file for this
var myApp = angular.module('myApp',[]);
myApp.service('sampleService', function(){
var temp = '';
this.setValue = function(data){
temp = data;
}
this.getValue = function(){
return temp;
}
});
myApp.controller('ctrl1', function($scope,sampleService) {
$scope.greeting = 'This line is in first controller but I exist in both';
var data= $scope.greeting;
sampleService.setValue(data);
});
myApp.controller('ctrl2', function($scope, sampleService){
$scope.dataToHtml2 =sampleService.getValue();
});
Here is the blog that explains this flow : Frequently asked questions in angularjs
It has the demo of what I written. Happy coding..!!

Resources