Angular js cycle when declare component - angularjs

I have an Angular js module
var appModule = angular.module('appModule', ['datatables','ngMaterial', 'ui.select'])
when i declare a new component using my appModule, I refresh my app in browser but it creates a request load cycle in XHR and my app breaks
appModule.component('myCustomTable', {
templateUrl: 'table-component',
controller: function () {
this.user = {
name: 'user name'
};
}
});
Am using AngularJS 1.65

It might happen because your controller is an anonymous function.
(According to: https://docs.angularjs.org/tutorial/step_03)
appModule.component('myCustomTable', {
templateUrl: 'table-component',
controller: function () { // <- Here
this.user = {
name: 'user name'
};
}
});

Related

Cannot read property 'injector' of null when downgrading angular 2 service

everyone. I need help with such problem.
I have such code for my angular 1.x app.js:
angular.module('app', []);
angular.module('app.test', ['app'])
.config(($stateProvider) =>
$stateProvider.state('base', {
url: '/',
controller: 'TestStateCtrl',
resolve: {
authenticationCheck: ['angularNg1Service', angularNg1Service=> {
angularNg1Service.test1();
}]
}
})
})
.run((angularNg1Service) => {
angularNg1Service.test2();
});
Here is the code of my angularNg1Service:
angular.module('app')
.service('angularNg1Service',
function (angularNg2Service} {
//some code here
}
My angularNg2Service is downgraded before .run function of angular 1.x module starts:
window['angular']
.module('app')
.factory(
'angularNg2Service',
upgradeAdapter.downgradeNg2Provider(AngularNg2Service)
);
But, I have an error message :
Cannot read property 'injector' of null
When .run function of angular 1.x module starts.
Here is my main.ts file:
import { upgradeAdapter } from './upgradeAdapter';
import { bootstrapNg1Components } from './app/ng1Components';
bootstrapNg1Components(upgradeAdapter);// this function downgarades my AngularNg2Service
upgradeAdapter.bootstrap(document.querySelector('html'), ['app.start']);
I have read some similar problems but don't find any solution.
Also I have a lot of Angular2 Services which are downgraded.But the problem is just for one particular service that is injected into Angular1 Service that is used in .run function.
You can use workaround described here https://github.com/angular/angular/issues/10992:
put your run code in setTimeout function.
angular.module('app.test', ['app'])
...
.run(($injector) => {
setTimeout(function() {
var angularNg1Service = $injector.get('angularNg1Service');
angularNg1Service.doSmth();
// downgraded angularNg2Service is available here
// inside of async function that will run after module.run method
var angularNg2Service = $injector.get('angularNg2Service');
},0);
});
To be sure the application state does not start before application is configured (that performed in run method) you can add resolve for every state.
angular.module('app.test', ['app'])
.config(($stateProvider) =>
$stateProvider.state('base', {
url: '/',
controller: 'TestStateCtrl',
resolve: {
appBootstrapped: ['appBootstrapStateService', () => {
return appBootstrapStateService.statePromise;
]},
authenticationCheck: ['angularNg1Service', 'appBootstrapped', (angularNg1Service, appBootstrapped) => {
angularNg1Service.test1();
}]
}
})
})
And to make it works you should change bootstraped state at the end of module.run method
angular.module('app.test', ['app'])
...
.run(($injector) => {
setTimeout(function() {
...
var appBootstrapStateService = $injector.get('appBootstrapStateService');
appBootstrapStateService.complete(); // allow state work
},0);
});
appBootstrapStateService is angularJS service like
angular.module('app')
.service('appBootstrapStateService', function () {
const stateSubject = new Rx.Subject();
this.statePromise = stateSubject.asObservable().toPromise();
this.complete = () => {
stateSubject.complete();
};
});

Use resolve with es6 class syntax

I want to use resolve to check if user is present on localStorage before controller fires up but can't get it to work.
Example:
//loginDirective.js
import {LoginController as controller} from './login.controller';
import template from './login.html';
export const loginDirective = () =>{
return{
controller,
template,
scope:{},
controllerAs:'vm',
replace: true,
restrict: 'E'
}
}
//login.js
import {loginDirective} from './login.directive';
import angular from 'angular';
import uiRouter from 'angular-ui-router';
export const login = angular.module('admin', [uiRouter])
.config(($stateProvider) => {
$stateProvider.state('login', {
url : '/login',
template: '<login></login>',
resolve : {
loggedUser: function () {
if(localStorage.getItem('user')){
return true;
}
return false;
}
}
})
})
.directive('login', loginDirective);
//LoginController
'use strict';
class LoginController {
constructor($state, AuthenticationService, loggedUser) {
this.$state = $state;
this.AuthenticationService = AuthenticationService;
this.email = '';
this.password = '';
this.loggedIn = loggedUser;
}
login = () => {
this.AuthenticationService.Login(this.email, this.password).then((result) => {
this.loggedIn = result;
if (this.loggedIn === true) {
this.$state.go('home', {}, {reload: true});
}
});
}
logout = () => {
this.AuthenticationService.Logout();
this.loggedIn = false;
this.$state.go('login', {location: true});
}
}
LoginController.$inject = ['$state', 'AuthenticationService', 'loggedUser'];
export {LoginController}
This throws Unknown provider: loggedUserProvider <- loggedUser
Note:
I am using webpack, gulp and babel to transpile. WebPack has babel stage 1 enabled.
I can see the resolve method in this.$state but that is not the way I want to access it.
Your 'login' state definition has no controller. So ui-router doesn't instantiate the login controller. So it can't pass anything to its constructor.
The controller is not the controller of the state. It's the controller of a directive that happens to be used in the template of the state. So that can't work.
Read https://ui-router.github.io/guide/ng1/route-to-component to understand how you must do: either use a controller and a template, or use a component.

How can I write a test for $cookieStore like this?

I have 2 roles: one is admin, this another is normal user. Admin can navigate to item-detail page, and normal user can not. Hence, I store user's role in "globals" cookies when they are login. I get their role from cookieStore to check which one can navigate to item-detail page. This function works well. However, I have no idea how to write a test for $cookieStore in checkUserRole function:
angular.module('config', ['ui.router'])
.config(function($stateProvider, $urlRouterProvider)
{
$urlRouterProvider.otherwise('/login');
$stateProvider
.state('login',
{
url: '/login',
templateUrl: 'login-page.html',
controller: 'LoginController'
})
.state('index',
{
url: '/index',
templateUrl: 'bridge.html'
})
.state('item-detail',
{
url: '/index/item-detail/:Name',
templateUrl: 'item-detail.html',
controller: 'myCtrl',
resolve:
{
checkUserRole: function($cookieStore)
{
if($cookieStore.get('globals').currentUser.userRole === 'user')
{
return state.go('index');
}
}
}
});
});
And, here is my test case:
describe('config', function()
{
var $scope, $state, $cookieStore, userRole;
beforeEach(function()
{
module('config', function($provide)
{
$provide.value('$cookieStore', { get: 'globals' });
});
inject(function($injector, $templateCache)
{
$scope = $injector.get('$rootScope');
$state = $injector.get('$state');
$cookieStore = $injector.get('$cookieStore');
$templateCache.put('login-page.html', '');
$templateCache.put('bridge.html', '');
$templateCache.put('item-detail.html', '');
});
});
it('home page', function()
{
$scope.$apply();
expect($state.current.name).toBe('login');
expect($state.current.templateUrl).toBe('login-page.html');
expect($state.current.controller).toBe('LoginController');
});
it('login page', function()
{
$scope.$apply(function()
{
$state.go('login');
});
expect($state.current.name).toBe('login');
expect($state.current.templateUrl).toBe('login-page.html');
expect($state.current.controller).toBe('LoginController');
});
it('items page', function()
{
$scope.$apply(function()
{
$state.go('index');
});
expect($state.current.name).toBe('index');
expect($state.current.templateUrl).toBe('bridge.html');
});
it('item-detail page', function()
{
spyOn($cookieStore, 'get').and.callFake(function()
{
return 'user';
});
expect($cookieStore.get('globals')).toBe('user');
$scope.$apply(function()
{
$state.go('item-detail');
});
expect($state.current.name).toBe('item-detail');
expect($state.current.templateUrl).toBe('item-detail.html');
expect($state.current.controller).toBe('myCtrl');
expect($state.href('item-detail', { Name: 'lumia-950'})).toEqual('#/index/item-detail/lumia-950');
});
});
My question is: How can I write a test for $cookieStore.get('globals').currentUser.userRole? or how can I mock it to test what if user's role is user?.
I don't know what version of angular you're using but $cookieStore is now deprecated, prefer using $cookies instead (see doc).
There are at least 3 ways to proceed :
Using Jasmine with Angular $cookie (from v1.4.x) :
describe('config module', function(){
var $cookies;
beforeEach(function(){
angular.mock.module('config');
angular.mock.inject(function(_$cookies_){
$cookies = _$cookies_;
});
});
it('should have a "globals" cookie with "user" value', function(){
var globals = $cookies.getObject('globals');
expect(globals.currentUser.userRole).toBe('user');
});
});
Using Jasmine with pure JavaScript :
describe('config module', function(){
it('should have a "globals" cookie with "user" value', function(){
var globals = document.cookie.replace(/(?:(?:^|.*;\s*)globals\s*\=\s*([^;]*).*$)|^.*$/, "$1");
globals = JSON.parse(globals);
expect(globals.currentUser.userRole).toBe('user');
});
});
Using Jasmine with Protractor (e2e test) :
describe('config module', function(){
it('should have a "globals" cookie with "user" value', function(){
var globals = JSON.parse(browser.manage().getCookie('globals'));
expect(globals.currentUser.userRole).toBe('user');
});
});
Here Protractor gives us browser global variable to handle browser related behavior.
Note that JSON.parse() is used to unserialized the cookie string value and parse it to an usable JavaScript object.
example :
var obj = JSON.parse('{ "foo" : "bar" }');
console.log(obj.foo); //print 'bar' in the console
IMPORTANT:
In your application, use only 1.4.7 version of Angular libraries with the Angular 1.4.7 Core.
Let me know if it helps.

passing complex type as param

Angular js / angular ui router 0.2.15
I am trying to pass complex js array into the controller using $state.go
Following is my code can you help. looks like following code throwing error
Error: [$injector:unpr] http://errors.angularjs.org/1.2.28/$injector/unpr?p0=servicesProvider%20%3C-%20services
.js file
var services = {complex type};
$state.go("linearOfferProcess", {'services': services});
in my route.js
state('linearOfferProcess', {
url: '/linearOfferProcessor',
templateUrl: '/partials/linear_process.html',
controller: 'linearProcessController',
services:
function($stateParams) {
return $stateParams.services;
}
}
controller
angular.module('app').controller('linearOfferProcessController',function($scope) {
$scope.services = services;
});
Unfortunately I don't know any possible of accomplishing this, but you could try doing it using a Factory. I made a small mockup for you, in the container I'm storing the services and creating a unique ID for every complex object. So you can retrieve it whenever you want.
angular.module('app').factory('ServicesContainer', function() {
var counter = 1;
return {
container: {},
add: function(services) {
container[counter] = services;
return counter;
}
};
});
angular.module('app').controller('SomeController', function(ServicesContainer, $state) {
var id = ServicesContainer.add({ complex object });
$state.go('services', {
id: id
});
});
angular.module('app').config(function($stateProvider) {
$stateProvider.state('services', {
url: '/{id}',
onEnter: function(ServicesContainer, $stateParams, $log) {
$log.info('Services', ServicesContainer[$stateParams.id]);
}
});
});

Best way to save and use config settings in AngularJS

I'm new to AngularJS and just building an app to learn it. My app calls a REST API and right now I have the hostname hard coded in the app. I want to make this in app setting (and maybe later have somewhere to configure it). I thought I'd start with a constant. Should it go in my app.js like this? If so, I'm not sure of the syntax for adding it to the .config settings with $routeProvider there too
(function () {
// Define module and add dependencies inside []
var app = angular.module("haClient", ["ngRoute"]);
app.constant('hostname', 'http://192.192.192.192:8176');
app.config(function ($routeProvider) {
$routeProvider
// Register routes
// Main route
.when("/main", {
templateUrl: "main.html",
controller: "MainController"//,
//activeTab: 'home'
})
// Device List
.when("/devices", {
templateUrl: "devicelist.html",
controller: "DeviceListController"
})
// Device details (param is device name)
.when("/device/:devicename", {
templateUrl: "device.html",
controller: "DeviceController"
})
// Invalid URL's get sent back to main route
.otherwise({ redirectTo: "/main" });
}); // End App Config
}());
This is the module that needs to use it (called from controllers):
(function () {
var deviceCtrl = function ($http) {
var getDevices = function () {
return $http.get("http://192.192.192.192:8176/devices.json/")
.then(function (response) {
return response.data;
});
};
// get details and return a promise
var getDeviceDetails = function (deviceName) {
return $http.get("http://192.192.192.192:8176/devices/" + deviceName + ".json/")
.then(function (response) {
return response.data;
});
};
// Public API
return {
getDeviceDetails: getDeviceDetails,
getDevices: getDevices
};
};
var module = angular.module("haClient");
}());
Can someone enlighten em on the best way to set it and get it?
Thanks
I currently do this by using templates to build the root .html file in the backend. Eg, using doT templates in node.js, I put this below my other js includes:
<!-- load any templated constants -->
<script>
angular.module('mymod').constant('globals', {
api: '{{=it.api}}'
});
</script>
This way my backend can work out the logic of where the client needs to point to. In order to use the value in another service or controller, you simply inject the constant by name:
angular.module('somemod', []).factory('myservice', ['globals', function(globals){
// use globals.api or what ever you set here for example
}]);
The best place to do configuration is in a Provider and inside your config block.
Providers expose an API that allows you to configure your service before it's injected into your controllers and directives.
Suppose you have a service called myService that you want injected into your controller function like this:
app.controller('ctrl', function($scope, myService) { ...});
And myService is responsible for retrieving data through web API calls. Let's further assume that you would like to configure your service with the root URL htt://servername/, since all calls will share the same host name.
You can define your myServiceProvider like this:
app.provider('myService', function(){
var webapiurl;
this.setWebServiceUrl = function (url) {
webapiurl = url;
}
// the injector will call the $get function to create the singleton service that will be injected
this.$get = function( /*injectables*/) {
return {
getData: function() {... Use webapiurl ...}
}
}
});
Then in your config function, configure your provider:
app.config(function(myServiceProvider){
myServiceProvider.setWebServiceUrl('htt://servername');
});
Finally you can inject the service anywhere it can be injected:
app.controller('ctrl', function($scope, myService) {
$scope.data = myService.getData();
});
I couldn't get the supplied answers to work (no reason to think they wouldn't). here's what I did.
My main app.js (constants section)
(function () {
// Define module and add dependencies inside []
var app = angular.module("haClient", ["ngRoute"]);
//constants
app.constant('mySettings', {
baseURL: 'http://192.192.192.192:8176',
otherSetting: 'XYZ'
});
app.config(function ($routeProvider) {
$routeProvider
// Register routes
// Main route
.when("/main", {
templateUrl: "main.html",
controller: "MainController"//,
//activeTab: 'home'
})
// Device List
.when("/devices", {
templateUrl: "devicelist.html",
controller: "DeviceListController"
})
// Device details (param is device name)
.when("/device/:devicename", {
templateUrl: "device.html",
controller: "DeviceController"
})
// Invalid URL's get sent back to main route
.otherwise({ redirectTo: "/main" });
}); // End App Config
}());
In my service (added mySettings as a dependency and then just used mySettings.baseURL):
(function () {
var deviceCtrl = function ($http, $log, mySettings) {
$log.info("DeviceCtrl - baseURL: " + mySettings.baseURL);
// get device list and return a promise
var getDevices = function () {
return $http.get(mySettings.baseURL + "/devices.json")
.then(function (response) {
return response.data;
});
};
// get details and return a promise
var getDeviceDetails = function (deviceName) {
$log.info("DeviceCtrl - Getting device info for " + deviceName);
return $http.get(mySettings.baseURL + "/devices/" + deviceName + ".json")
.then(function (response) {
return response.data;
});
};
// Public API
return {
getDeviceDetails: getDeviceDetails,
getDevices: getDevices
};
};
var module = angular.module("haClient");
module.factory("deviceCtrl", deviceCtrl);
}());
I'm certainly no expert (as is clear from not being able to get supplied answers working), and I'm not sure (yet) if there are any drawbacks to this method. It allowed me to get on with my project and learn more of Angular, so I went with it.
Regards
Mark
I have used another module and injected it into app module like this
Create constant.js and include that in your index.html & add following code inside that
angular.module('constantsModule', []).constant('BASEURL', 'http://google.com');
Inside app.js, inject 'constantsModule' so that all constants inside it will be available for 'haClient'
angular.module('haClient', [
'constantsModule'
])
.config(function ($routeProvider) {
.when('/', {
templateUrl: 'views/landing.html',
controller: 'landingCtrl'
})
});
Inside landingCtrl, since its in scope of 'haClient', we can inject BASEURL constant from 'constantsModule'
angular.module('haClient').controller('landingCtrl', function ($scope, BASEURL) {
// just to show, how to access it inside controller
$scope.baseurl = BASEURL;
});

Resources