Example of injecting services in Angular 1.5 components - angularjs

Can anyone give an example on using services with Angular 1.5 components?
I'm trying to inject a service in an Angular 1.5 component, but it doesn't work.
I have a login component like so:
class Login {
constructor($scope, $reactive, $state, myService) {
console.log(myService.somevariable); //doesn't work
}
}
// create a module
export default angular.module(name, [
angularMeteor
]).component(name, {
templateUrl: 'imports/ui/components/${name}/${name}.html',
controllerAs: name,
controller: Login
});
My service looks like this:
angular.module(name).service("myService", function () {
this.somevariable = 'somevalue';
});
I just cant seem to be able to get the service injected in the component.What am I doing wrong?
SOLUTION:
With sebenalern's help, I got it working.
I needed a service to validate an email address using a regular expression. I did it like this:
import angular from 'angular';
import angularMeteor from 'angular-meteor';
class Validator {
validateEmail(email) {
var re = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
}
const name = 'validator';
// create a module
export default angular.module(name, [
angularMeteor
])
.service("Validator", Validator);
I then injected the service like so:
import {name as Validator} from '../../../api/services/validator'
class Login {
constructor($scope, $reactive, $state, Validator) {
'ngInject';
this.$state = $state;
$reactive(this).attach($scope);
this.Validator = Validator;
}
login() {
if(this.Validator.validateEmail(this.credentials.email)) {
// email is valid.
}
}
}
const name = 'login';
export default angular.module(name, [
angularMeteor,
Validator
]).component(name, {
templateUrl: `imports/ui/components/${name}/${name}.html`,
controllerAs: name,
controller:Login
})
Hope this helps :)

So one problem I see is you should be using the keyword this inside the constructor
this.$scope = $scope;
Another thing it is probably easier to stay away from classes and use functions:
class Login {
constructor($scope, $reactive, $state, myService) {
console.log(myService.somevariable); //doesn't work
}
}
Becomes:
angular
.module('name')
.service('myService', myService);
function myService () {
this.somevariable = 'somevalue';
}
To me it seems a lot cleaner. Also another thing about ES6 classes is
ES6 Classes are not hoisted, which will break your code if you rely on hoisting
For more info see link.
Now here is the working code I came up with:
First we declare the module:
angular.module('name', []);
Next we register our service and then create the service definition:
angular
.module('name')
.service('myService', myService);
function myService () {
this.somevariable = 'somevalue';
}
Next we do the same procedure for our controller and also we inject $scope and our service into it.
angular
.module('name')
.controller('Login', Login);
function Login($scope, myService) {
$scope.someVar = myService.somevariable;
}
Last I registered our component:
angular
.module('name')
.component('my-html', {
templateUrl: 'my-html.html',
controller: Login
});
And that is it on the javascript side.
Here is my html code:
<!DOCTYPE html>
<html lang="en-us" ng-app='name'>
<head>
<script src="//code.angularjs.org/1.5.0-rc.1/angular.js"></script>
<script src="controller.js"></script>
</head>
<body >
<h ng-controller="Login">{{ someVar }}</h>
</body>
</html>
I hope this helps!!

Here is how I am doing it and it works well. It works fine with classes. I assume you are using TypeScript.
class AdminHomeService {
consignment: IConsignment;
get: () => IConsignment;
constructor() {
this.consignment = new Consignment();
this.consignment.id = 10;
this.consignment.customer = "Customer3";
this.consignment.customerList = [{ id: 1, name: "Customer1" }, { id: 2, name: "Customer2" }, { id: 3, name: "Customer3" }];
this.consignment.shipperList = [{ key: "1", value: "Shipper1" }, { key: "2", value: "Shipper2" }, { key: "3", value: "Shipper3" }];
this.consignment.consigneeList = [{ key: "1", value: "Consignee1" }, { key: "2", value: "Consignee2" }, { key: "3", value: "Consignee3" }];
this.consignment.billingList = [{ key: "1", value: "Billing1" }, { key: "2", value: "Billing2" }, { key: "3", value: "Billing3" }];
this.consignment.carrierList = [{ key: "1", value: "Carrier1" }, { key: "2", value: "Carrier2" }, { key: "3", value: "Carrier3" }];
this.get = () => {
return this.consignment;
}
}
}
class AdminHomeComponentController {
consignment: IConsignment;
selectedCustomer: any;
static $inject = ["adminHomeService"];
constructor(private adminHomeService: AdminHomeService) {
this.consignment = new Consignment();
this.consignment = this.adminHomeService.get();
this.selectedCustomer = {};
this.selectedCustomer.selected = { "name": this.consignment.customer };
}
customerAddClick(): void {
}
}
class AdminHomeComponent implements ng.IComponentOptions {
bindings: any;
controller: any;
templateUrl: string;
$routeConfig: angular.RouteDefinition[];
constructor() {
this.bindings = {
textBinding: "#",
dataBinding: "<",
functionBinding: "&"
};
this.controller = AdminHomeComponentController;
this.templateUrl = "templates/admin.home.html";
//this.$routeConfig = [
// { path: "/admin", name: "AdminHome", component: "adminHome", useAsDefault: true }
//];
}
}
angular.module("adminHome", [])
.component("adminHome", new AdminHomeComponent())
.service("adminHomeService", AdminHomeService);
This post helped me a lot: http://almerosteyn.com/2016/02/angular15-component-typescript

Related

Angular 2+ services to angularJs

I m trying to convert Angular 2+ service and use it in angularJs project.
app/users.service.ts
import { Injectable } from '#angular/core';
#Injectable()
export class UsersService {
private users: any = [
{ id: 1, name: 'john' },
{ id: 2, name: 'jane' }
];
constructor() { }
getUsers() {
return this.users;
}
}
then I tranform it into js with rollup
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('#angular/core'), require('#angular/upgrade/static'), require('#angular/platform-browser')) :
typeof define === 'function' && define.amd ? define(['exports', '#angular/core', '#angular/upgrade/static', '#angular/platform-browser'], factory) :
(factory((global.mymodule = {}),global.ng.core,global._static,global.platformBrowser));
}(this, (function (exports,core,_static,platformBrowser) { 'use strict';
var UsersService = /** #class */ (function () {
function UsersService() {
this.users = [
{ id: 1, name: 'john' },
{ id: 2, name: 'jane' }
];
}
UsersService.prototype.getUsers = function () {
return this.users;
};
UsersService.decorators = [
{ type: core.Injectable },
];
/** #nocollapse */
UsersService.ctorParameters = function () { return []; };
return UsersService;
}());
var MyModule = /** #class */ (function () {
function MyModule(upgrade) {
this.upgrade = upgrade;
}
MyModule.decorators = [
{ type: core.NgModule, args: [{
imports: [platformBrowser.BrowserModule, _static.UpgradeModule],
declarations: [],
providers: [UsersService],
exports: []
},] },
];
/** #nocollapse */
MyModule.ctorParameters = function () { return [
{ type: _static.UpgradeModule, },
]; };
return MyModule;
}());
exports.UsersService = UsersService;
exports.MyModule = MyModule;
Object.defineProperty(exports, '__esModule', { value: true });
})));
I add downgradeInjectable to convert angular2+ service into angularJs
angular.module('users.service', [])
.factory('usersServices', downgradeInjectable(usersServices));
next I try to load it in angularJs
(function() {
'use strict';
angular.module(
'ng1.module', [
'usersServices'
]);
})();
I m missing something because it doesn't work :/
I can't find ressource to do it, all people a trying to convert ng1 service to ng2 but not the reverse.
Someone can help ?
Thank you
Instead of importing 'usersServices' (the service factory) in the module 'ng1.module', you should import the actual module with 'usersServices' in it instead:
(function() {
'use strict';
angular.module(
'ng1.module', [
'users.service'
]);
})();

UI Router, Passing Data From Parent Controller

I'm attempting to refactor a website into Angular through UI-Router. On the parent component I have defined some data on the controller. How do I pass this data to the child nested routed component through UI-Router? UI-Router's resolve only works when you have a binding on data, but I don't know if it's possible to bind the parent controller data to the parent component.
const main = angular.module('main', ['ui.router']);
main.config(function($stateProvider) {
const states = [
{ name: 'parent', url: '/parent', component: 'parent' },
{ name: 'parent.child', url: '/{childUrl}', component: 'child',
resolve: {
data: function($transition$) {
// I want to pass in { name: 'Name1', content: 'Page-Long Content1' }, { name: 'Name2', content: Page-Long Content2' }
// How do I do this?
}
}
}
];
states.forEach(function(state) {
$stateProvider.state(state);
});
});
angular.module('main')
.component('parent', {
template:
'<div ng-repeat="data in $ctrl.data">' +
'<p>{{data.name}}</p>' +
'<a ui-sref="parent.child({ childUrl: data.name })" ui-sref-active="active">Child link</a>' +
'</div>' +
'<ui-view></ui-view>',
controller: function() {
this.data = [
{name: 'Name1', content: 'Page-Long Content1'},
{name: 'Name2', content: 'Page-Long Content2'}
]
}
});
angular.module('main')
.component('child', {
bindings: { data: '<' },
templateUrl: 'link.html',
});
Basically you have an unique identifier of your record in the URL. So by that you can retrieve your data once again from the dataset. For achieving the same, I'd suggest you to put your data in service and then fetch the same data in resolve of your state. Afterward apply an filter over the data and get desired record from it based on childUrl parameter of state.
angular.module('main')
.service('dataService', function(){
var dataService = this;
dataService.getData = getData;
var data = [
{name: 'Name1', content: 'Page-Long Content1'},
{name: 'Name2', content: 'Page-Long Content2'}
];
function getData(){
return data;
}
});
state
const states = [
{ name: 'parent', url: '/parent', component: 'parent' },
{ name: 'parent.child', url: '/{childUrl}', component: 'child',
resolve: {
data: function($transition$, dataService) {
let childUrl = $transition$.params('childUrl');
//in case of API call below will be changed to promise driven code.
return dataService.getData().filter((item) => item.name === childUrl)[0];
}
}
}
];
And inside a parent controller you can fetch data from service directly.
controller: function(dataService) {
this.data = dataService.getData();
}

Controller Tests using Jasmine and Typescript

I am getting the following error
Expected 0 to be 4.
...anggen/app/products/products.list.controller.spec.js:22:35
Could anyone tell me why the httpbackend is not returning the response? or is it something silly that I missed? Spent a lot of time on getting this to work and am stuck..
Please look at the test case below. The test in question is it("should create controller"... test
My controller code is as follows...
module app.productList {
'use strict';
export interface IProductListModel {
title: string;
showImage: Boolean;
products: app.domain.IProduct[];
toggleImage(): void;
addNumbers(firstNumber:number, secondNumber:number): number;
}
export class ProductListCtrl implements IProductListModel {
title:string;
showImage:boolean;
products:app.domain.IProduct[];
static $inject = ["dataAccessService"]; // for minification
constructor(private dataAccessService:app.productService.DataAccessService) {
this.title = "Product List";
this.showImage = false;
this.products = [];
dataAccessService.getProductResource().query((data:app.domain.IProduct[]) => {
this.products = data;
});
}
public toggleImage():void {
this.showImage = !this.showImage;
}
public addNumbers(firstNumber:number, secondNumber:number) {
return firstNumber + secondNumber;
}
}
angular.module("app.products").controller("ProductListCtrl", ProductListCtrl)
}
My service code is as follows...
module app.productService {
export interface IDataAccessService {
getProductResource(): ng.resource.IResourceClass<IProductResource>;
getProductResourceDetails(): ng.resource.IResourceClass<IProductResource>;
}
export interface IProductResource extends ng.resource.IResource<app.domain.IProduct> {
}
export class DataAccessService
implements IDataAccessService {
// for minification.
static $inject = ["$resource"];
constructor(private $resource: ng.resource.IResourceService) {
}
getProductResource(): ng.resource.IResourceClass<IProductResource> {
//return this.$resource("/api/products/:productId");
return this.$resource("http://localhost:9998/generic/products");
}
}
angular
.module("app.products")
.service("dataAccessService", DataAccessService);
}
My Test code is as follows...
describe("Product List Controller", function () {
var controller: app.productList.ProductListCtrl;
var dataAccessService: app.productService.DataAccessService;
var $httpBackend : ng.IHttpBackendService;
beforeEach(function () {
angular.mock.module("productManagement");
});
beforeEach(inject(function (
$rootScope: ng.IRootScopeService,
_$httpBackend_: ng.IHttpBackendService,
_dataAccessService_: app.productService.DataAccessService) {
dataAccessService=_dataAccessService_;
$httpBackend=_$httpBackend_;
}));
it("should create controller", () => {
$httpBackend.expectGET("http://localhost:9998/generic/products").respond([
{"id": "1", "name": "Pizza Vegetaria", "price": 5 },
{"id": "2", "name": "Pizza Salami", "price": 5.5 },
{"id": "3", "name": "Pizza Thunfisch", "price": 6 },
{"id": "4", "name": "Aktueller Flyer", "price": 0 }
]);
controller = new app.productList.ProductListCtrl(dataAccessService);
expect(controller).not.toBeNull();
var result = controller.products;
expect(result.length).toBe(4);
$httpBackend.flush();
});
});

Service with Angularjs and Typescript

I have a $http service that read a json file. I want to use modularization, but I don't know how to call my service in my controller and I don't know if my service is well written.
Here a jsFidlle : https://jsfiddle.net/aqbmdrvn/ .
Thanks!!!
/// <reference path="../../typings/angularjs/angular.d.ts" />
/// <reference path="../../typings/angularjs/angular-route.d.ts" />
/// <reference path="../app.ts" />
/// <reference path="servicePets.ts" />
"use strict";
module AnimalPlanet {
// pet interface
export interface IPet {
type: string;
name: string;
age: number;
color: string;
specialCare: boolean;
availableForAdoption: boolean;
ldAdoption: boolean;
history: string;
featured: boolean;
newest: boolean;
imageUrl: string;
}
export interface RootObject {
pets: IPet[];
}
// pet controller with ui-grid
export class petsCtrl implements RootObject {
pets: IPet[];
constructor(private $http: ng.IHttpService,public petsService, private $scope: any, uiGridConstants: any, filterldAdoption: any) {
$scope.pets = {};
// ui grid option
$scope.gridOptions = {
enableSorting: true,
enableFiltering: true,
paginationPageSizes: [5, 10, 15],
paginationPageSize: 5,
onRegisterApi: (gridApi) => {
$scope.gridApi = gridApi;
},
columnDefs: [
{
name: 'type',
cellTooltip: true,
headerTooltip: true
},
{
name: 'name',
cellTooltip: true,
headerTooltip: true
},
{
name: 'age',
// filters: [{
// condition: uiGridConstants.filter.GREATER_THAN,
// placeholder: 'greater than'
// }, {
// condition: uiGridConstants.filter.LESS_THAN,
// placeholder: 'less than'
// }
// ],
cellTooltip: true,
headerTooltip: true
},
{
name: 'color',
cellTooltip: true,
headerTooltip: true
},
{
name: 'specialCare',
cellTooltip: true,
headerTooltip: true
},
{
name: 'availableForAdoption',
cellTooltip: true,
headerTooltip: true
},
{
name: 'history',
cellTooltip: true,
headerTooltip: true
},
{
name: 'featured',
cellTooltip: true,
headerTooltip: true
},
{
name: 'newest',
cellTooltip: true,
headerTooltip: true
},
{
name: 'imageUrl',
cellTooltip: true,
headerTooltip: true,
enableFiltering: false,
enableHiding: false,
cellTemplate: "<img width=\"50px\" ng-src=\"{{grid.getCellValue(row, col)}}\" lazy-src>"
}
]
};
// read json using http service
this.$http.get('/app/pets/pets.json').success((data) => { // pune te rog asta intr-un serviciu
// fill ui grid using http service
$scope.filterPets = data;
var uiGridPets = [];
angular.forEach($scope.filterPets, (item) => {
if (item.ldAdoption) {
uiGridPets.push(item);
}
});
$scope.gridOptions.data = uiGridPets;
// filter for main page with 3 pets
$scope.pets = data;
$scope.quantity = 3;
var featuredPets = [];
var newestPets =[];
angular.forEach($scope.pets, (item) => {
if (item.featured) {
featuredPets.push(item);
}
if(item.newest){
newestPets.push(item);
}
});
$scope.featuredPets = featuredPets;
$scope.newestPets = newestPets;
});
$scope.fromService = petsService.weatherChange();
}
}
petsCtrl.$inject = ['$http', '$scope', 'uiGridConstants', 'petsService'];
app.controller("petsCtrl", petsCtrl);
}
/// <reference path="../../typings/angularjs/angular.d.ts" />
/// <reference path="../../typings/angularjs/angular-route.d.ts" />
/// <reference path="../app.ts" />
"use strict";
module AnimalPlanet {
export interface IPetsService {
http: ng.IHttpService;
uiGridConstants: any;
}
export class servicePets implements IPetsService {
http: ng.IHttpService;
uiGridConstants: any;
constructor( $scope:any , $http: ng.IHttpService, uiGridConstants: any )
{
// read json using http service
$scope.pets = {};
this.http = $http;
}
public get() {
this.http.get('/app/pets/pets.json').success((data) => { // pune te rog asta intr-un serviciu
// fill ui grid using http service
var filterPets = data;
var uiGridPets = [];
angular.forEach(filterPets, (item) => {
if (item.ldAdoption) {
uiGridPets.push(item);
}
});
var gridOptions.data = uiGridPets;
// filter for main page with 3 pets
var pets = data;
var quantity = 3;
var featuredPets = [];
var newestPets =[];
angular.forEach(pets, (item) => {
if (item.featured) {
featuredPets.push(item);
}
if(item.newest){
newestPets.push(item);
}
});
var featuredPets = featuredPets;
var newestPets = newestPets;
});
}
}
servicePets.$inject = ['$http', '$scope', 'uiGridConstants'];
app.service('servicePets', servicePets);
}
Here's what I use for boilerplate controller and factory:
Controller code:
declare var app: angular.IModule;
class MyController {
static $inject = ['myService'];
localData: IGetDataResult;
constructor(private myService: MyService) {
this.myService.getData('key1')
.then(result => {
this.localData = result.data;
});
}
}
app.controller('myController', MyController);
Factory code:
declare var app: angular.IModule;
interface IGetDataResult {
field1: string;
}
class MyService {
static $inject = ['$http'];
constructor(private $http: angular.IHttpService) {
}
getData(key: string): angular.IHttpPromise<IGetDataResult> {
return this.$http({
method: 'POST',
data: {
key: key
},
url: '/WebService/GetData'
});
}
}
function MyServiceFactory($rootElement) : MyService {
const inj = $rootElement.injector();
return inj.instantiate(MyService);
}
app.factory('myService', ['$rootElement', $rootElement => MyServiceFactory($rootElement)]);
Some explanations:
The controller is straightforward enough, but note the usage of the static injection variable $inject. The order of elements in that array must correspond to the order of parameters in the constructor function.
My factory is instantiated by a factory function (pardon the pun). Note that I am injecting a dependency on $rootElement. I discovered that if I ever want to use $location I must use the injector instantiated on that $rootElement.
I am using a strongly typed return value on the $http call. This helps a lot for the obvious reasons.
Note that everything is definitely typed, except for $rootElement. There is no matching type for it in angular.d.ts that includes the injector() function.
One more thing, typescript lends itself to the controllerAs methodology. Meaning that you don't need to inject $scope into your controller, and in your html you must prepend the controller alias (the controllerAs value) before every scope expression. When doing it that way, you put all your scope variables and functions as members on your controller.
Sometimes you do need to inject $scope if you want to use $watch or $on, but there are a few gotchas there, with regards to this closure, but this is a topic for another post.
One gotcha with ui-grid is that if you use a string value for your gridoptions.data member, it must also include the controller alias prefix.
To modularize your code, you want to refactor the $http call and its dependency injection out of the controller and into the service.
When you inject the service into the controller you need to account for the module "namespace". So, your $inject will look like: "AnimalPlanet.petsService". This is probably why you are having trouble invoking the service from your controller. It doens't look like it's wired properly.
I don't use $inject the way you are doing it. I follow this pattern:
// Dependencies for this controller.
static $inject = ["$q",
"$location",
"$window",
"$scope",
"$log",
"common",
"RL.SS.Portal.RLToday.Services.AroundTheIndustryService",
"RL.SS.Portal.RLToday.Services.LocationFilterService"];
That's inside the class definition itself. I don't know if that's necessarily more accurate, but you might find it useful.
In the controller's constructor, you don't need to (and probably should not) make the petservice reference public, but that's not really your problem, just a style thing.
Once you get the injection right, you can do this from your controler:
this.petsService.Get().then( () => { /* success */ }, () => { /* fail */});
HTH.
(this is my first attempt at answering a stack overflow question, so if anyone wants to advise me on how to do better, please feel free to do so).

How to load angular1 dependencies using systemjs?

I'm using typescript, angular1 and systemjs in the following example.
The problem that I have is I'm not able to load the dependencies for the module app as in
module('app', ["ngComponentRouter", "hub"])
I see an error in the chrome dev tab that the injecting a module failed.
The System.import loads the app module which supposed to load the hub module also. But how to inject the hub module as a dependency for app module?
index.html
<app></app>
<script src="~/lib/system.js/dist/system.src.js"></script>
<script>
System.config({
defaultJSExtensions: true,
baseURL: "wwwroot",
map: {
"angular": "/lib/angular/angular.min.js",
"hub": "/App/hub.js"
}
});
System.import('./App/app');
</script>
app.ts
import * as angular from 'angular';
import * as hub from './hub/hub'
module App {
"use strict";
export class AppController {
static $inject = [
"$router"
];
constructor(private $router: any) {
$router.config([
{ path: "/hub", component: "hub", as: "Hub" },
{ path: "/home", component: "home", as: "Home" }
]);
$router.navigateByUrl("hub");
}
}
angular.
module('app', ["ngComponentRouter", "hub"]).
controller('AppController', AppController).
directive('app', () => {
return {
restrict: "E",
template: "<ng-outlet><ng-outlet>",
controller: "AppController"
}
});
}
hub.ts
export module Hub {
export class Tile {
title: string;
name: string;
constructor(title: string, name: string) {
this.name = name;
this.title = title;
}
}
export class HubController {
selectedTiles: number[];
hubTiles: Tile[];
static $inject = [
"$scope",
"$router"
];
constructor(private $scope: any, private $router: any) {
this.hubTiles = [new Tile("USA", "home"),
new Tile("Canada", "home"),
new Tile("Mexico", ""),
new Tile("India", ""),
new Tile("Germany", ""),
new Tile("Australia", "")];
this.selectedTiles = [];
$scope.$watch(() => this.selectedTiles, (newValue: number[], oldValue: number[]) => {
if (newValue[0] != oldValue[0]) {
$router.navigateByUrl(this.hubTiles[newValue[0]].name);
}
}, true);
}
}
var hub = angular.
module("hub", ["ngComponentRouter", "winjs"]).
controller("HubController", HubController).
directive("hub", () => {
return {
templateUrl: "./app/hub/hub.html",
controller: HubController,
controllerAs: "hub"
}
});
}

Resources