I have a todo list in AngularJS that looks like this
.controller('TodoCtrl', function ($scope) {
$scope.todos = [
{text:'Ask smth on Stackoverflow', done:false},
{text: 'Resolve this', done:false}
];
$scope.getTotalTodos = function () {
return $scope.todos.length;
};
$scope.addTodo = function () {
$scope.todos.push({text:$scope.formTodoText, done:false});
$scope.formTodoText = '';
};
$scope.clearCompleted = function () {
$scope.todos = _.filter($scope.todos, function(todo){
return !todo.done;
});
};
})
And I would like to add a Todo (with a text, and a boolean 'done') from another controller that is launched when I click a button.
How can I do that ?
A big THANKS to who will help me
Typically services are used to pass information back and forth. Create a service and store your TODO list inside there. Inject that service into both controllers. Each controller can now act on the items in the list
I will append Scotts answer with some shorted Code.
Like he said, the best is to use a Service ;)
The Service:
.factory('todoService', function() {
var todolist = [];
return {
getTodoList: function() {
return todolist;
}
addTodo: function(todo) {
todolist.push(todo);
},
getTotalTodos: function() {
return todolist.length;
},
// some other
}
});
Now you can inject the service into any controller via
.controller('TodoCtrl', function ($scope, todoService)
and then you can call the functions of the service in the controller, e.g.
$scope.addTodo = function () {
todoService.addTodo({text:$scope.formTodoText, done:false});
$scope.formTodoText = '';
};
By using Angular Services:
I've made a simple demo.
Hope this helps.
(function() {
var app = angular.module("myApp", []);
// myService service.- This service contains an array, called «todos» wich contains data.
app.service("myService", function() {
return {
todos: [{
"text": "Ask smth on Stackoverflow",
"done": false
}, {
"text": "Resolve this",
"done": false
}]
};
});
// Add the dependecy in the controller.
app.controller("Controller", ["$scope", "myService",
function($scope, myService) {
$scope.title = "TODOS";
// This function returns the data stored in the service.
$scope.getTodos = function() {
return myService.todos;
}();
$scope.getTotalTodos = function() {
return myService.todos.length;
};
// This functions contains the object with the values from the form.
$scope.addTodo = function(model) {
myService.todos.push({
text: model.text,
done: model.done
});
$scope.model.text = "";
};
}
]);
})();
<html data-ng-app="myApp">
<head>
<meta charset="utf-8" />
<title>Demo</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body data-ng-controller="Controller">
<h3>{{title}}</h3>
<form id="myForm" ng-submit="addTodo(model)">
<label>
Todo
<input type="text" data-ng-model="model.text" />
</label>
<br />
<label>
Done
<input type="checkbox" data-ng-model="model.done" />
</label>
<br/>
<button type="submit">Add</button>
<hr />
<ul>
<li data-ng-repeat="todo in getTodos">
{{todo.text}} ({{todo.done}})
<input type="checkbox" data-ng-model="todo.done" />
</li>
</ul>
</form>
</body>
</html>
Update: Using the service in multiple controllers.
(function() {
var example = angular.module("starter", [])
example.service("todoService", function() {
return {
todos: [],
addTodo: function($text, $classe) {
this.todos.push({
text: $text,
done: false,
});
}
};
});
example.controller('nationsLeaguesCtrl', function($scope, todoService) {
$scope.randomNationsLeagues = function() {
var text = "Text 1";
todoService.addTodo(text, null);
};
})
example.controller('statsCtrl', function($scope, todoService) {
$scope.randomStats = function() {
var text = "Text 2";
todoService.addTodo(text, null);
};
})
example.controller('specialCtrl', function($scope, todoService) {
$scope.randomSpecial = function() {
var text = "Text 3";
todoService.addTodo(text, null);
};
})
example.controller('TodoCtrl', function($scope, todoService) {
$scope.getTodos = function() {
return todoService.todos;
}();
$scope.getTotalTodos = function() {
return todoService.todos.length;
};
$scope.clearCompleted = function() {
$scope.todos = _.filter($scope.todos, function(todo) {
return !todo.done;
})
};
});
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="starter">
<button class="button button-full button-light" ng-controller="nationsLeaguesCtrl" ng-click="randomNationsLeagues()">Nations & Leagues</button>
<button class="button button-full button-light" ng-controller="statsCtrl" ng-click="randomStats()">Stats</button>
<button class="button button-full button-light" ng-controller="specialCtrl" ng-click="randomSpecial()">Special</button>
<div ng-controller="TodoCtrl">
<ul>
<li ng-repeat="todo in getTodos">{{todo.text}}
<input type="checkbox" name="checkboxG1" id="checkboxG1" ng-model="todo.done" class="css-checkbox" />
<label for="checkboxG1" class="css-label" style="font-family:checkboxFont; color:#ffffff;"><span class="done-{{todo.done}}"></span>
</label>
</li>
</ul>
</div>
</body>
Related
This question already has answers here:
Why are Callbacks from Promise `.then` Methods an Anti-Pattern
(2 answers)
Closed 5 years ago.
I am adding a simple todo list to my ionic vs1/angularjs project.
I want to use the ionic modal to launch this list. Since I need to launch it from everywhere I looked for a way to do this.
I found this article https://medium.com/#saniyusuf/create-an-isolate-modal-with-ionic-v1-b4cf73f05727 which had me create a factory that could be launched from any controller. Love it!
Problem is, it's a todo list where users need to be able to add/delete.
I want to save this information to firebase so I created a factory to hold all the firebase crud operations for the ToDo list.
So I now have a factory that launches the modal and one that has the firebase crud operations.
In the modal launcher I figured out how to include the crud operations so that the buttons would interact. Problem is how do I get the value of the input.
Modal Launcher
angular.module('formulaWizard')
.factory('IsolateModal', IsolateModal)
IsolateModal.$inject = ['$rootScope', '$ionicModal', '__ToDo',
'$window'];
function IsolateModal($rootScope, $ionicModal, __ToDo, $window) {
var modalsIsolateScope = $rootScope.$new();
var currentUser = $rootScope.currentUser || JSON.parse($window.sessionStorage.getItem('payload'));
var isolateModal = {
open: open
};
function open() {
$ionicModal.fromTemplateUrl(
'app/to-do/toDo.html',
{
scope: modalsIsolateScope
}
)
.then(function (modalInstance) {
modalsIsolateScope.close = function () {
closeAndRemove(modalInstance);
},
modalsIsolateScope.add = function(){
addTodo(modalInstance);
};
return modalInstance.show();
});
}
function closeAndRemove(modalInstance) {
return modalInstance.hide()
.then(function () {
return modalInstance.remove();
});
}
function addTodo(modalInstance) {
var i = modalInstance
__ToDo.toDo($scope.input)
.then(function(result) {
$scope.input = {};
// Reload our todos, not super cool
getAllTodos();
});
}
return isolateModal;
}
My Data Factory
angular.module('formulaWizard')
.factory('__ToDo', function(__Refs, $q) {
return {
toDo: function(id, userObject, cb) {
var toDo = __Refs.userNotes.child(id).push(userObject);
__Refs.userNotes.child(id).child(newToDo.key()).update({ toDo_id:
newToDo.key() }).then(function(response) {
cb(response);
}, function(e) {
console.error('error occured');
});;
},
updateToDo: function(userId, toDoId, userObject, cb) {
var x =
__Refs.userNotes.child(userId).child(toDoId).update(userObject)
.then(function(response) {
cb(response);
}, function(e) {
console.error('Error editing');
});
},
deleteToDo: function(userId, toDoId, cb) {
__Refs.userNotes.child(userId).child(toDoId).remove(function(error) {
if (error) {
console.error(error);
} else {
cb();
}
});
},
getuserNotes: function(id, cb) {
__Refs.userNotes.child(id).on('value', function(response) {
cb(response.val());
});
}
}
});
Call From Controller
$scope.openModal = function () {
IsolateModal.open();
};
Html
<ion-modal-view>
<ion-header-bar class="bar-stable">
<h1 class="title">My List</h1>
<button class="button icon ion-close-round button-icon"
ng-click="close()"></button>
</ion-header-bar>
<ion-content>
<div class="list card" style="width:100%; margin-left:0;">
<div class="item item-body">
<div class="item-input-inset">
<label class="item-input-wrapper">
<input id="newInput" type="text" placeholder="New Todo"
ng-model="input.name">
</label>
<button class="button button-balanced button-small" ng-click="add()">
Add Todo
</button>
</div>
<ion-list can-swipe="true">
<ion-item ng-repeat="item in todos">
{{item.name}}
<ion-option-button class="button-assertive" ng-click="deleteTodo(item.id)">
Delete
</ion-option-button>
</ion-item>
</ion-list>
</div>
</div>
</ion-content>
</ion-modal-view>
How can I get the input value from the html page into the factory?
Thanks
how do I get the value of the input?
Return the promise:
function open() {
̲r̲e̲t̲u̲r̲n̲ $ionicModal.fromTemplateUrl(
'app/to-do/toDo.html',
{
scope: modalsIsolateScope
}
)
.then(function (modalInstance) {
modalsIsolateScope.close = function () {
closeAndRemove(modalInstance);
},
modalsIsolateScope.add = function(){
addTodo(modalInstance);
};
return modalInstance.show();
});
}
Then extract the returned value from the promise:
$scope.openModal = function () {
var promise = IsolateModal.open();
promise.then(function(value) {
console.log(value);
});
return promise;
};
Why after invoking English() or France() functions value of $scope.currentLanguageId in myFilter does not change?
Is it normal way to make different localization of site by filters? Or existing may be more common way?
List:
<div ng-init="GetList()">
<div ng-repeat="item in items | filter: myFilter">
{{item.Text}} {{item.LanguageId}}
</div>
</div>
Menu:
<div class="menu">
<ul>
<li>About</li>
<li>Contacts</li>
<li><a>Test</a></li>
<li><a>Test</a></li>
<li><a>Test</a></li>
<li><input type="button" value="En" ng-controller="homeController" ng-click="English()" /></li>
<!--<li><a>En</a></li>
<li><a>Fr</a></li>-->
</ul>
</div>
Controller:
app.controller('homeController', function ($scope, $http) {
$scope.items = [];
$scope.currentLanguageId = '';
$scope.GetList = function () {
$http({
method: 'GET',
url: '/Home/GetList',
params: { languageId: '1'}
}).then(function successCallback(response) {
$.each(response.data, function (id,item) {
$scope.items.push({ Text: item.Text, LanguageId: item.LanguageId });
});
}, function errorCallback(response) {
alert('Error');
});
}
$scope.English = function () {
$scope.currentLanguageId = '2';
}
$scope.France = function () {
$scope.currentLanguageId = '3';
}
$scope.myFilter = function (item) {
console.log($scope.currentLanguageId);
return item.LanguageId == $scope.currentLanguageId;
};
});
DEMO
I would create a service for that, and attach it to the $rootScope so it is available everywhere in my application and does not need to be injected in each controller
var app = angular.module('app', []);
app.run(function(langService){
langService.fetch('spanish');
});
app.controller('MainController', function(){
var vm = this;
});
app.service('langService', function($rootScope){
this.current = {};
this.fetch = function(lang){
//do your fetching here with $http
$rootScope.lang = {
ok: 'si',
yes: 'si',
no: 'no'
};
};
});
then you can use it anywhere in your app like
<button>{{$root.lang.ok}}</button>
<button>{{$root.lang.no}}</button>
Other things worth pointing out:
Your controller is too fat, you should not put logic on your controller, logic should be in services
Avoid using ng-init as much as possible, do it inside the controller
I am having difficulties combining both the editing of a current contact and the creation of a new contact. I am able to edit the contact that I click on, however, when I try and click on add new address the following error message -
angular.js:13424 Error: [$injector:unpr] http://errors.angularjs.org/1.5.3/$injector/unpr?p0=editIndexProvider%20%3C-%20editIndex%20%3C-%20ModalCtrl
This is due to using resolve based on the answer here
Factory
'use strict';
var app = angular.module('testApp', ['ngRoute','ui.bootstrap']);
app.factory('addressFactory', function(){
var addressFactory = {};
addressFactory.addressBook = [];
addressFactory.saveAddress = function(name, address){
addressFactory.addressBook.push({
name: name,
address: address
});
};
addressFactory.updateAddress = function(name, address, index){
addressFactory.addressBook[index] = {
name: name,
address: address
};
};
return addressFactory;
})
CONTROLLERS
.app.controller('testCtrl', ['$uibModal', 'addressFactory', function ($uibModal, addressFactory) {
var testCtrl = this;
this.addressBook = addressFactory.addressBook;
this.open = function () {
touchnoteCtrl.modalInstance = $uibModal.open({
templateUrl: 'app/views/partials/add-address.html',
controller: 'ModalCtrl',
controllerAs:'ctrl'
});
}
this.openEdit = function(index) {
touchnoteCtrl.modalInstance = $uibModal.open({
templateUrl: 'app/views/partials/edit-address.html',
controller: 'ModalCtrl',
controllerAs:'ctrl',
resolve: {
editIndex: function () {
return index;
}
}
});
}
}])
.controller('ModalCtrl', ['$uibModalInstance','addressFactory','editIndex', function ($uibModalInstance,addressFactory, editIndex) {
this.addressBook = addressFactory.addressBook;
this.saveAddress = function( name, address) {
addressFactory.saveAddress( name, address);
$uibModalInstance.dismiss('cancel');
}
this.getIndex = editIndex;
console.log(this.getIndex)
this.updateAddress = function(name, address, index) {
addressFactory.updateAddress( name, address, index);
}
this.cancelAddress = function () {
$uibModalInstance.dismiss('cancel');
};
}]);
HTML
home.html
<a ng-click="ctrl.open()">Enter new address</a>
<div ng-repeat="contact in ctrl.addressBook track by $index">
<p class="contactName">{{contact.name}}</p>
<p class="contactAddress">{{contact.address}}</p>
<button ng-click="ctrl.openEdit($index)">Edit Contact</button>
</div>
form.html
<form>
<input ng-model="ctrl.name" type="text">
<input ng-model="ctrl.address" type="text">
</form>
edit-address.html
<h3>Edit address</h3>
<div ng-include="'app/views/partials/form.html'"></div>
<div>
<button ng-click="ctrl.cancelAddress()">Cancel</button>
<button ng-click="ctrl.updateAddress(ctrl.name, ctrl.address, ctrl.getIndex)">Save</button>
</div>
add-address.html
<h3>Add address</h3>
<div ng-include="'app/views/partials/form.html'"></div>
<div>
<button ng-click="ctrl.cancelAddress()">Cancel</button>
<button ng-click="ctrl.addAddress(ctrl.name, ctrl.address)">Save</button>
</div>
You use the same ModalCtrl for both the open and the openEdit function. The open function does not have a resolve parameter. This way, angular cannot resolve the injected editIndex. Try this:
this.open = function () {
touchnoteCtrl.modalInstance = $uibModal.open({
templateUrl: 'app/views/partials/add-address.html',
controller: 'ModalCtrl',
controllerAs:'ctrl',
resolve: {
editIndex: function () {
return undefined;
}
}
});
}
I think you simply miss a module dependency to angular.ui.bootstrap...
I'm working on some mega simple weather app in Angular for practice reasons and i'm stuck..
i have a angular json feed like this:
app.factory('forecast', ['$http', function($http) {
return $http.get('http://api.openweathermap.org/data/2.5/weather?q=Amsterdam,NL&lang=NL_nl&units=metric')
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
}]);
and it loads the feed in to the index.html. its all working and what i wand now is a input form on index that changes the Amsterdam part of the url on js/services/forcast.js where the above code is to another city so people can see their city weather.
See the demo here: http://dev.bigstoef.nl/workspace/shiva/weer/index.html
Ive tryd about all posable options about now and i'm 3 days further and its a no go. What is there correct way here?
First, create a "configurable" service :
app.factory('forecast', ['$http', function($http) {
var city;
var cities = {
amsterdam: 'Amsterdam,NL',
paris: 'Paris,FR'
};
var api_base_url = 'http://api.openweathermap.org/data/2.5/weather';
var other_params = 'lang=NL_nl&units=metric';
return {
setCity: function(cityName){
city = cityName ;
console.log(city);
},
getWeather: function(cityName){
console.log(city);
if(cityName) this.setCity(cityName);
if (!city) throw new Error('City is not defined');
return $http.get(getURI());
}
}
function getURI(){
return api_base_url + '?' + cities[city] + '&' + other_params;
}
}]);
Then you can create a controller like the following:
app.controller('forecastCtrl', ['$scope', 'forecast',function($scope,forecast){
$scope.city = 'amsterdam' ;
$scope.$watch('city',function(){
console.log($scope.city);
forecast.setCity($scope.city);
});
$scope.getWeather = function(){
console.log('get weather');
forecast.getWeather()
.success(function(data){
console.log('success',data);
$scope.weatherData = data;
}).error(function(err){
console.log('error',err);
$scope.weatherError = err;
});
};
}]);
To implement a template as the following
<link rel="stylesheet" href="style.css" />
<div data-ng-controller="forecastCtrl">
<form>
<label>
<input type="radio" name="city" data-ng-model="city" data-ng-value="'amsterdam'">Amsterdam
</label>
<br/>
<label>
<input type="radio" name="city" data-ng-model="city" data-ng-value="'paris'">Paris
</label>
<br/>
<button data-ng-click="getWeather()">Get Weather</button>
</form>
<p class="weather-data">
{{weatherData}}
</p>
<p class="weather-error">
{{weatherError}}
</p>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="script.js"></script>
You can view the code working here : http://plnkr.co/edit/rN14M8GGX62J8JDUIOl8?p=preview
You can return a function in your factory. Define your forcast as
app.factory('forecast', ['$http', function($http) {
return {
query: function(city) {
return $http.get('http://api.openweathermap.org/data/2.5/weather?q=' + city + '&lang=NL_nl&units=metric')
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
}
};
}]);
Then in your controller
forecast.query('Amsterdam,NL').success(function(data) {
$scope.weer = data;
});
Change service code to have a dedicated method which you can call multiple times with different parameters (cities):
app.factory('forecast', ['$http', function($http) {
return {
load: function(location) {
return $http.get('http://api.openweathermap.org/data/2.5/weather?q=' + location + '&lang=NL_nl&units=metric')
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
}
}
}]);
Then in controller you would be able to load forecat for other locations when you need:
forecast.load('Amsterdam,NL').then(function(data) {
$scope. weer = data;
});
Demo: http://plnkr.co/edit/GCx35VxRoko314jJ3M7r?p=preview
I have a three functions in each there is $scope.fetch() which is being called when ng-submit=fetch is pressed. I want one ng-submit button which would call all three fetch() in three functions how can I do this?
HTML:
<div type="text/ng-template" id="getnewcoolios.html" class="users">
<h1>{{message}}</h1>
<form name="myform" id="myform1" ng-submit="fetch()" >
<input type="date"
ng-model="date"
value="{{ 'date' | date: 'dd/MM/yyyy' }}" />
<div><center><button type="submit" >Fetch</button></center></div>
</form>
{{formdata.date}}
<ul ng-controller="NewCooliosCtrl" ng-repeat="newCoolio in newCoolios.newCoolios">
<li>{{newCoolio.personID}}, {{newCoolio.placeID}}, {{newCoolio.datePlaced}}</li>
</ul>
<ul ng-controller="NewPlacesCtrl" ng-repeat="newPlace in newPlaces.newPlaces">
<li>{{newPlace}} </li>
</ul>
<ul ng-controller="NewUsersCtrl" ng-repeat="newUser in newUsers.newUsers">
<li>New Users: {{newUser}} </li>
</ul>
</div>
Angular:
myApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider
.when('/getdailydata', {
templateUrl: 'templates/getnewcoolios.html',
controller: 'DailyCtrl'
})
}])
.controller('DailyCtrl', function($scope) {
})
.controller('NewUsersCtrl', function($scope,$http,$filter) {
$scope.fetch= function(){
var formdata =
{'date' : $filter('date')(this.date, 'dd/MM/yyyy')
};
var inserturl = 'http://94.125.132.253:8001/getnewusers?date=' + formdata.date;
$http.get(inserturl).success(function (data) {
console.log(formdata);
$scope.newUsers = data;
console.log(inserturl);
console.log(data);
$scope.message = 'List of New Users';
})}
})
.controller('NewPlacesCtrl', function($scope,$http,$filter) {
$scope.fetch= function(){
var formdata =
{'date' : $filter('date')(this.date, 'dd/MM/yyyy')
};
var inserturl = 'http://94.125.132.253:8001/getnewplaces?date=' + formdata.date;
$http.get(inserturl).success(function (data) {
console.log(formdata);
$scope.newPlaces = data;
console.log(inserturl);
console.log(data);
$scope.message = 'List of New places';
}
)
}
})
.controller('NewCooliosCtrl', function($scope,$http,$filter) {
$scope.fetch= function(){
var formdata =
{'date' : $filter('date')(this.date, 'dd/MM/yyyy')
};
var inserturl = 'http://94.125.132.253:8001/getnewcoolios?date=' + formdata.date;
$http.get(inserturl).success(function (data) {
console.log(formdata);
$scope.newCoolios = data;
console.log(inserturl);
console.log(data);
$scope.message = 'List of New Coolios';
}
)}});
You can access outer scope properties in inner scopes, so you can define each fetch function in inner scopes and push them into array of functions to be called on submit. Then, on submit^ just iterate through that array and call each function. Voila.
<div ng-controller="fetcher">
<form name="myForm" ng-submit="fetch"></form>
<ul ng-controller="NewCooliosCtrl"></ul>
<ul ng-controller="NewPlacesCtrl"></ul>
<ul ng-controller="NewUsersCtrl"></ul>
<div>
.controller('fetcher', function($scope){
$scope.toFetch = [];
$scope.fetch = function(){
for(var i=0; i<$scope.toFetch.length; i++){
$scope.toFetch[i]();
}
}
});
.controller('NewCooliosCtrl', function($scope){
$scope.fetch= function(){...};
$scope.toFetch.push($scope.fetch);
})
.controller('NewPlacesCtrl', function($scope){
$scope.fetch= function(){...};
$scope.toFetch.push($scope.fetch);
})
.controller('NewUsersCtrl', function($scope){
$scope.fetch= function(){...};
$scope.toFetch.push($scope.fetch);
})