Why $scope is not working on the view? - javascript

I got the next controller:
.controller('LogInController', function(logInFactory, $scope, $location, $state){
$scope.logIn = function() {
$scope.dataLoading = true;
logInFactory.logIn($scope.email, $scope.password, function (response) {
if (response.success) {
$scope.userName = response.userName;
console.log('userName', $scope.userName);
logInFactory.setCredentials($scope.email, $scope.password);
$location.path('/users');
} else {
$scope.dataLoading = false;
}
});
};
$scope.clearCredentials = function(){
$state.go('login');
logInFactory.clearCredentials();
};
});//End controller
I want to use it in this view:
<div class="header" ng-controller = 'LogInController'>
<img src= "logo.jpg">
{{userName}}
<button ng-click = 'clearCredentials()'> Cerrar sesión</button>
</div>
But userName is not showing in the view but when I print it on the controller it is displayed correctly. That view is displayed after call the logIn() function.
This is the logIn function in my factory:
var logIn = function(email, password, callback){
var URL;
if(ENV.mocksEnable){
URL = ENV.apiMock + ENV.logInMock;
return (
$timeout(function () {
var response;
getUser()
.then(function (user) {
console.log('USER', user);
if (user !== null) {
response = { success: true, userName: user.userName};
} else {
response = { success: false, message: 'Username or password is incorrect' };
}
callback(response);
});
}, 1000)
);
}else{
URL = ENV.apiURL + ENV.logIn;
return (
$http.post(URL, {email : email, password : password})
.then(function onFulfilled(response){
var data = response.data;
userName = data.username;
userEmail = data.email;
userId = data.id;
profiles = data.profiles;
callback(response);
return data;
})
.catch(function onRejected(errorResponse){
console.log('Error in logInFactory');
console.log('Status: ', errorResponse.status);
callback(errorResponse);
return errorResponse;
})
);
}
};//End login
I trigger the logIn() function in this view
<form ng-submit = 'logIn()'>
<h1>Log In</h1>
Correo electrónico:
<input type="email" ng-model='email' required><br>
Contraseña
<input type="password" ng-model='password' required><br>
<input type="submit" value="Log in">
</form>
When I tigger logIn() I should go to that header and show the userName.

why are you triggering clearCredentials() ? whereas according to this code you should triggering login() instead.

The result of your logIn() function may be out of Angular scope.
Try wrapping the result of the logIn function into a $timeout (which calls $apply, a way to force Angular to refresh a controller scope):
$timeout(function() {
if (response.success) {
$scope.userName = response.userName;
console.log('userName', $scope.userName);
logInFactory.setCredentials($scope.email, $scope.password);
$location.path('/users');
} else {
$scope.dataLoading = false;
}
});
Do not forget to inject the dependency $timeout in your controller.

You have $scope.userName inside the success of the logIn method. It won't be available until that has happened.
If you put $scope.userName outside of the method and set it to something, it would appear.
.controller('LogInController', function(logInFactory, $scope, $location, $state) {
$scope.userName = 'test name';
$scope.logIn = function() { ...
Something like that.

we don't have your factory code but this line is very strange to me :
logInFactory.logIn($scope.email, $scope.password, function (response) { ..; } )
so your passing the fonction to the factory and it is not the factory who returning data to the controller.
it should be something like this :
logInFactory.logIn($scope.email, $scope.password).then(function (response) { ..; } );
EDIT :
You have to remove the callback function from your factory and make the factory return data and handle data like this logInFactory.logIn($scope.email, $scope.password).then(function (response) { ..; } );.
You have log in the console but the $scope is not shared between the factory and controller so the callback in your factory edit the $scope.userName but the controller cannot get this change.

My problem was that I was expecting to get data from a controller to two different views. And when I go from LogIn view to my header view, the controller refresh its data. So, I have to create in my factory:
var getUserName = function() {
return userName;
};
And in the controller
$scope.userName = logInFactory.getUserName();
Now my userName persists in the factory.

Related

I am unable to access an asp.net mvc controller to return me a JsonResult using $resource of angular

What am I missing ? I am new to Angularjs. Trying angularjs with asp.net mvc. I am unable to access an asp.net mvc controller to return me a JsonResult using $resource of angular.
However, I get success otherwise using $.getJson of javascript but not using angularjs. What am I missing ? please guide. Thank you for replying any.
Following is my Service
EbsMvcApp.factory('classListService', function ($resource, $q)
{
var resource = $resource
(
'/Home/ClassList'
, {}
//{ method: 'Get', q: '*' }, // Query parameters
, { 'query': { method: 'GET' , isArray:false } }
);
function get($q)
{
console.log('Service: classListServic > Started');
var Defered = $q.defer();
resource.get
(
function (dataCb)
{
console.log('success in http service call');
Defered.resolve(dataCb);
}
, function (dataCb)
{
console.log('error in http service')
Defered.reject(dataCb);
}
);
return Defered.promise; // if missed, would throw an error on func: then.
};
return { get: get };
});
angular Controller:
var EbsMvcApp = angular.module('myApp', ['ngResource']);
//'classListService',
EbsMvcApp.controller
(
'myAppController',
['$scope','classListService','$q' , function ($scope, classListService, $q)
{
console.log('controller myAppController started');
var classList = classListService.get($q);
classList = classList.then(
function ()
{
(
function (response)
{
console.log('class list function response requested');
return response.data;
}
);
}
);
console.log(classList.ClassName);
console.log(classList);
console.log('end part of ctrl');
$scope.classList = classList;
$scope.SelectedClassID = 0;
$scope.message = ' message from Controller ';
}
]
);
Asp.net MVC Controller
namespace EBS_MVC.Controllers
{
public class HomeController : BaseController
{
ApplicationDbContext db = new ApplicationDbContext();
public JsonResult ClassList()
{
var List = new SelectList(db.tblClass, "ID", "ClassName");
return Json(List, JsonRequestBehavior.AllowGet);
}
}
}
Brower's response (F12):
ControllerTry1.js:11 controller myAppController started
serviceGetClassList.js:16 Service: classListServic > Started
ControllerTry1.js:28 undefined
ControllerTry1.js:29 c
ControllerTry1.js:31 end part of ctrl
angular.js:12520 Error: [$resource:badcfg]
[Browers response: screen shot][1]
Oky, finally, I got a solution using the $http service. from here
http://www.infragistics.com/community/blogs/dhananjay_kumar/archive/2015/05/13/how-to-use-angularjs-in-asp-net-mvc-and-entity-framework-4.aspx
in csHtml file, a reference to the service.js and Controler.js is required.
I am not sure if I have added it earlier or later now. but its required.
ng-Controller:
EbsMvcApp.controller('ClassListController', function ($scope, ClassListService2) {
console.log('ClassListController Started');
GetClassList();
function GetClassList()
{
ClassListService2.GetJson()
.success(function (dataCallBack) {
$scope.classList = dataCallBack;
console.log($scope.classList);
})
.error(function (error) {
$scope.status = 'Unable to load data: ' + error.message;
console.log($scope.status);
});
}
});
ng-Service:
EbsMvcApp.factory('ClassListService2', ['$http', function ($http) {
console.log('ClassListService2 Started');
var list = {};
list.GetJson = function () {
return $http.get('/Home/ClassList');
};
return list;
}]);
csHtml View:
<div class="text-info" ng-controller="ClassListController">
<h3> Text from Controller: </h3>
#*table*#
<table class="table table-striped table-bordered">
<thead>
<tr><th>DisplayName</th><th>Value</th>
</thead>
<tbody>
<tr ng-hide="classList.length">
<td colspan="3" class="text-center">No Data</td>
</tr>
<tr ng-repeat="item in classList">
<td>{{item.Text}}</td>
<td>{{item.Value}}</td>
</tr>
</tbody>
</table>
Sorry for the delay, I just wrote up some code to quickly test the ngResource module as I haven't used it yet.
I've got the code working to do what you want using the ngResource module. I think part of the problem was that you was configuring the query method but calling the get method so your configurations was not applied.
Here is the service class that I wrote to test against a controller the same as yours.
(function () {
'use strict';
angular
.module('myApp')
.service('classService', ClassService);
ClassService.$inject = ['$resource', '$q'];
function ClassService($resource, $q) {
var resource = $resource
(
'/Home/ClassList',
{},
{
'get': { method: 'GET', isArray: true },
'query': { method: 'GET', isArray: true }
}
);
var service = {
get: get
};
return service;
////////////
function get() {
var Defered = $q.defer();
resource.get(function (dataCb) {
console.log('success in http service call');
Defered.resolve(dataCb);
}, function (dataCb) {
console.log('error in http service')
Defered.reject(dataCb);
});
return Defered.promise;
};
};
})();
The controller looks like this
(function () {
'use strict';
angular
.module('myApp')
.controller('classController', ClassController);
ClassController.$inject = ['$scope', 'classService'];
function ClassController($scope, classService) {
var vm = this;
vm.data = null;
activate();
/////////////
function activate() {
var classList = classService.get().then(function (response) {
console.log('class list function response requested');
vm.data = response;
console.log(vm.data);
});
console.log('end part of ctrl');
$scope.SelectedClassID = 0;
$scope.message = ' message from Controller ';
};
};
})();
I've included some of your original code just so you can see how it would fit in.
Glad to see you have got it working though!

angularjs $http.post with parameters

I'm new at angular and if my question is kinda low lvl dont be angry with me.
I have web service which returns sessionId and login success message if user will pass auth. for example url is that:
http://localhost:8181/login?username=USERNAME&password=12345
and here's my response:
{"sessionId":"0997cec2a8b34c84ba8419ab1204e6aa","loginSucceeded":true}
here's my login controller:
app.controller('loginCtrl', function($scope, loginService){
$scope.login=function(user){
loginService.login(user);
}
});
and here's my service:
app.factory('loginService', function($http){
return{
login: function(user){
var $promise=$http.post('http://localhost:8181/login?', user);
$promise.then(function(msg){
if(msg.loginSucceeded=="true")
console.log("opa")
else
console.log("den");
});
}
}
});
and I have user.username and user.password in my scope (using textboxes).
How can I pass those parameters in url and how can I parse that response?
In your code you're passing the username and password in the URL of the POST request. If that's what you really want (it's more common to pass them as POST data) than you can use this:
login: function(user){
var url = 'http://localhost:8181/login?username=' + user.name + '&password=' + user.password;
$http.post(url).then(function(msg){
if(msg.loginSucceeded==="true"){
console.log("opa")
}else{
console.log("den");
}
});
}
If you want to pass the data as POST data, you can pass that as the second argument in the $http.post() call:
login: function(user){
var url = 'http://localhost:8181/login';
var data = {username: user.name, password: user.password};
$http.post(url, data).then(function(msg){
if(msg.loginSucceeded==="true"){
console.log("opa")
}else{
console.log("den");
}
});
};
I never seen anyone passing login data via query string,
if you are in simple http protocol... you should consider using Basic Access Authentication or oAuth...
by the way, if you need to do what described above... this could be help you!
angular
.module('test', [])
.service('LoginService', function($q, $http) {
var self = this;
self.login = function(username, password) {
var configs = { cache: false };
var payload = {
"username" : username,
"password" : password
};
// The Method Post is generally used with a payload, but if you need to pass it as query string... you have to do:
configs.params = payload;
return $http
.post('/api/login', null /* but normally payload */, configs)
.then(function(result) {
console.log('LoginService.login:success', result);
return result.data;
})
.catch(function(error) {
console.log('LoginService.login:error', error);
return $q.reject(error);
});
;
};
})
.controller('LoginCtrl', function(LoginService, $scope) {
var vm = $scope
vm.username = 'hitmands';
vm.password = 'helloWorld';
vm.debug = 'CIAO';
vm.onFormSubmit = function(event, form) {
if(form.$invalid) {
event.preventDefault();
return;
}
vm.debug = null;
return LoginService
.login(vm.username, vm.password)
.then(function(data) { vm.debug = data; })
.catch(function(error) { vm.debug = error; })
;
};
})
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<article ng-app="test">
<div ng-controller="LoginCtrl">
<form ng-submit="onFormSubmit($event, loginForm);" name="loginForm">
<input type="text" placeholder="username" ng-model="username">
<input type="password" placeholder="Password" ng-model="password">
<div>
<button type="submit">Send Login Data</button>
</div>
<div style="color: blue; padding: 1em .5em;" ng-bind="debug | json">
</div>
</form>
</div>
</article>

Delay directive/div until scope value available

I have a custom directive for soundcloud that requires the soundcloud url. The soundcloud url is fetched from the database through the $http service, however, the div for the soundcloud custom directive is loaded and requires the value of the soundcloud url before it is even defined.
The Plangular Directive Code I got is here:
https://github.com/jxnblk/plangular/blob/master/src/plangular.js *I did not develop this
This is my HTML code:
<div plangular="{{soundcloud}}">
<button ng-click="playPause()">Play/Pause</button>
<progress ng-value="currentTime / duration || 0">
{{ currentTime / duration || 0 }}
</progress>
</div>
And this is the Angular Code:
displaySong.controller('song', ['$scope', '$http', 'fetchSong', function($scope, $http, fetchSong) {
$scope.songID
$scope.songName;
//Controller properties
$scope.songPromise; //The song promise for fetching
$scope.init = function(songID, userID) {
$scope.songID = songID;
$scope.userID = userID;
$scope.songPromise = $http({
method: "post",
url: fetchSong,
data: {
song_id: $scope.songID
},
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).then(function(successResponse) {
console.log('Successfully fetched song');
console.log(successResponse);
var song = successResponse.data;
$scope.songID = song.song_id;
$scope.songName = song.song_name;
$scope.songType = song.song_type;
$scope.songEmbed = song.song_embed;
$scope.soundcloud = song.song_embed;
}, function(errorResponse) {
console.log('Error fetching');
$scope.songID = null;
});
};
}]);
I know it's a problem with the asynchronous nature because when I add this line in the beginning of my song controller:
$scope.soundcloud = "https://soundcloud.com/jshigley/shine";
It works perfectly fine. I've also noticed that when I spam the play/pause button that DOES come up from the directive, I get multiple console errors of "HTTP 404 Not Found", which leads me to believe it's trying to find a track of undefined url
Since it's a div directive and not a function call I can't use promises such as chaining a then to my $scope.songPromise. I've thought of putting it into a controller and having the controller do something like $timeout for 5 seconds, but I don't think this delays the execution of the DOM.
The soundcloud URL DOES end up getting loaded, but it remains undefined in the eyes of the plangular directive (I've actually encountered lots of these problems with bad timing of loading scope and directives). Any Angular Wizards willing to teach me how to tame the asynchronous nature of AngularJS?
You can use $watch in the custom directive to watch when url attributes is changed.
In
link: function(scope, el, attr) {
change from
if (src) {
resolve({ url: src, client_id: client_id }, function(err, res) {
if (err) { console.error(err); }
scope.$apply(function() {
scope.track = createSrc(res);
if (Array.isArray(res)) {
scope.tracks = res.map(function(track) {
return createSrc(track);
});
} else if (res.tracks) {
scope.playlist = res;
scope.tracks = res.tracks.map(function(track) {
return createSrc(track);
});
}
});
});
}
to
scope.$watch('attr.plangular', function(newVal) {
resolve({ url: attr.plangular, client_id: client_id }, function(err, res) {
if (err) { console.error(err); }
scope.$apply(function() {
scope.track = createSrc(res);
if (Array.isArray(res)) {
scope.tracks = res.map(function(track) {
return createSrc(track);
});
} else if (res.tracks) {
scope.playlist = res;
scope.tracks = res.tracks.map(function(track) {
return createSrc(track);
});
}
});
});
}, true);
If you dont want to change the directive then you might want to use ng-if to load that plangular div only when you get the url.
<div plangular="{{soundcloud}}" ng-if="haveurl">
and in the angular code :
}).then(function(successResponse) {
console.log('Successfully fetched song');
console.log(successResponse);
$scope.haveurl = true;
Try using ng-show like this to only show the div once your $http request has been completed.
<div ng-show="httpRequestComplete" plangular="{{soundcloud}}">
<button ng-click="playPause()">Play/Pause</button>
<progress ng-value="currentTime / duration || 0">
{{ currentTime / duration || 0 }}
</progress>
</div>
displaySong.controller('song', ['$scope', '$q', '$http', 'fetchSong', function($scope, $http, fetchSong) {
/* add $q promise library */
$scope.songID
$scope.songName;
var httpRequest = function() {
var deferred = $q.defer();
$http({
method: "post",
url: fetchSong,
data: {
song_id: $scope.songID
},
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).success(function(successResponse) {
deferred.resolve({response: successResponse});
console.log('Successfully fetched song', successResponse);
var song = successResponse.data;
$scope.songID = song.song_id;
$scope.songName = song.song_name;
$scope.songType = song.song_type;
$scope.songEmbed = song.song_embed;
$scope.soundcloud = song.song_embed;
}).error(function(error) {
console.log(error);
});
return deferred.promise;
};
httpRequest().then(function(response) {
$scope.httpRequestComplete = true;
console.log('div will show');
};
}]);
I would do something like this that delays the showing of the div until httpRequestComplete = true, or until your promise ($q) is fulfilled. This will make sure that your div isn't loaded until you have the information available.

Revealing module for AJAX Angular Service

Below I've got an angular app and controller where the controller have data access inside of it (bad idea, I know)
var app = angular.module('app',[]);
app.controller('HomeController',function($scope,$http){
$scope.people = null;
$scope.get = function() {
$http({
url: 'largeTestData.json',
method: 'GET'
}).then(function(data){
console.log('request successful, here is your data: ');
console.log(data['data']);
$scope.people = data['data'];
},function(reason){
console.log('this failed, this is the reason: ');
console.log(reason);
})
}
});
app.controller('ControllerWithService',function($scope, MyService){
$scope.get = MyService.get;
$scope.get(function(data){
console.log('you succeeded');
},function(reason){
console.log('you failed');
console.log(reason);
})
})
This will work in retrieving data and putting it onto the page. Knowing that having data Access in the controller is no bueno I tried to abstract that out into a service:
app.service('MyService',function($http,$q){
var get = function(){
var deferred = $q.defer();
var url = 'test.json';
$http.get(url).success(deferred.resolve).error(deferred.reject);
}
return {
get: get
}
})
Here my 'data layer' is a service that only has one method: get from the above listed URL.
app.service('MyService',function($http,$q){
var get = function(){
var deferred = $q.defer();
var url = 'test.json';
$http.get(url).success(deferred.resolve).error(deferred.reject);
}
return {
get: get
}
})
and my HTML
<body>
<script src="libs/angular-1.2.15.js"></script>
<script src="app/app.js"></script>
<script src="app/DocumentService.js"></script>
<script src="libs/jQuery-2.1.1.js"></script>
<div ng-controller="HomeController">
<button ng-click="get()" href="#">Get data</button>
<div>{{message}}</div>
<!--<div ng-repeat="p in people" >-->
<!--<b>Business Doc ID: </b><h1>{{p['busDocId']}}</h1>-->
<!--<b>DOC ID: </b>{{p['docId']}}-->
<!--<b>FILE NAME: </b><div style="color: green">{{p['fileName']}}</div>-->
<!--</div>-->
</div>
<div ng-controller="ControllerWithService">
{{message}}
<button ng-click="get()">get data</button>
<div>{{data}}</div>
</div>
</body>
I'm not getting any error messages, and the commented out out stuff in my HomeController works as expected. What am I doing wrong in trying to make my AJAX calls a service?
working solution changes:
app.service('MyService',function($http,$q){
this.get = function(){
return $http.get('test.json')
}
})
app.controller('ControllerWithService',function($scope, MyService){
$scope.data = null;
$scope.get = function() {
MyService.get().then(function (data) {
console.log('this is the success data: ');
console.log(data)
$scope.data = data;
}, function (reason) {
console.log('this is the fail reason');
console.log(reason);
$scope.data = reason;
})
}
})
It looks like it could be a couple different things. I'll post an example I have working in one of my projects right now. It should be extremely similar and simple with what you're goal is.
Service:
'use strict';
angular.module('srcApp')
.service('Getlanguage', function Getlanguage($location, $http, $log, $state, $rootScope) {
this.getContent = function() {
var language = $location.path().split('/'),
languageCulture = language[1];
if (!languageCulture) {
languageCulture = 'en';
}
$rootScope.cultureCode = languageCulture;
return $http({method: 'GET', url: '/languages/' + languageCulture + '.json'})
.error(function() {
// If service cannot find language json file, redirect to index
$state.go('lang', {lang: 'en'});
});
};
});
Controller Call to service:
After passing in the service as a dependency into the controller.
Getlanguage.getContent().then(function(res) {
$scope.content = res.data;
});
Hope this helps.

angularjs variable not showing when called before $http

In my angular app, i have a message service to display info, loading and error messages for my app. It looks like that:
module.factory('msgSvc', function(){
var current_message = '';
return {
message: function(){
return current_message;
},
setMessage: function(msg){
console.log('setting message: '+ msg);
current_message = msg;
},
clear: function(){ current_message = null; current_style = null}
}
});
and in my view i have
<span class="pull-left label label-warning" >{{ msg.message() }}</span>
I have a login controller, when the user submits the form i want to show a "logging you in..." message while an ajax login is sent. and an error message if there was an error. here's my code:
function LoginCtrl($scope, $http, msgSvc) {
[...]
$scope.login = function(creds) {
console.log(creds);
msgSvc.setMessage('Logging in...');
$http.post('http://...',creds)
.success(function(data){
console.log(data);
[...]
msgSvc.clear();
$location.path('/');
})
.error(function(data, status){
console.log(status);
msgSvc.setMessage('Wrong username or password.');
});
};
}
login() is called by the submit form, Logging in... never shows even though the function is called (it appears in the console). but the error message appears.
am i doing something wrong?
edit: the login form
<form class="form">
<input type="text" placeholder="Username" ng-model="loginCreds.username" required />
<input type="password" placeholder="Password" ng-model="loginCreds.password" required />
<button ng-click="login(loginCreds)">Login</button>
</form>
edit 2
If it changes anything, there are many controllers setting messages in the service and in the actual code, the controller showing the message (where the $scope.msg variable is set) is different from the one setting the message.
function BodyCtrl($scope, msgSvc) {
$scope.msg = msgSvc;
}
There are couple problems with your implementation:
As the message is being set in a private variable, you would need use $watch for the message to be displayed;
A .factory is a singleton and therefore setMessage would have set the same message for all controllers.
The simplest solution is to pass the controller's $scope to the svcMsg:
app.factory("msgSvc", function () {
return function (scope) {
var priv_scope = scope;
this.setMessage = function (msg) {
console.log('setting message: '+ msg);
priv_scope.message = msg;
};
this.clear = function () {
priv_scope.message = "";
};
};
});
In you controller, you would then do:
var msg = new msgSvc($scope);
In case you do want to propagate the message to all controllers, use $rootScope:
app.service("msgSvc", function ($rootScope) {
var priv_scope = $rootScope;
this.setMessage = function (msg) {
console.log('setting message: '+ msg);
priv_scope.message = msg;
};
this.clear = function () {
priv_scope.message = "";
};
});
Check out this Plunker using $rootScope:
http://plnkr.co/edit/NYEABNvjrk8diNTwc3pP?p=preview
As $rootScope is really a global variable in Angular, you shouldn't abuse it. It can also be problematic if you accidentally set the $scope.message in controllers. An alternative is to use $watch to detect the change to the message:
// In your controller, do:
$scope.$watch(
function () {
return msgSvc.message;
},
function () {
$scope.message = msgSvc.message;
}
)
Here is an example using $watch:
http://plnkr.co/edit/vDV2mf?p=info
Set $scope.msg in each place you want to display the value on the view:
function LoginCtrl($scope, $http, msgSvc) {
[...]
$scope.msg = "";
$scope.login = function(creds) {
console.log(creds);
$scope.msg = msgSvc.setMessage('Logging in...');
$http.post('http://...',creds)
.success(function(data){
console.log(data);
[...]
$scope.msg = msgSvc.clear();
$location.path('/');
})
.error(function(data, status){
console.log(status);
$scope.msg = msgSvc.setMessage('Wrong username or password.');
});
};
}
I know you may be trying to avoid that but the changes in its value are not being propagated.
Add $apply to in error()
function LoginCtrl($scope, $http, msgSvc) {
[...]
$scope.login = function(creds) {
console.log(creds);
msgSvc.setMessage('Logging in...');
$scope.msg = msgSvc;
$http.post('http://...',creds)
.success(function(data){
console.log(data);
[...]
msgSvc.clear();
$location.path('/');
})
.error(function(data, status){
$scope.$apply(function() {
msgSvc.setMessage('Wrong username or password.');
});
});
};
}
SEE DEMO

Categories