I have a sample service:
export class ProjectActionService extends ActionService {
constructor() {
'ngInject'
super($state)
}
getProjects() {
// Call API...
}
}
I would like to change the value of a variable (this.showLoader) that exists in a controller from the getProjects () method.
Controller:
export class ProjectComponent {
constructor() {
'ngInject'
}
$onInit() {
this.showLoader = false
}
}
what is the best way to do it, with a multiple inheritance (mixin), a directive ...?
This is not an appropriate way to deal with it. Your service method should only call the api and get the data and not change controller variables. One way to show/hide loader is changing the boolean before calling and change again after getting the response. A sample inside controller after injecting you service:
this.showLoader = true;
this.ProjectActionService.getProjects().then(response => {
...
})
.finally(() => this.showLoader = false);
Related
export class AppComponent {
title = 'my-app';
constructor(private notifyService : NotificationService) {}
ngOnInit() {
socket.on("laravel_database_chat:test", function(message){
//I WANT TO CALL NOTIFICATION SERVICE HERE, BUT NOT WORKING
this.notifyService.showSuccess();
});
I am trying to call notification service inside socket.io class, but not working.
Change normal function to arrow function to get access to this outside current scope.
ngOnInit() {
socket.on("laravel_database_chat:test", (message) => {
this.notifyService.showSuccess();
});
}
I'm learning Angular 2. And got confused over constructor.
Consider the below code :
import { Component, OnInit } from '#angular/core';
import { FormGroup,FormsModule,FormControl } from '#angular/forms';
import { WeatherService } from '../weather.service';
import { WeatherItem } from '../weather-item';
#Component({
selector: 'app-weather-search',
templateUrl: './weather-search.component.html',
styleUrls: ['../../assets/app.css'],
//providers: [WeatherService]
})
export class WeatherSearchComponent implements OnInit {
constructor(private _weatherService : WeatherService) { }
onSubmit(form : FormGroup){
//alert(form.value.location);
this._weatherService.searchWeatherData(form.value.location)
.subscribe(
data => {
const weatherItem = new WeatherItem(data.data.request["0"].query,data.data.weather["0"].maxtempC,data.data.weather["0"].maxtempC);
this._weatherService.addWeatherItems(weatherItem);
console.log(form);
})
}
ngOnInit() {
}
}
Here we are injecting 'WeatherService' in constructor. Can't we do the same outside constructor ? What constructor is doing here actually? Do we really need it here?
The constructor itself is not doing actual work.
Angular creates a new WeatherSearchComponent executing
new WeatherSearchComponent(weatherService);
and this causes the constructor in WeatherSearchComponent to receive the weatherService value.
The constructor
constructor(private _weatherService : WeatherService)
causes an instance field _weatherService to be created and initialized with the value passed from DI.
The constructor is the only place where it is easy to know when the injected service is available and when not.
If the service would passed to a field, setter or method, code in the constructor could not access it because the constructor is executed before outside code has a change to set a field or call a method.
Also for code outside the constructor it is not safe to assume the service is available because this code could be called from the constructor before a field could be set from the outside.
For dependency injection passing dependencies to the constructor is the only way to avoid a lot of complexity.
Dependency Injection in constructor is always better option and while the component is getting created it will get the weatherService as a parameter. To make it clear, below is the transpiled code for your snippet.
var WeatherSearchComponent = (function () {
function WeatherSearchComponent(_weatherService) {
this._weatherService = _weatherService;
}
WeatherSearchComponent.prototype.onSubmit = function (form) {
var _this = this;
//alert(form.value.location);
this._weatherService.searchWeatherData(form.value.location)
.subscribe(function (data) {
var weatherItem = new weather_item_1.WeatherItem(data.data.request["0"].query, data.data.weather["0"].maxtempC, data.data.weather["0"].maxtempC);
_this._weatherService.addWeatherItems(weatherItem);
console.log(form);
});
};
WeatherSearchComponent.prototype.ngOnInit = function () {
};
WeatherSearchComponent = __decorate([
core_1.Component({
selector: 'app-weather-search',
templateUrl: './weather-search.component.html',
styleUrls: ['../../assets/app.css'],
})
], WeatherSearchComponent);
return WeatherSearchComponent;
}());
exports.WeatherSearchComponent = WeatherSearchComponent;
As you can see in turn the javascript code has weatherService Instance being passed on to the function weatherSearchComponent.
I am trying to reuse some working code from AngularJS 1 services written in plain JavaScript in an Angular 2 environment.
The services look, for instance, like the following example:
(function () {
angular.module('myapp.mysubmodule').factory('myappMysubmoduleNormalService', ['someOtherService',
function (someOtherService) {
var internalState = {
someNumber: 0
};
var service = {};
service.someFunction = function () {
internalState.someNumber++;
};
someOtherService.getValues().forEach(function (v) {
service[v] = function () {
console.log(v + internalState.someNumber);
};
});
return service;
}]);
})();
I have found various examples of how to convert AngularJS 1 services to Angular 2 services (such as this one), all of which have in common that instead of the service factory, I have to export a class.
This should look roughly as follows:
import { Injectable } from '#angular/core';
#Injectable()
export class myappMysubmoduleNormalService {
someFunction: function () {
// ?
}
}
Now, the question is how to incorporate the internal state and the dynamically added properties.
Is it really the way to go to do all that in the constructor, i.e. fill each instance of the class upon initialization, like so:
import { Injectable } from '#angular/core';
#Injectable()
export class myappMysubmoduleNormalService {
constructor() {
var internalState = {
someNumber: 0
};
var service = {};
this.someFunction = function () {
internalState.someNumber++;
};
this.getValues().forEach(function (v) {
service[v] = function () {
console.log(v + internalState.someNumber);
};
});
}
}
Or is there any other way? The above probably works (save for the missing dependency injection, that I still have to find out about how to do in Angular 2). However, i am wondering whether it is a good way because I have not come across any samples that did much of a member initialization in their constructor.
You can use just the same approach in Angular with factory providers:
export function someServiceFactory(someOtherService) {
var internalState = {
someNumber: 0
};
var service = {};
service.someFunction = function () {
internalState.someNumber++;
};
someOtherService.getValues().forEach(function (v) {
service[v] = function () {
console.log(v + internalState.someNumber);
};
});
return service;
};
#NgModule({
providers: [
{
token: 'myappMysubmoduleNormalService',
useFactory: someServiceFactory,
deps: ['someOtherService']
}
]
})
Both in Angular and AngularJS the value returned by the factory function is cached.
A service is just a class that you can inject into components. It will create a singleton in the scope where it is named a provider.
import { Injectable. OnInit } from '#angular/core';
#Injectable()
export class myappMysubmoduleNormalService implements OnInit {
internalState: number;
constructor() {}
ngOnInit(){
this.internalState = 0;
}
incrementSomeNumber() {
this.internalState++;
console.log(this.internalState};
}
}
I realize this is not logging a distinct internal state for multiple functions but you get the idea.
Register this as a provider in the app.module (if you want a singleton for app scope)
When you import into a component and then inject in the constructor
constructor(private _myservice : myappMysubmoduleNormalService) {}
you can now use the _myservice methods
myNumber : number = 0 ;
componentFunction() {
_myservice.incrementSomeNumber();
this.myNumber = _myservice.internalState;
}
Of course you could have the service method return the incremented number (or data or a promise of data)
This is rough but gives you the idea. Very little code belongs in the constructor. A service should be injected. what is shown in component constructor is shorthand to a get private variable referencing the service. The service will be a singleton for the scope in which it is provided. (can be overridden within the scope but that seems a code smell to me)
To pass back a value :
In service
incrementSomeNumber(): number {
this._internalState++;
console.log(this._internalState};
return this._internalState;
}
In component:
mynumber: number;
componentFunction() {
this.mynumber = _myservice.incrementSomeNumber();
}
Not sure what you're trying to accomplish but just wanted to show example of getting information from services. Most common use of services for me is a dataservice, so the code would be a little more complex as it is asynch.
Below is my code so far:
My Module
module App.SomeModule {
import ILabelSettingsViewModel = App.GeneralSettings.data.ILabelSettingsViewModel;
import IGeneralSettingsService = App.GeneralSettingsService.IGeneralSettingsService;
export enum LabelPageFormat {
A4,
Thermal
}
export interface IConsignmentDataService {
getAccountLabelFormat(): LabelPageFormat;
}
export class MyDataService implements IMyDataService {
accountLabelFormat: LabelPageFormat;
static $inject: string[] = ["generalSettingsService"];
constructor(private generalSettingsService: IGeneralSettingsService) {
this.determineAccountLabelFormat();
}
getAccountLabelFormat(): LabelPageFormat {
return this.accountLabelFormat;
}
private determineAccountLabelFormat() {
var that = this;
this.generalSettingsService.getLabelSettings().then((data: ILabelSettingsViewModel) => {
switch (data.name) {
case LabelPageFormat[LabelPageFormat.Thermal]:
that.accountLabelFormat = LabelPageFormat.Thermal;
break;
default:
that.accountLabelFormat = LabelPageFormat.A4;
break;
}
}, () => {
that.accountLabelFormat = LabelPageFormat.A4;
});
}
}
angular.module("app.common").service("myDataService", MyDataService);
}
and my controller
module App.Consignment.List {
"use strict";
import IConsignmentDataService = Consignment.IConsignmentDataService;
import ConsignmentListGridScope = Consignment.IConsignmentListGridScope;
class ConsignmentListController implements IConsignmentBulkActionProvider {
accountLabelFormat: LabelPageFormat;
static $inject = ["$scope", "myDataService"];
constructor(private $scope: ConsignmentListGridScope, private myDataService: IMyDataService) {
this.accountLabelFormat = this.consignmentDataService.getAccountLabelFormat();
}
}
angular.module("app.consignment").controller("consignmentListController", ConsignmentListController);
}
what I am trying to do is, get the accountLabelFormat from my data service and then use it to somewhere else. In data service, a method is used to get the format from database which is returned as a promise and then if success, I am setting the variable that will be returned when I call the getAccountLabelFormat() method from my controller. Now my problem is, as the service method is async, by the time I call the getAccountLabelFormat() method, the variable in accountLabelFormat service was not yet set, so that every time I got an undefined value in my controller. Any ideas about how can I solve this? Thanks in advance.
use $q.when. check out https://docs.angularjs.org/api/ng/service/$q
For example:
$q.when(this.accountLabelFormat)
so when you ask for that value it will return a promise then just chain it a then statement
I am trying to create directive in Typescript which will keep watch on pending $resource requests. I want only one directive as an attribute which will be used with div in index.html to show loading progress. Below is my code for directive.
module app.common.directives {
interface IProgressbarScope extends ng.IScope {
value: number;
isLoading: any;
showEl: any;
}
class Progressbar implements ng.IDirective {
static $inject = ['$http'];
static instance(): ng.IDirective {
return new Progressbar;
}
//transclude = true;
restrict = 'A';
replace = true;
link = function (scope: IProgressbarScope, elements: ng.IAugmentedJQuery, attrs: ng.IAttributes, $http: ng.IHttpService) {
debugger;
scope.isLoading = function () {
return $http.pendingRequests.length > 0;
};
scope.$watch(scope.isLoading, function (v) {
debugger
if (v) {
elements.addClass("hidediv")
} else {
elements.removeClass("hidediv");
}
});
}
}
angular.module('app')
.directive('progressbar', Progressbar.instance);
}
in Index.html, it is used as below:
<div progressbar id="myProcess" name="myProcess">
// loading image
</div>
But in directive, $http is always undefined. Note that I am not using $http directly. I a using $resource service for making server side api requests.
The reason $http undefined is, you are trying to get $http dependency from link function of directive. Basically 4th parameter of link function stands for require controller.
You should Ideally get that injected dependency instance from Progressbar constructor function.
class Progressbar implements ng.IDirective {
_http: ng.IHttpService; //defined _http variable
static $inject = ['$http'];
//asking for dependency here
static instance($http: ng.IHttpService): ng.IDirective {
this._http = $http; //get `$http` object assigned to `_http`
return new Progressbar;
}
//transclude = true;
restrict = 'A';
replace = true;
//removed dependency from here
link = function (scope: IProgressbarScope, elements: ng.IAugmentedJQuery, attrs: ng.IAttributes) {
//use arrow function here
scope.isLoading = ()=> {
return this._http.pendingRequests.length > 0;
};
//use arrow function here
scope.$watch(scope.isLoading, (v)=> {
if (v) {
elements.addClass("hidediv")
} else {
elements.removeClass("hidediv");
}
});
}
}
define $scope.isLoading inside directiveController and make $http call from service layer.
basic controller.ts
export class sampleController {
// inject service here
constructor() {
}
public isLoading() {
callServiceFunction();
}
}
sampleController.$inject['service'];
Import this controller inside custom directive.
SampleService.ts
export class sampleService {
constructor() {
}
}
sampleService.$inject = ['$http'];
Register this service inside app module.
For more info refer sample Importing and exporting example and large scale app architecture