I have two angularJS controllers that should be synchronized.
The first is a filter on a list, second displays the list. I have a service user by both controllers and that makes some async ajax-like calls.
My problem is that the filter filters before the list is initialized, so when the page loads for the first time I have unfiltered results. How to solve it?
Here is my JSFiddle
Here is the code:
var myApp = angular.module('myApp', []);
myApp.controller("infoCtrl", function ($scope, $timeout, person) {
person.get().then(function (response) {
// timeout to prevent '$digest already in progress' error
$timeout(function () {
$scope.people = response;
$scope.$apply();
})
});
});
myApp.controller("filterCtrl", function ($scope, person) {
$scope.$watch("maxAge", function (newValue) {
if (newValue) {
person.filterPeople(newValue);
}
});
});
myApp.service("person", function ($q, $timeout) {
var _me = this;
var AjaxGetPeople = function () {
return $timeout(function () {
var somedata = [{name: 'Marcel Sapin',age: 26},
{name: 'Anhel De Niro',age: 42},
{name: 'Johny Resset',age: 30}];
_me.people = somedata;
return somedata;
});
};
var filterPeople = function (maxAge, collection) {
if (!collection) collection = _me.people;
if (!collection) return;
angular.forEach(collection, function (p) {
p.visible = (p.age <= maxAge);
});
};
var get = function () {
if (_me.people) { // return from 'cache'
return $q.resolve(_me.people);
}
// if not 'cached', call 'ajax'
return AjaxGetPeople().then(function (response) {
// add visible property to people
filterPeople(100, response);
_me.people = response;
return response;
});
};
return {
'get': get,
'filterPeople': filterPeople
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="filterCtrl" ng-init="maxAge=30">People younger than
<input ng-model="maxAge" type="number" /> years:
</div>
<hr/>
<div ng-controller="infoCtrl">
<div ng-repeat="person in people" ng-show="person.visible">
{{person.name}}, age {{person.age}}
</div>
</div>
</div>
Anhel De Niro, age 42 should not be displayed when the page is loaded initially, because my filter's max age is 30...
Well, try initialize like this:
var myApp = angular.module('myApp', []);
myApp.controller("infoCtrl", function ($scope, $timeout, person) {
person.get(30).then(function (response) {
// timeout to prevent '$digest already in progress' error
$timeout(function () {
$scope.people = response;
$scope.$apply();
})
});
});
myApp.controller("filterCtrl", function ($scope, person) {
$scope.$watch("maxAge", function (newValue) {
if (newValue) {
person.filterPeople(newValue);
}
});
});
myApp.service("person", function ($q, $timeout) {
var _me = this;
var AjaxGetPeople = function () {
return $timeout(function () {
var somedata = [{name: 'Marcel Sapin',age: 26},
{name: 'Anhel De Niro',age: 42},
{name: 'Johny Resset',age: 30}];
_me.people = somedata;
return somedata;
});
};
var filterPeople = function (maxAge, collection) {
if (!collection) collection = _me.people;
if (!collection) return;
angular.forEach(collection, function (p) {
p.visible = (p.age <= maxAge);
});
};
var get = function (init) {
if (_me.people) { // return from 'cache'
return $q.resolve(_me.people);
}
// if not 'cached', call 'ajax'
return AjaxGetPeople().then(function (response) {
// add visible property to people
filterPeople(init, response);
_me.people = response;
return response;
});
};
return {
'get': get,
'filterPeople': filterPeople
};
});
Its work's in your JSFiddle, hope help you ;D
Following Filter(ageFilter) will filter depending upon maxAge variable
HTML
<div ng-app='myApp' ng-controller="Main" ng-init="maxAge=30">
<input type="text" ng-model="maxAge">
<li ng-repeat="user in users | ageFilter:maxAge">{{user.name}}</li>
</div>
Script
var myApp = angular.module('myApp', []);
myApp.filter('ageFilter', function() {
return function(input, Maxage) {
var out = [];
for (var i = 0; i < input.length; i++){
if(input[i].age <= Maxage)
out.push(input[i]);
}
return out;
};
});
function Main($scope){
$scope.users = [{name: 'Marcel Sapin',age: 26},
{name: 'Anhel De Niro',age: 42},
{name: 'Johny Resset',age: 30}]
}
Related
(function() {
'use strict';
angular
.module('autocompleteCustomTemplateDemo', ['ngMaterial'])
.controller('DemoCtrl', DemoCtrl);
function DemoCtrl($timeout, $q, $log, $scope, $http) {
var self = this;
$scope.service_details = [];
$scope.productdetail == [];
$scope.add = function() {
$scope.show_servicelist = true;
$scope.type = function(e) {
alert(e);
}
$scope.service_details.push(JSON.parse($scope.productdetails));
}
self.simulateQuery = false;
self.isDisabled = false;
self.repos = loadAll();
self.querySearch = querySearch;
self.selectedItemChange = selectedItemChange;
self.searchTextChange = searchTextChange;
function querySearch(query) {
var results = query ? self.repos.filter(createFilterFor(query)) : self.repos,
deferred;
if (self.simulateQuery) {
deferred = $q.defer();
$timeout(function() {
deferred.resolve(results);
}, Math.random() * 1000, false);
return deferred.promise;
} else {
return results;
}
}
function searchTextChange(text) {
$log.info('Text changed to ' + text);
}
function selectedItemChange(item) {
$log.info('Item changed to ' + JSON.stringify(item));
$scope.productdetails = JSON.stringify(item);
}
function loadAll() {
var repos = [{
'product_gid': '1',
'product_name': 'stabilizer',
'forks': '16,175',
}, {
'product_gid': '2',
'product_name': 'stand',
'forks': '760',
}, {
'product_gid': '3',
'product_name': 'ac',
'forks': '1,241',
},
];
return repos.map(function(repo) {
repo.value = repo.product_name.toLowerCase();
return repo;
});
}
function createFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(item) {
return (item.value.indexOf(lowercaseQuery) === 0);
};
}
}
})();
app.service("productservice", function($http) {
this.getproduct = function() {
deggure
var response = $http.get("/Productjson/");
return response;
}
});
I'm displaying angularjs file for listing autocomplete, now i have added new service file to get some data.I want to send the service data into app code, i dont know how to connect service with controller code.when i try to connect i got syntax error. please modify the code and guide me to fetch the service data .
This is full sample to show you how to use service in angularjs with controller:
var app = angular.module("app", []);
app.controller("ctrl", function($scope, service) {
$scope.alert = function() {
service.alert();
}
$scope.console = function() {
service.console();
}
})
app.service("service", function() {
this.alert = function() {
alert("hey")
}
this.console = function() {
console.log("hey")
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<button ng-click="alert()">alert</button>
<button ng-click="console()">console</button>
</div>
This is how you write a service-
I prefer factory:
(function() {
'use strict';
angular
.module('app')
.factory('productservice', productservice);
function productservice($q, $http) {
return {
getproduct : getproduct
};
function getproduct(){
var d = $q.defer();
$http({
method: 'GET',
url: 'url'
}).success(function(response){
d.resolve(response);
}).error(function(response){
d.reject(response);
});
return d.promise;
}
}
})();
Here;s controller
(function() {
'use strict';
angular
.module('app')
.controller('AppController', AppController);
function AppController(
$scope, productservice
) {
function getproduct(){
productservice.getproduct().then(function(response){
//got the response here
}, function(){
});
}
}
})();
I have a function which updates the object, the problem is when I go back from the update form field to the detailed view, it initializes the old object instead of the updated object.
I want to populate the cars list in the CarService instead of the app.js
This is my carService:
window.app.service('CarService', ['HTTPService', '$q',
'$http', function (HTTPService, $q, $http) {
'use strict';
this.cars = [];
this.get = function () {
var deferred = $q.defer();
HTTPService.get('/car').then(function resolve(response) {
deferred.resolve(response.data);
}, function reject(response){
deferred.reject(response);
});
};
this.add = function (formCar) {
var deferred = $q.defer();
console.log("CarService response 1 : ");
$http.post('/#/car', formCar).then(function resolve(response){
deferred.resolve(response.data);
}, function reject(response){
deferred.reject(response);
});
return deferred.promise;
};
this.showDetails = function (carId){
var deferred = $q.defer();
$http.get('/car/view/{{carId}}').then(function resolve(response){
HTTPService.get('/car/view/' + carId).then(function
resolve(response) {
deferred.resolve(response.data);
}, function reject(response){
deferred.reject(response);
});
return deferred.promise;
};
this.put = function (carformUpdate, opleidingsprofielId) {
var deferred = $q.defer();
$http.put('/#/car/:carId/update', carformUpdate).then(function resolve(response){
deferred.resolve(response.data);
}, function reject(response){
deferred.reject(response);
});
return deferred.promise;
};
}]);
This is my updateCar controller:
window.app.controller('updateCarCtrl', ['$scope', '$routeParams',
'CarService', '$location', function ($scope, $routeParams, CarService,
$location) {
'use strict';
$scope.carId = $routeParams.carId;
initCar($scope.carId);
function initCar(carId) {
CarService.showDetails(carId).then(function success(car) {
$scope.car = car;
}, function error(response) {
});
}
$scope.updateCar = function (carId) {
carId = $scope.carId;
if($scope.car !== null){
CarService.put($scope.car, carId).then(function
success(response) {
$scope.car = response;
$location.path('/car/view/' + carId);
alert("Car updated");
}, function error(response) {
$scope.error = response.statusText;
$scope.myform = {};
});
}
};
}]);
This is my carView controller:
window.app.controller('carViewCtrl', ['$scope', '$routeParams', '$location',
'CarService', function ($scope, $routeParams, $location, CarService) {
'use strict';
$scope.carId = $routeParams.carId;
initCar($scope.carId);
function initCar(carId) {
CarService.showDetails(carId).then(function success(car) {
$scope.car = car;
}, function error(response) {
});
}
}]);
My carView initializes the object again when it gets redirected with $location.path('/car/view/' + carId); but as the original object and not the updated object.
I'm trying to do this on an ngMock backend.
My app.js looks like this:
App.js
routing:
.when('/car', {
templateUrl: 'pages/car/car.html'
})
.when('/car/view/:carId', {
templateUrl: 'pages/car/carView.html',
controller: 'carViewCtrl',
controllerAs: 'ctrl'
})
.when('/car/addCar', {
templateUrl: 'pages/car/carAdd.html'
})
.when('/car/:carId/update', {
templateUrl: 'pages/car/carUpdate.html',
controller: 'updateCarCtrl',
conrtollerAs: 'ctrl'
})
app.run: this is where my mock backend is defined
window.app.run(function($httpBackend) {
var cars = [
{
id: 0,
name: ‘car0’,
address: 'adress0',
tel: 'tel0',
email: 'email0'},
{
id: 1,
name: ‘car1’,
address: 'adress1',
tel: 'tel1',
email: 'email1'
}];
var carUrl = “/#/car”;
$httpBackend.whenGET(carUrl).respond(function(method,url,data) {
return [200, cars, {}];
});
$httpBackend.whenGET(/\/#\/car\/view\/(\d+)/, undefined,
['carId']).respond(function(method, url, data, headers, params) {
return [200, cars[Number(params.carId)], {
carId : params.carId
}];
});
$httpBackend.whenPUT('/#/car/:carId/update').respond(function(method, url,
data, carId) {
var car = angular.fromJson(data);
return [200, car, {}];
});
Thanks for any help!
It looks like your update function calls the CarService.put, which in turn calls a HTTPService.put. In your mocked backend you have this:
$httpBackend.whenPUT
-> add new car;
So it always adds a new car, and doesn't update one. This means that when you do the get, you probably get the first car back that matches the given id, which isn't the updated one.
In pseudo code:
// carService.cars = [{id:1,name:"name"}]
var myCar = carService.get(1); // returns {id:1,name:"name"}
myCar.name = "otherName";
carService.put(car); // -> cars.push(car); -> cars = [{id:1,name:"name"},{id:1,name:"otherName"}]
goToDetails(1);
var myCar = carService.get(1); // iterate over the cars, and return the one with id = 1,
// which is {id:1,name:"name"}
I'm new to angularjs. In my webapp I'm trying to work with user contacts as follows.
SERVICE
app.service('Contacts', function ($http,$timeout,$q) {
return {
getData: function() {
var defer = $q.defer();
$http.get('../ListContacts')
.success(function(data) {
defer.resolve(data);
});
return defer.promise;
}
}
});
ContactsController, OtherControllers
$scope.contactsBook = {};
...
Contacts.getData().then(function(data) {
$scope.contactsBook = data;
});
I found the above method somewhere in SO itself. I used it because I don't want to use separate module for Contacts.
I can get data at page load. I can update my contacts at server through ajax posts (from ContactsController). Now I only need a way to update(/refresh) the list automatically in all controllers. How can I achieve that.
I found these three links related but being a newbie I'm unable to figure my way out.
While it is understandable that you may not want to update your current architecture, it may be necessary to adjust your calls slightly if you want to be able to easily share data between controllers via a service.
One flexible approach is to store the data in your service and register watchers in each controller. This allows you to call the service update from one controller (the Contacts controller) and have the change be reflected in all consuming controllers. Note the service is mocked.
You can find the working plunker example here.
Contacts Service:
var app = angular.module('app', []);
app.service('contactsService', function ($http) {
var contacts = [];
return {
loadData: function() {
var mockGet = $q.defer();
var data = [
{ id: 1, name: 'Jack' },
{ id: 2, name: 'Jill' }
];
contacts = data;
mockGet.resolve(contacts);
return mockGet.promise;
},
retrieveNewData: function() {
var mockGet = $q.defer();
var data = [
{ id: 1, name: 'Jack' },
{ id: 2, name: 'Jill' },
{ id: 3, name: 'Bob' },
{ id: 4, name: 'Susan' }
];
contacts = data;
mockGet.resolve(contacts);
return mockGet.promise;
},
getContacts: function () {
return contacts;
}
}
});
Contacts Controller:
app.controller('ContactsCtrl', ['$scope', 'contactsService',
function ($scope, contactsService) {
var vm = this;
vm.contacts = [];
vm.loadData = loadData;
vm.retrieveNewData = retrieveNewData;
$scope.$watch(angular.bind(contactsService, function () {
return contactsService.getContacts();
}), function (newVal) {
vm.contacts = newVal;
});
function loadData() {
contactsService.loadData();
}
function retrieveNewData() {
contactsService.retrieveNewData();
}
}
]);
Other Controller:
app.controller('OtherCtrl', ['$scope', 'contactsService',
function($scope, contactsService) {
var vm = this;
vm.contacts = [];
$scope.$watch(angular.bind(contactsService, function () {
return contactsService.getContacts();
}), function (newVal) {
vm.contacts = newVal;
});
}
]);
You can do
Contacts.getData().then(function(data) {
$scope.contactsBook = data;
$scope.$emit('contacts:updated', data);
});
And then, where you need to notify the controller about the update:
$rootScope.$on('contacts:updated', function(e, contacts) {
$scope.contacts = contacts;
});
Another approach
The service is holding the current contacts list
app.service('Contacts', function ($http,$timeout,$q) {
this.currentList = [];
this.getData = function() {
var defer = $q.defer();
$http.get('../ListContacts')
.success(function(data) {
defer.resolve(data);
});
return defer.promise;
}
});
In your controller:
Contacts.getData().then(function(data) {
$scope.contactsBook = data;
Contacts.currentList = data;
});
In other controller:
controller('AnotherController', function($scope, Contacts) {
$scope.contacts = Contacts.currentList;
});
If you are going to return an object literal you will need to turn your .service() into a .factory() module . In this case I'll be using a service module .
Example
Your service .
app.service('Contacts', function ($http,$timeout,$q) {
var Contacts = this;
contacts.getData = function() {
var defer = $q.defer();
$http.get('../ListContacts')
.success(function(data) {
defer.resolve(data);
});
return defer.promise;
}
}
return Contacts;
});
You will then need to inject this server into your ContactsController .
app.controller('ContactsController', function(Contacts){
$scope.data = null;
$scope.init = function(){
Contacts.getData().then(function(response){
$scope.data = response;
})
}
})
now data can be used in dom
Example
<li ng-repeat="x in data">{{x.name}}</li>
I have a problem with my service in angular.
My service has the next code:
app.service("Utilidades", ['$http', '$window', function ($http, $window) {
return {
Get: function (urlAbsoluta, parametros, callback) {
var Utilidades = this;
$http
.get(app.UrlBase + urlAbsoluta, parametros)
.then(function (data) {
var Datos = angular.fromJson(data);
Utilidades.GuardarToken(Datos.Token);
callback(Datos);
});
},
ObtenerMenu: function () {
var Utilidades = this;
Utilidades.Get("Administracion/Api/Usuarios/Menu", {}, function (Datos) {
Datos = angular.fromJson(Datos.data);
if (Datos.Error == "") {
return Datos.Resultado;
} else {
return "";
}
});
}
}
}]);
Then, in my controller i have the next code:
app.controller('LoginCtrl', ['$scope', '$http', '$location', 'Utilidades',
function Iniciador($scope, $http, $location, Utilidades) {
var Li = this;
Li.Usuario = "";
Li.Contrasena = "";
Li.Error = "";
Li.MenuItems = [];
Li.Menu = function () {
Li. MenuItems = Utilidades.ObtenerMenu();
}
}]
);
When i run this, Li.MenuItems have undefined value and i don't know why.
Your return statements are in a function inside your ObtenerMenu method so the ObtenerMenu method is not actually returning anything. You need to provide a way to access the resulting value:
Service
app.service("Utilidades", ['$http', '$window', function ($http, $window) {
return {
Get: function (urlAbsoluta, parametros) {
var Utilidades = this;
// v------------ return statement here
return $http
.get(app.UrlBase + urlAbsoluta, parametros)
.then(function (data) {
var Datos = angular.fromJson(data);
Utilidades.GuardarToken(Datos.Token);
// v------------ return statement here
return Datos;
});
},
ObtenerMenu: function () {
var Utilidades = this;
// v------------ return statement here
return Utilidades.Get("Administracion/Api/Usuarios/Menu", {})
.then(function (Datos) {
if (Datos.Error == "") {
return Datos.Resultado;
} else {
return "";
}
});
}
};
}]);
In Controller
Li.Menu = function () {
Utilidades.ObtenerMenu()
.then(function (resultado) {
Li. MenuItems = resultado;
});
}
It's because ObtenerMenu function is asynchronous function. This function doesn't return anything initially (so undefined) and later, after some time when ajax request finishes, this function is already finished its execution stack
I am tring to setup a counter where for each country in my list I can keep count of how many clicks there has been plus an overall tally.
I have the below so far which can be viewd in this fiddle. The issue I am having is that I am not able to keep the count unique for each country. How can this be achieved?
<div ng-app="myApp">
<div data-ng-view></div>
</div>
'use strict';
var myApp = angular.module('myApp', ['ngRoute', 'templates/view1.html', 'templates/view2.html']);
myApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'templates/view1.html',
controller: 'CountryListCtrl'
})
.when('/:id', {
templateUrl: 'templates/view2.html',
controller: 'CountryCtrl'
})
}]);
myApp.factory('Countries', ['$q', function ($q) {
var countriesList = [];
// perform the ajax call (this is a mock)
var getCountriesList = function () {
// Mock return json
var contriesListMock = [
{
"id": "0",
"name": "portugal",
"abbrev": "pt"
}, {
"id": "1",
"name": "spain",
"abbrev": "esp"
}, {
"id": "2",
"name": "angola",
"abbrev": "an"
}
];
var deferred = $q.defer();
if (countriesList.length == 0) {
setTimeout(function () {
deferred.resolve(contriesListMock, 200, '');
countriesList = contriesListMock;
}, 1000);
} else {
deferred.resolve(countriesList, 200, '');
}
return deferred.promise;
}
var getCountry = function(id) {
var deferred = $q.defer();
if (countriesList.length == 0) {
getCountriesList().then(
function() {
deferred.resolve(countriesList[id], 200, '');
},
function() {
deferred.reject('failed to load countries', 400, '');
}
);
} else {
deferred.resolve(countriesList[id], 200, '');
}
return deferred.promise;
}
var cnt = 0;
var cntryCnt = 0;
var incCount = function() {
cnt++;
return cnt;
}
var incCntryCount = function(id) {
cntryCnt++;
return cntryCnt;
}
return {
getList: getCountriesList,
getCountry: getCountry,
getCount : function () {
return cnt;
},
getCntryCount : function () {
return cntryCnt;
},
incCount: incCount,
incCntryCount: incCntryCount
};
}]);
myApp.controller('CountryListCtrl', ['$scope', 'Countries', function ($scope, Countries) {
$scope.title = '';
$scope.countries = [];
$scope.status = '';
Countries.getList().then(
function (data, status, headers) { //success
$scope.countries = data;
},
function (data, status, headers) { //error
$scope.status = 'Unable to load data:';
}
);
}]);
myApp.controller('CountryCtrl', ['$scope', '$routeParams', 'Countries', function ($scope, $routeParams, Countries) {
$scope.country = {
id: '',
name: '',
abbrev: ''
};
var id = $routeParams.id;
Countries.getCountry(id).then(
function(data, status, hd) {
console.log(data);
$scope.country = data;
$scope.countOverall = Countries.getCount;
$scope.countCntry = Countries.getCntryCount;
$scope.clickCnt = function () {
$scope.countTotal = Countries.incCount();
$scope.country.clicks = Countries.incCntryCount(id);
console.log($scope);
};
},
function(data, status, hd) {
console.log(data);
}
);
}]);
angular.module('templates/view1.html', []).run(["$templateCache", function ($templateCache) {
var tpl = '<h1>{{ title }}</h1><ul><li ng-repeat="country in countries"><a href="#{{country.id}}">{{country.name}}</div></li></ul>';
$templateCache.put('templates/view1.html', tpl);
}]);
angular.module('templates/view2.html', []).run(["$templateCache", function ($templateCache) {
var tpl = '<div>{{country.name}} clicks {{countCntry()}} <br> overall clicks {{countOverall()}}</div><button>BACK</button><button ng-click="clickCnt()" >count clicks ++ </button>';
$templateCache.put('templates/view2.html', tpl);
}]);
The problem is that you are not incrementing a count based on the country. Working on the fiddle right now.
EDIT:
I've updated the fiddle: http://jsfiddle.net/1xtc0zhu/2/
What I basically did was making the cntryCnt an object literal which takes the country id as a property and keeps the right counting per each id, like so:'
var cnt = 0;
var cntryCnt = {};
...
// The function now receives the country id and increments the specific country clicks only.
var incCntryCount = function(id) {
cntryCnt[id] = cntryCnt[id] || 0;
cntryCnt[id]++;
return cntryCnt[id];
}
The rest of the changes are in the templates, and are basically only sending the country id as a param when getting or incrementing the counts.
Also, this is not an Angular Specific question, but more a programming in general question.