I have here my javascript code:
define(['controllers/controllers', 'services/alerts'], function(module) {
'use strict';
return module.controller('alerts', [
'$scope', '$http', 'alerts', function($scope, $http, alerts) {
console.log('alerts controller initialized');
$scope.settings = {};
return $scope.submit = function() {
$scope.busy = true;
console.log('scope', $scope);
return console.log('data', $scope.data);
};
}
]);
});
I tried to log the contents of $scope.data that I expect to contain the values of ng-model => data.followers but always show undefined but when I tried to log value contents of $scope, $scope.data exists. As shown in the image:
I tried initializing $scope.data but it will always return an empty array after changing the value of ng-model => data.followers. This is the code (in haml) when I initialized ng-model:
%input{:type => "checkbox", "ng-checked" => "settings.#{$key}", "ng-model" => "data.#{$key}", "ng-true-value" => "true", "ng-false-value" => "false", "ng-click" => "submit()"}
Any thoughts?
UPDATE:
Already fix this. All I did was to initialize $scope.data and used data.#{$key} in the ng-checked. That got me stuck. Noob angular programmer here.
When lazy loading Angular components, you need to use $scope.$apply(). When components are lazy loading using Require, or some other method, the functions are registered onto teh $scope outside of the $digest loop of Angular. using $scope.$apply() makes the $digest aware of your bindings, and brings them inside the app instance.
You will likely also need to use module.register.controller instead of module.controller
define(['controllers/controllers', 'services/alerts'], function(module) {
'use strict';
return module.register.controller('alerts', [
'$scope', '$http', 'alerts', function($scope, $http, alerts) {
console.log('alerts controller initialized');
$scope.settings = {};
return $scope.submit = function() {
$scope.busy = true;
console.log('scope', $scope);
return console.log('data', $scope.data);
};
$scope.$apply();
}
]);
Related
I'm running into that annoying Angular minify problem (I really hope this issue is non-existent in Angular 2)
I've commented out all my app module injections and going down the list 1 by 1 to find out where the problem is and I think I narrowed it down to my searchPopoverDirectives:
Can you see what I'm doing wrong?
Original code, produces this error Unknown provider: eProvider <- e:
(function() { "use strict";
var app = angular.module('searchPopoverDirectives', [])
.directive('searchPopover', function() {
return {
templateUrl : "popovers/searchPopover/searchPopover.html",
restrict : "E",
scope : false,
controller : function($scope) {
// Init SearchPopover scope:
// -------------------------
var vs = $scope;
vs.searchPopoverDisplay = false;
}
}
})
})();
I then tried the [] syntax in an attempt to fix the minify problem and ran into this error Unknown provider: $scopeProvider <- $scope <- searchPopoverDirective:
(function() { "use strict";
var app = angular.module('searchPopoverDirectives', [])
.directive('searchPopover', ['$scope', function($scope) {
return {
templateUrl : "popovers/searchPopover/searchPopover.html",
restrict : "E",
scope : false,
controller : function($scope) {
// Init SearchPopover scope:
// -------------------------
var vs = $scope;
vs.searchPopoverDisplay = false;
}
}
}])
})();
UPDATE:
Also found out this guy is causing a problem:
.directive('focusMe', function($timeout, $parse) {
return {
link: function(scope, element, attrs) {
var model = $parse(attrs.focusMe);
scope.$watch(model, function(value) {
if (value === true) {
$timeout(function() {
element[0].focus();
});
}
});
element.bind('blur', function() {
scope.$apply(model.assign(scope, false));
})
}
}
})
When you minify code, it minify all code, so your
controller : function($scope) {
was minified to something like
controller : function(e) {
so, just use
controller : ["$scope", function($scope) { ... }]
When minifying javascript the parameter names are changed but strings remain the same.
You have 2 ways that you can define which services need to be injected:
Inline Annotation:
phonecatApp.controller('PhoneListCtrl', ['$scope', '$http', function($scope, $http){
...
}]);
Using the $inject property:
phonecatApp.controller('PhoneListCtrl', PhoneListCtrl);
PhoneListCtrl.$inject = ['$scope', '$http'];
function PhoneListCtrl($scope, $http){
...
}
Using $inject is considered more readable than inline annotations. It is best practice to always have one line declaring the controller, service, or directive, one line for defining the injection values, and finally the implementation method. The method may be defined last due to the hoisting nature of javascript.
Remember: The order of your annotation (strings) and your function parameters must be the same!
I have an angular directive that looks like this:
myApp.directive('foo', function() {
return {
template: '<span>{{foo.bar}}</span>',
restrict: 'E',
scope: true,
controller: 'myController'
};
});
EDIT
I set the directive initially with this controller:
myApp.controller('myController', function ($scope, MyModel) {
$scope.foo = MyModel.get();
});
and it seems to work fine to modify the model from a second controller:
myApp.controller('myOtherController', function($scope, MyModel) {
setTimeout(function() {
MyModel.set({
bar: "biz"
});
}, 3000);
});
but not with this controller code:
myApp.controller('myOtherController', function($scope, MyModel) {
$http.get("/resource").then(function(response) {
MyModel.set(response.data);
});
});
I have confirmed that the model updates in both instances, but the directive does not update the view with the $http request.
Here is a Plunker that will give you the general idea.
I have tried all sorts of $timeout/$scope.$apply solutions and they all either do nothing or through a digest in progress error. Any help would be appreciated
When you use .then(), the data for your response is in response.data
myApp.controller('myController', function($scope, MyModel) {
$scope.foo = MyModel.get();
$http.get("/resource").then(function(response) {
MyModel.set(response.data);
});
});
The .success() method of a promise passes response.data as the first argument:
myApp.controller('myController', function($scope, MyModel) {
$scope.foo = MyModel.get();
$http.get("/resource").success(function(response) {
MyModel.set(response.data);
});
});
Also, you initialize $scope.foo = MyModel.get() when you initialize your controller, so the value of $scope.foo will be the old value after you call MyModel.set(). To fix:
myApp.controller('myController', function($scope, MyModel) {
$scope.foo = MyModel.get();
$http.get("/resource").then(function(response) {
MyModel.set(response.data);
$scope.foo = MyModel.get();
});
});
Here is a working Plunk
The only change I had to make was in the data being sent from your run function
.run(function($httpBackend) {
var res = {
bar: "It WORKED"
};
Not quite sure what the purpose of the $timeout calls is in your factory implementation, and your MyModel factory seems a bit complicated (I think there are much easier ways to accomplish what you are after).
I have a list object in the parent state of this controller. Every time I load the state that uses this controller, $scope.items is emptied and the controller must load the data from the parent scope again.
ListCtrl corresponds to the list state. The parent state contains many lists.
.controller('ListCtrl', ['$scope','$state','$timeout',
function($scope, $state, $timeout) {
console.log($scope.items); // undefined
if(!$scope.items) { // always evaluates to true
$timeout(function() {
$scope.items = $scope.$parent.list.items;
}, 500);
} else {
console.log($scope.items); // never executes here
}
}])
It was my understanding that AngularJS by default caches some views. Does this mean the caching does not apply to the scope variables in the cached views? And most importantly, is there a way to cache this $scope.items so that every time I return to this state, there isn't this timeout delay?
As a beginner of AngularJS, what I understand is that $scope.items is only defined within the current controller. If you want to load items in your html content, just define items under that controller used in that specified html tag.
I made up a toy example ,
angular.module('scopeExample', [])
.controller('MyController', ['$scope', function($scope) {
$scope.username = 'World';
}]);
which is similar to that on AngularJS Scope documentation.
Parent Controller
.controller('parentController', ['$scope','$state','$timeout','$rootScope'
function($scope, $state, $timeout, $rootScope) {
$rootScope.items=['item 1','item 2'];
}])
Another Controller
.controller('ListCtrl', ['$scope','$state','$timeout','$rootScope'
function($scope, $state, $timeout,$rootScope) {
console.log($rootScope.items); // Should give the result
if(!$scope.items) { // always evaluates to true
$timeout(function() {
$scope.items = $scope.$parent.list.items;
}, 500);
} else {
console.log($scope.items); // never executes here
}
}])
Dont forget to add $rootScope on top.
The solution ended up being to check if the $rootScope.list.item had been loaded, instead of just the $scope.item.
if(!$rootScope.list) {
$timeout(function() {
$scope.items = $rootScope.list.items;
}, 1000);
} else {
$scope.items = $rootScope.list.items;
}
I've ran into problem with ng-controller and 'resolve' functionality:
I have a controller that requires some dependency to be resolved before running, it works fine when I define it via ng-route:
Controller code looks like this:
angular.module('myApp')
.controller('MyController', ['$scope', 'data', function ($scope, data) {
$scope.data = data;
}
]
);
Routing:
...
.when('/someUrl', {
templateUrl : 'some.html',
controller : 'MyController',
resolve : {
data: ['Service', function (Service) {
return Service.getData();
}]
}
})
...
when I go to /someUrl, everything works.
But I need to use this controller in other way(I need both ways in different places):
<div ng-controller="MyController">*some html here*</div>
And, of course, it fails, because 'data' dependency wasn't resolved. Is there any way to inject dependency into controller when I use 'ng-controller' or I should give up and load data inside controller?
In the below, for the route resolve, we're resolving the promise and wrapping the return data in an object with a property. We then duplicate this structure in the wrapper service ('dataService') that we use for the ng-controller form.
The wrapper service also resolves the promise but does so internally, and updates a property on the object we've already returned to be consumed by the controller.
In the controller, you could probably put a watcher on this property if you wanted to delay some additional behaviours until after everything was resolved and the data was available.
Alternatively, I've demonstrated using a controller that 'wraps' another controller; once the promise from Service is resolved, it then passes its own $scope on to the wrapped controller as well as the now-resolved data from Service.
Note that I've used $timeout to provide a 1000ms delay on the promise return, to try and make it a little more clear what's happening and when.
angular.module('myApp', ['ngRoute'])
.config(function($routeProvider) {
$routeProvider
.when('/', {
template: '<h1>{{title}}</h1><p>{{blurb}}</p><div ng-controller="ResolveController">Using ng-controller: <strong>{{data.data}}</strong></div>',
controller: 'HomeController'
})
.when('/byResolve', {
template: '<h1>{{title}}</h1><p>{{blurb}}</p><p>Resolved: <strong>{{data.data}}</strong></p>',
controller: "ResolveController",
resolve: {
dataService: ['Service',
function(Service) {
// Here getData() returns a promise, so we can use .then.
// I'm wrapping the result in an object with property 'data', so we're returning an object
// which can be referenced, rather than a string which would only be by value.
// This mirrors what we return from dataService (which wraps Service), making it interchangeable.
return Service.getData().then(function(result) {
return {
data: result
};
});
}
]
}
})
.when('/byWrapperController', {
template: '<h1>Wrapped: {{title}}</h1><p>{{blurb}}</p><div ng-controller="WrapperController">Resolving and passing to a wrapper controller: <strong>{{data.data ? data.data : "Loading..."}}</strong></div>',
controller: 'WrapperController'
});
})
.controller('HomeController', function($scope) {
$scope.title = "ng-controller";
$scope.blurb = "Click 'By Resolve' above to trigger the next route and resolve.";
})
.controller('ResolveController', ['$scope', 'dataService',
function($scope, dataService) {
$scope.title = "Router and resolve";
$scope.blurb = "Click 'By ng-controller' above to trigger the original route and test ng-controller and the wrapper service, 'dataService'.";
$scope.data = dataService;
}
])
.controller('WrapperController', ['$scope', '$controller', 'Service',
function($scope, $controller, Service) {
$scope.title = "Resolving..."; //this controller could of course not show anything until after the resolve, but demo purposes...
Service.getData().then(function(result) {
$controller('ResolveController', {
$scope: $scope, //passing the same scope on through
dataService: {
data: result
}
});
});
}
])
.service('Service', ['$timeout',
function($timeout) {
return {
getData: function() {
//return a test promise
return $timeout(function() {
return "Data from Service!";
}, 1000);
}
};
}
])
// our wrapper service, that will resolve the promise internally and update a property on an object we can return (by reference)
.service('dataService', function(Service) {
// creating a return object with a data property, matching the structure we return from the router resolve
var _result = {
data: null
};
Service.getData().then(function(result) {
_result.data = result;
return result;
});
return _result;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular-route.min.js"></script>
<div ng-app="myApp">
By ng-controller |
By Resolve |
By Wrapper Controller
<div ng-view />
</div>
Create a new module inside which you have the service to inject like seen below.
var module = angular.module('myservice', []);
module.service('userService', function(Service){
return Service.getData();
});
Inject newly created service module inside your app module
angular.module('myApp')
.controller('MyController', ['$scope', 'myservice', function ($scope, myservice) {
$scope.data = data;
// now you can use new dependent service anywhere here.
}
]
);
You can use the mechanism of the prototype.
.when('/someUrl', {
template : '<div ng-controller="MyController" ng-template="some.html"></div>',
controller: function (data) {
var pr = this;
pr.data = data;
},
controllerAs: 'pr',
resolve : {
data: ['Service', function (Service) {
return Service.getData();
}]
}
})
angular.module('myApp')
.controller('MyController', ['$scope', function ($scope) {
$scope.data = $scope.pr.data; //magic
}
]
);
Now wherever you want to use
'<div ng-controller="MyController"></div>'
you need to ensure that there pr.data in the Scope of the calling controller. As an example uib-modal
var modalInstance = $modal.open({
animation: true,
templateUrl: 'modal.html',
resolve: {
data: ['Service', function (Service) {
return Service.getData();
}]
},
controller: function ($scope, $modalInstance, data) {
var pr = this;
pr.data = data;
pr.ok = function () {
$modalInstance.close();
};
},
controllerAs:'pr',
size:'sm'
});
modal.html
<script type="text/ng-template" id="modal.html">
<div class="modal-body">
<div ng-include="some.html" ng-controller="MyController"></div>
</div>
<div class="modal-footer">
<button class="btn btn-primary pull-right" type="button" ng-click="pr.ok()">{{ 'ok' | capitalize:'first'}}</button>
</div>
</script>
And now you can use $scope.data = $scope.pr.data; in MyController
pr.data is my style. You can rewrite the code without PR.
the basic principle of working with ng-controller described in this video https://egghead.io/lessons/angularjs-the-dot
Presuming that Service.getData() returns a promise, MyController can inject that Service as well. The issue is that you want to delay running the controller until the promise resolves. While the router does this for you, using the controller directly means that you have to build that logic.
angular.module('myApp')
.controller('MyController', ['$scope', 'Service', function ($scope, Service) {
$scope.data = {}; // default values for data
Service.getData().then(function(data){
// data is now resolved... do stuff with it
$scope.data = data;
});
}]
);
Now this works great when using the controller directly, but in your routing example, where you want to delay rendering a page until data is resolved, you are going to end up making two calls to Service.getData(). There are a few ways to work around this issue, like having Service.getData() return the same promise for all caller, or something like this might work to avoid the second call entirely:
angular.module('myApp')
.controller('MyController', ['$scope', '$q', 'Service', function ($scope, $q, Service) {
var dataPromise,
// data might be provided from router as an optional, forth param
maybeData = arguments[3]; // have not tried this before
$scope.data = {}; //default values
// if maybeData is available, convert it to a promise, if not,
// get a promise for fetching the data
dataPromise = !!maybeData?$q.when(maybeData):Service.getData();
dataPromise.then(function(data){
// data is now resolved... do stuff with it
$scope.data = data;
});
}]
);
I was trying to solve the problem using ng-init but came across the following warnings on angularjs.org
The only appropriate use of ngInit is for aliasing special properties
of ngRepeat, as seen in the demo below. Besides this case, you should
use controllers rather than ngInit to initialize values on a scope.
So I started searching for something like ng-resolve and came across the following thread:
https://github.com/angular/angular.js/issues/2092
The above link consists of a demo fiddle that have ng-resolve like functionality. I think ng-resolve can become a feature in the future versions of angular 1.x. For now we can work around with the directive mentioned in the above link.
'data' from route resolve will not be available for injection to a controller activated other than route provider. it will be available only to the view configured in the route provider.
if you want the data to the controller activated directly other than routeprovider activation, you need to put a hack for it.
see if this link helps for it:
http://www.johnpapa.net/route-resolve-and-controller-activate-in-angularjs/
Getting data in "resolve" attribute is the functionality of route (routeProvider) , not the functionality of controller.
Key( is your case : 'data') in resolve attribute is injected as service.
That's why we are able fetch data from that service.
But to use same controller in different place , you have fetch data in controller.
Try this
Service:
(function() {
var myService = function($http) {
var getData = function() {
//return your result
};
return {
getData:getData
};
};
var myApp = angular.module("myApp");
myApp.factory("myService", myService);
}());
Controller:
(function () {
var myApp = angular.module("myApp");
myApp.controller('MyController', [
'$scope', 'myService', function($scope, myService) {
$scope.data = myService.getData();
}
]);
//Routing
.when('/someUrl', {
templateUrl : 'some.html',
controller : 'MyController',
resolve : {
data: $scope.data,
}
})
}());
I need help, about added jasmine tast to my factory.
My code is...
---dataService.js---
angular.module('angularAppApp')
.factory('dataService', function($resource){
return $resource(`http://...:3100/posts/:id`, null,
{
'update': { method:'PUT' }
});
})
---onePostCtrl.js ---
angular.module('angularAppApp')
.controller('onePostCtrl', ['$scope', '$http', '$routeParams', 'dataService',
function ($scope, $http, $routeParams, dataService) {
dataService.get ({id: $routeParams.postId}).$promise.then(function(data){
$scope.postInfo = data;
});
}]);
-- main container ---
angular.module('angularAppApp').controller('postCtrl', ['$scope','$http', 'ngDialog', 'dataService','trimService', function ($scope, $http, ngDialog, dataService, trimService) {
//save data to remote server from loaded pop-up
$scope.savePost = function(){
$scope.addFormData.date = $scope.formated_date;
dataService.save($scope.addFormData, function() {
laodData();
});
ngDialog.closeAll();
};
//delete post from remote server
$scope.deletePost = function(article) {
dataService.delete({ id: article._id }, function() {
laodData();
});
};
//edit post from remote server
$scope.updatePost = function (article) {
dataService.update({ id: article._id},article).$promise.then(function() {
laodData();
});
ngDialog.closeAll();
}
}]);
--- mock data ---
angular.module('mock', []).value('items', [{ ... }]
---At index.html I am have loaded mocks scripts---
src="bower_components/angular-mocks/angular-mocks.js"
src="mosk_data/mocks.module.js"
--Jasmine tests is ...
describe("factory of dataService", function (){
var $httpBackend, $http, $q, factory;
beforeEach(module("angularAppApp"));
beforeEach(module('mock'));
beforeEach(function(){
inject(function($injector, _$httpBackend_,_$http_,_$q_){
$q = _$q_;
$http = _$http_;
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', '/items').respond(items);
factory = $injector.get('dataService');
});
afterEach(function () {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it("Data service", function(){
});
});
Now, I have error "ReferenceError: items is not defined" and cannot ideas how I can test my dataService.
You forgot to inject your value and assign it to a variable in the tests. Try this:
var $httpBackend, $http, $q, factory, items; //declare variable items here (or you can do it inside beforeEach)
beforeEach(module("angularAppApp"));
beforeEach(module('mock'));
beforeEach(function(){
inject(function($injector, _$httpBackend_,_$http_,_$q_, _items_){
$q = _$q_;
$http = _$http_;
//inject the value and assign to your variable
items = _items_
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', '/items').respond(items);
factory = $injector.get('dataService');
});
The Reference error you got was because there was no variable called items. You defined an angular value with name items, but it's not the same as a variable - think of it as it lives "somewhere inside angular guts" and to use it you have to inject it and then use as normal variable.