I followed some tutorials on the web for todo lists... but I think, these tutorials don't use best practises for the implementation. All code is in the controller.
My controller looks like this and I think the code for the change-listener is not at the best "place" there. Where should I implement the listener?
.controller('TodosCtrl', ['$scope', '$state', 'Todo', function($scope, $state, Todo) {
$scope.create = function() { $state.go('todo_create'); };
$scope.todos= [];
$scope.$on('$ionicView.loaded', function() {
localDB.changes({
since: 'now',
live: true,
include_docs: true
}).on('change', function (change) {
if (change.doc && change.doc._id.substring(0, change.doc._id.indexOf('_')) === 'todo') {
if (change.deleted) {
....
} else {
....
}
}
});
});
Todo.all().then(function (result) {
for (var i = 0; i < result.length; i++) {
$scope.todos.push(result[i].doc);
}
});
}])
Conceptually all related code the is coupled to the DOM should be in the controller.
All other logic should be inserted to services.
See my comment for an exmaple:
.controller('TodosCtrl', ['$scope', '$state', 'Todo', function($scope, $state, Todo) {
$scope.create = function() { $state.go('todo_create'); };
$scope.todos= [];
$scope.$on('$ionicView.loaded', function() {
localDB
/** Not sure exactly what it is but if that some sort of initialization - this is better to be placed in one of the services
.changes
({
since: 'now',
live: true,
include_docs: true
})
*/
.on('change', function (change) {
if (change.doc && change.doc._id.substring(0, change.doc._id.indexOf('_')) === 'todo') {
if (change.deleted) {
....
} else {
....
}
}
});
});
Todo.all().then(function (result) {
for (var i = 0; i < result.length; i++) {
$scope.todos.push(result[i].doc);
}
});
}]) ;
Related
I'm trying to learn the basics of angularjs and am working with the tutorial app and ng-js v1.6.6. Although the items show up (localhost), the browser shows a warning as seen in the screengrab. Chrome doesn't show the error-firefox does. Is the code okay or are there errors on line 17 / line 50?
'use strict';
angular.module('components.phone')
.factory('Phone', ['$resource', function($resource,) {
return $resource('components/phone/data/phones/:phoneId.json', {}, {
query: {
method:'GET',
params: { phoneId:'phones' },
isArray: true
}
}
)
return factory;//<======LINE 17 -IS THE REST OF THE CODE 'UNREACHABLE'?
}])
.service('PhoneService', ['$q','$http', '$resource', function($q, $http) {
var service = {
get: function() {
var deferred = $q.defer();
$http.get('components/phone/data/phones/phones.json', {
params: { phoneId:'phones' }
})
.then(function(response){
deferred.resolve(response.data);
})
.catch(function(response){
deferred.resolve(response);
});
return deferred.promise;
},
getAllPhones: function() {
return $http.get('components/phone/data/phones/:phoneId.json', {
cache: true }
).then(function(resp) {
return resp.data;
});
},
getPhone: function(phoneId) {
for (i = 0; i < phones.length; i++) {
if(phones[i].id == phoneId){
this.phone = phones[i];
break; <==== THIS SYNTAX IS NOW CHANGED, THANKS#SYNCRO!
return $http.get('components/phone/data/phones/' + $stateParams.phoneId + '.json')
.then(function(response) {
self.phone = response.data;
})
}
return service.getAllPhones().then(function (getPhone) {
return selected.phone;
})
}
}
return service;
}])
.run(['$rootScope', '$state', '$stateParams',
function ($rootScope, $state, $stateParams) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
}
])
In your getPhone method, if you return inside a for loop, the code after the for loop will be not executed, that's why your browser is alerting you with this warning message.
You can see this message also in Chrome, you just have to change the Log priority in your Chrome DevTools.
You can see that return problem in this little script that I wrote for you: https://jsfiddle.net/kmy7hq8q/
To leave the loop you just have to use break, not return.
if(phones[i].id == phoneId){
this.phone = phones[i];
break;
}
i´ve got an problem with my ng-repeat.
After clicking ng-click, my ng-repeat doesn´t load, the page is empty. I´ve got an html file calls "food.html" in which i call the ng-click() further i´ve got an "food-snack.html" file which contains the ng-repeat and at least my "controller.js" where the function of ng-click is calling. I hope someone can help me. Im sorry for my confusing notation but it´s my first blog.
1.food-snack.html
<ion-content class="padding" >
<ion-refresher pulling-text="Refresh" on-refresh="refreshAll('snack')"></ion-refresher>
<ion-checkbox class = "item-checkbox-right checkbox-dark" ng-repeat="food in snacks">
<h2><b>{{food.food}} </b></h2>
<p>Preis: {{food.price}} {{food.currency}}</p>
</ion-checkbox>
</ion-content>
2.food.html
<ion-content class="padding">
<br><br><br><br><br><br><br>
<button class = "button button-block button-dark" ng-click = "getSnacks()" > Snacks </button>
<button class = "button button-block button-dark" ng-click = "getSandwich()" > Sandwich </button>
</ion-content>
3.controller.js
.controller('TablesCtrl', function($scope, $stateParams, $ionicPopup, $http, $state, $timeout, Foods) {
$scope.getSnacks = function(){
$state.go("tab.food-snack");
$http.get('http://xxxxxx/connect.php?getSnacks=1').then(function successCallback(response)
{
$scope.snacks = Foods.appendAll(response.data);
console.log(response);
}, function errorCallback(response) {
var confirmPopup = $ionicPopup.confirm({
title: 'Nicht erfolgreich',
cancelText: 'Nein',
okText: 'Ja',
okType: 'button-dark',
cancelType: 'button-positive'
});
});
};//END OF FUNCTION getSnacks()
$scope.getSandwich = function ()
{
$state.go("tab.food-sandwich");
$http.get('http://xxxxxx/connect.php?getSandwich=1').then(function successCallback(response)
{
$scope.sandwich = Foods.appendAll(response.data);
console.log(response);
}, function errorCallback(response) {
var confirmPopup = $ionicPopup.confirm({
title: 'Nicht erfolgreich',
cancelText: 'Nein',
okText: 'Ja',
okType: 'button-dark',
cancelType: 'button-positive'
});
});
}// END OF FUNCTION $scope.getSanwich()
4. app.js
.state('tab.foods', {
cache: false,
url: '/addTable/foods',
views: {
'tab-tables': {
templateUrl: 'templates/foods.html',
controller: 'TablesCtrl'
}
}
})
.state('tab.food-snack', {
cache: false,
url: '/addTable/foods/food-snack',
views: {
'tab-tables': {
templateUrl: 'templates/food-snack.html',
controller: 'TablesCtrl'
}
}
})
5.services.js
.factory('Foods', function() {
var foods = [];
return {
appendAll: function(array) {
for(var i = 0 ; i < array.length; i++)
{
foods.splice(array[i]);
}
for (var i = 0; i < array.length; i++)
{
foods.unshift(array[i]);
}
return foods;
},
getAll: function() {
return foods;
},
remove: function(food) {
foods.splice(foods.indexOf(food), 1);
},
removeAll: function(array) {
for( var i = 0 ; i < array.length; i++)
{
foods.splice(array[i]);
}
},
get: function(foodId) {
for (var i = 0; i < foods.length; i++) {
if (foods[i].id === parseInt(foodId)) {
return foods[i];
}
}
return null;
}
The controller you shared is the controller of the food. html I guess and it is adding the snack list to it's scope which is not the scope of snack.html. In the food controller just change the state and in the controller of snacks call the service to get the snacks.
I am working on a note-taking app.
I want my app to delete all checked radios on clicking the 'Remove' link. An insight into the code:
HTML:
<p>[ <a href='#/home'>Cancel</a> | <a href='#/home/edit' ng-click='remove()'>Remove</a> ]</p>
<table border='0'>
<tbody>
<tr ng-repeat='note in notes'>
<td>
<input type='radio' ng-model='note.rmv'></input>
</td>
<td>{{note.text}}</td>
</tr>
</tbody>
</table>
Controller:
app.controller('editCtrl', ['$scope', 'notes', function($scope, notes) {
$scope.notes = notes.notes;
$scope.remove = function() {
for (var i = 0; i < $scope.notes.length; ++i) {
if ($scope.notes[i].rmv) {
delete $scope.notes[i];
notes.getLost($scope.notes[i]._id);
}
}
};
}]);
Factory:
app.factory('notes', ['$http', function($http) {
var t = {
notes: []
};
t.getLost = function(id) {
return $http.delete('/home/edit').success(function(data) {
return t.getAll();
});
};
return t;
};
What might be doing wrong here?
Well, there are a lot of mistakes in your code, I think you should refactor your code. Also there's no necessity of delete the item in Javascript, you can delegate it all to your back-end since you already have the function to getAll objects.
See the code below to take it as example:
(function() {
angular
.module('app', [])
.controller('editCtrl', editCtrl)
.factory('notes', notes);
editCtrl.$inject = ['$scope', 'notes'];
function editCtrl($scope, notes) {
getAll(); // <- initialization of notes
$scope.remove = remove;
function getSuccess(response) {
console.log('success');
}
function getError(response) {
console.log('error');
}
function remove() {
for (var i = 0; i < $scope.notes.length; ++i) {
if ($scope.notes[i].rmv) {
notes.getLost($scope.notes[i]._id)
.then(getSuccess)
.catch(getError);
}
}
fetchData();
}
function fetchData() {
notes.getAll()
.then(function(response) {
$scope.notes = response.data;
})
.catch(function(response) {
console.log('error');
});
}
}
notes.$inject = ['$http'];
function notes($http) {
var factory = {
getAll: getAll,
getLost: getLost
};
return factory;
function getAll() {
return $http.get('url_to_fetch');
}
function getLost(id) {
// It should only return the promise, not more than that
return $http.delete('/home/edit/' + id); // <- is this URL correct?
}
}
})();
You have a typo:
if($notes.notes[i].rmv) {
Should be:
if($scope.notes[i].rmv) {
I looked into examples on how to do this properly but it's definitely not updating on my end. I put a breakpoint to make sure it's updating and going through the timer in the Factory and it's updating properly. I shouldn't have to use $watch right? If someone can help me figure out what's going on it would help with my headache right now lol thanks.
Factory
app.factory('FoundationSystemStatusFactory', ['$timeout', '$q', 'SystemStatusFactory', function ($timeout, $q, SystemStatusFactory) {
var service = {};
service.Count = 0;
service.Ping = 0;
service.PollingTest = function() {
$timeout(function () {
SystemStatusFactory.PingIP('www.google.com')
.then(function (data) {
service.Ping = data.data;
service.Count++;
}, function (data) {
service.Ping = data.data;
});
service.PollingTest();
}, 2000);
}
return service;
}]);
Controller
FoundationSystemStatusFactory.PollingTest();
$scope.ping = FoundationSystemStatusFactory.Ping; //NOT UPDATING
$scope.count = FoundationSystemStatusFactory.Count; //NOT UPDATING
EDIT: tried as Service, still couldn't get it to work:
var self = this;
self.Count = 0;
self.Ping = 0;
self.PollingTest = function () {
$timeout(function () {
SystemStatusFactory.PingIP('www.google.com')
.then(function (data) {
self.Ping = data.data;
self.Count++;
}, function (data) {
self.Ping = data.data;
});
self.PollingTest();
}, 2000);
}
A different approach - events.
app.factory('FoundationSystemStatusFactory', ['$rootScope', '$timeout', '$q', 'SystemStatusFactory', function ($rootScope, $timeout, $q, SystemStatusFactory) {
var service = {
Count: 0
};
service.PollingTest = function() {
$timeout(function () {
SystemStatusFactory.PingIP('www.google.com')
.then(function (data) {
$rootScope.$broadcast('FoundationSystemStatus:ping', data.data);
service.Count++;
}).catch(function (data) {
$rootScope.$broadcast('FoundationSystemStatus:ping', data.data);
});
service.PollingTest();
}, 2000);
}
return service;
}]);
//On controller...
$scope.$on('FoundationSystemStatus:ping', function(ping){
$scope.ping = ping;
});
You can use watcher:
$scope.$watch('FoundationSystemStatusFactory.Ping', function(newValue) {
$scope.ping = newValue;
});
Or you can use reference to factory:
$scope.status = FoundationSystemStatusFactory;
$interval(function() {
console.log($scope.status.Ping); // gets updated
});
Okay I found out how to do it after some more research. Objects are referenced as numbers and strings are not.
Factory
app.factory('FoundationSystemStatusFactory', ['$timeout', '$q', 'SystemStatusFactory', function ($timeout, $q, SystemStatusFactory) {
var service = {};
service.Data = {
Count: 0,
Ping: 0
}
service.PollingTest = function() {
$timeout(function () {
SystemStatusFactory.PingIP('www.google.com')
.then(function (data) {
service.Data.Ping = data.data;
service.Data.Count++;
}, function (data) {
service.Data.Ping = data.data;
});
service.PollingTest();
}, 2000);
}
return service;
}]);
Controller
app.controller('SystemStatusController', ['$scope', '$rootScope', '$timeout', 'FoundationSystemStatusFactory',
function ($scope, $rootScope, $timeout, FoundationSystemStatusFactory) {
FoundationSystemStatusFactory.PollingTest();
$scope.data = FoundationSystemStatusFactory.Data;
}]);
View
{{data.Ping}}
{{data.Count}}
I am trying to reuse a few bigger functions over 3 controllers in Angular JS. I don't want to pin the functions to my root scope as I want to keep it clear of functions which will be used only 3 times within those 3 controllers.
angular.module('adminModule', ['adminDependency'])
.controller('ctrl1', ['$scope', 'details', function ($scope, details) {
// use functions
}])
.controller('ctrl2', ['$scope', 'details', function ($scope, details) {
// use functions
}])
.controller('ctrl3', ['$scope', 'details', function ($scope, details) {
// use functions
}])
Can you tell me how i can achieve that without writing my functions into the root scope?
Tried it inside a factory but calling AdminModule.toLevelKey() wont work...
.factory('AdminModule',
[ '$resource', 'serviceURL', function ($resource, serviceURL) {
return $resource(serviceURL + 'class/:id', {
id : '#id'
}, {
getClasses : {
method : 'GET',
url : serviceURL + 'extended/class',
isArray : true
},
toLevelKey : function (value) {
var return_key = parseInt(Math.floor(value / 3));
var return_level = value % 3;
return { level : return_level + 1, levelTranslationKey : return_key + 1 };
},
fromLevelKey : function (level, key) {
if (angular.isDefined(level)) {
var value = (key - 1) * 3 + (level - 1);
return value;
} else {
return null;
}
}
}
);
} ]);
This can be done by a service:
.service('myService', function(){
return {
fn: function(){
// do what you want
}
}
});
usage:
.controller('ctrl2', ['$scope', 'details', 'myService',
function ($scope, details, myService) {
// use functions
myService.fn();
}])
In accordance with the above comment of David Fariña: "Are there even more options?".
Except executing, you also can pass data from one controller to another and broadcast event, when it happens.
SharedService:
angular.module("yourAppName", []).factory("mySharedService", function($rootScope){
var mySharedService = {};
mySharedService.values = {};
mySharedService.setValues = function(params){
mySharedService.values = params;
$rootScope.$broadcast('dataPassed');
}
return mySharedService;
});
FirstController:
function FirstCtrl($scope, mySharedService) {
$scope.passDataInSharedSevice = function(params){
mySharedService.setValues(params);
}
}
SecondController:
function SecondController($scope, mySharedService) {
$scope.$on('dataPassed', function () {
$scope.newItems = mySharedService.values;
});
}