Following Dan Wahlin's article on dynamically loading controllers and views http://weblogs.asp.net/dwahlin/archive/2013/05/22/dynamically-loading-controllers-and-views-with-angularjs-and-requirejs.aspx, I coded an sample AngularJS application with one minor modification as follows.
While Dan for example loads the data service ahead of time (main.js), I would lile to load a factory only if a controller needs it like this:
main.js
require.config({
baseUrl: '/app'
});
require([
'app',
'services/routeResolver'
//'services/mathService'
],
function () {
angular.bootstrap(document, ['myFirstApp'])
});
mathService.js
'use strict';
define(['app'], function (app) {
var mathService = function () {
return {
add: function(n1, n2) {
if (isNaN(n1) || isNaN(n2)) {
return NaN;
}
return parseInt(n1) + parseInt(n2);
}
}
};
app.factory('mathService', mathService);
});
v2Controller.js
'use strict';
define(['app', 'services/mathService'], function (app) {
var ctrl = function ($scope, mathService) {
$scope.greeting = 'Hi there from viewtwoland!';
$scope.n1 = 0;
$scope.n2 = 0;
$scope.result = 0;
$scope.add = function ()
{
$scope.result = mathService.add($scope.n1, $scope.n2);
}
};
app.register.controller('v2Controller', ['$scope', 'mathService', ctrl]);
});
v2.html
<p>
{{greeting}}
</p>
<input type="text" data-ng-model="n1" id="n1" name="n1" data-ng-change="add()" />
<input type="text" data-ng-model="n2" id="n2" name="n2" data-ng-change="add()" />
<div>
The sum of {{n1}} and {{n2}} is: {{result}}
</div>
This is however not working as expected. Here is the result I am getting:
{{greeting}}
[ ] [ ]
The sum of {{n1}} and {{n2}} is: {{result}}
Any ideas. Is this doable at all.
I resolved the issue by changing the controller as follows:
define(['app', 'services/mathService'], function (app) {
var ctrl = function ($scope, mathService) {
$scope.greeting = 'Hi there from viewtwoland!';
$scope.n1 = 0;
$scope.n2 = 0;
$scope.result = 0;
$scope.add = function ()
{
return mathService.add($scope.n1, $scope.n2);
}
};
app.register.controller('v2Controller', ['$scope', 'mathService', ctrl]);
});
and the mathService as follows:
'use strict';
define(['app'], function (app) {
var mathService = function () {
return {
add: function(n1, n2) {
if (isNaN(n1) || isNaN(n2)) {
return NaN;
}
return parseInt(n1) + parseInt(n2);
}
}
};
app.register.factory('mathService', mathService);
});
If you have a better solution for this issue, I'd be interesting in knowing it.
Related
I'm using angularjs and I am trying to parse a value from a service to my $scope controller. The problem is that is loading first the $scope and then the service. As a result me my scope is always undefinied. I tried many solutions that I found by no-one is worked for me. Can anyone help me please?
My service:
'use strict';
angular.module('myApp')
.service('getCategoriesService', function ($rootScope) {
getCategories = function () {
var categoriesByLocation = 1;
return categoriesByLocation;
};
and my controller:
angular.module("myApp")
.controller('MyCtrl', function ($scope,getCategoriesService) {
$scope.resultCategory = getCategoriesService.getCategories();
});
Use the this. when declaring a function in a service.
.service('getCategoriesService', function ($rootScope) {
this.getCategories = function () {
var categoriesByLocation = 1;
return categoriesByLocation;
};
})
demo
var app = angular.module("myApp",[]);
app.controller('MyCtrl', function ($scope,getCategoriesService) {
$scope.resultCategory = getCategoriesService.getCategories();
});
app.service('getCategoriesService', function ($rootScope) {
this.getCategories = function () {
var categoriesByLocation = 1;
return categoriesByLocation;
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
{{resultCategory}}
</div>
Change it as
angular.module("myApp")
.controller('MyCtrl', function ($scope,getCategoriesService) {
getCategoriesService.getCategories().then((function (d) {
$scope.resultCategory = d;
}
});
I've a AngularJs controller that gets a list of categories from ASP.NET MVC controller. It works just fine and here is the below code:
productApp.controller('categoryController', function ($scope, categoryService) { //The controller here
$scope.Categories = null;
categoryService.GetCategories().then(function (d) {
$scope.Categories = d.data;
}, function () {
alert('Failed');
});
});
productApp.factory('categoryService', function ($http) { //The product service
var factory = {};
factory.GetCategories = function () {
return $http.get('/Product/GetCategories'); //The ASP.NET MVC controlled defined
}
return factory;
});
I am trying to convert it to the following passing the service as a controller parameter but it doesn't work:
(function () {
'use strict';
angular
.module('productApp', ['ngMessages'])
.controller('categoryController', categoryController);
categoryController.$inject = ['$scope', 'Products'];
function categoryController($scope, categoryService) {
$scope.Categories = null;
categoryService.GetCategories().then(function (d) {
$scope.Categories = d.data;
}, function () {
alert('Failed');
});
}
productApp.factory('categoryService', function ($http) { //The product service
var factory = {};
factory.GetCategories = function () {
return $http.get('/Product/GetCategories'); //The ASP.NET MVC controlled defined
}
return factory;
});
})();
My requirement is to get all the controller united as follows:
angular
.module('productApp')
.controller('categoryController', categoryController)
.controller('productController', productController);
I believe, I am close enough and missing something here. I would expect some suggestions to implement it in the correct way. Thanks.
Update 1 - The below code works almost but it doesn't bind the DropDownList with category data: But using alert method, it returns [Object][Object] that means it gets categories from database
(function () {
'use strict';
angular
.module('productApp', [])
.controller('categoryController', categoryController)
.factory('categoryService', categoryService);
categoryController.$inject = ['$scope', 'categoryService'];
function categoryController($scope, categoryService) {
$scope.Categories = null;
categoryService.GetCategories().then(function (d) {
$scope.Categories = "Service: " + d.data + ", Controller: categoryController";
alert(d.data); //Here it alert [Object][Object]
}, function () {
alert('Failed');
});
}
function categoryService($http) {
var factory = {};
factory.GetCategories = function () {
return $http.get('/Product/GetCategories');
}
return factory;
};
})();
In the view:
<div ng-app="productApp" ng-controller="categoryController">
<select ng-model="saveProducts.ParentId">
<option value="">----Please Select Category----</option>
<option ng-repeat="m in Categories" value="{{ m.CategoryId }}">{{ m.Category }}</option>
</select>
</div>
#user8512043 Here is a JSFiddle with the working code: http://jsfiddle.net/luisperezphd/2fvwz6wp/
(function () {
'use strict';
angular
.module('productApp', [])
.controller('categoryController', categoryController)
.factory('categoryService', categoryService);
categoryController.$inject = ['$scope', 'categoryService'];
function categoryController($scope, categoryService) {
$scope.Categories = null;
categoryService.GetCategories().then(function (d) {
$scope.Categories = d.data;
}, function () {
alert('Failed');
});
}
function categoryService($http, $q) {
var factory = {};
factory.GetCategories = function () {
//return $http.get('/Product/GetCategories');
return $q.when({ data: [
{ CategoryId: 1, Category: "Category 1" },
{ CategoryId: 10, Category: "Category 2" },
{ CategoryId: 20, Category: "Category 3" }
]});
}
return factory;
};
})();
Note that it looks like code was fine. The drop down wasn't working because you assigned a string to the scope variable instead of an array.
One technique to resolve issue like that is to make it as visual as you can. For example use an ng-repeat on a instead of an .
Also in general if you are going to ask for help on StackOverflow you are going to get a lot more responses if you create a JSFiddle or Plnkr with your sample code.
Finally if you are going to do that mock the server calls. This will allow the person who wants to answer to do some quick experiments.
Check this code
(function () {
'use strict';
angular
.module('productApp',[])
.controller('categoryController', categoryController)
.controller('productController', productController)
.factory('categoryService', categoryService);
function categoryController($scope, categoryService) {
$scope.data = "Service: "+categoryService.serviceName+", Controller: categoryController";
};
function productController($scope, categoryService) {
$scope.data = "Service: "+categoryService.serviceName+", Controller: productController";
};
function categoryService() {
return {serviceName: "categoryService"};
};})();
I have a view for SidebarController like below -
<a ng-click="reachMe($event);$event.preventDefault()" ng-href="#/app/hello">
Before going to the link I want to call reachMe() to check some changes on page and need to show an alert if any changes made
function SidebarController($rootScope, $scope, $state, $location, SidebarLoader){
$scope.reachMe = function(event){
//here I want to call function isPageChanged() from StaticPageController
//something like this
// if StaticPageController.isPageChanged() return true
// then show alert
// else
// $location.url($href)
}
}
Update 1 :
Not sure about this, But give it a try.
<div ng-app="testApp" ng-controller="ControllerOne">
<button ng-click="methodA();"> Call Another Controller</button>
</div>
<script>
var app = angular.module('testApp', []);
app.controller('ControllerOne', function($scope, $rootScope) {
$scope.reachMe = function() {
var arrayData = [1,2,3];
$rootScope.$emit('callEvent', arrayData);
if($rootScope.isChanged){
// Show Alert
}else{
//Go to route
}
}
});
app.controller('ControllerTwo', function($scope, $rootScope,$state) {
$scope.checkSomethingChanged = function() {
alert("Hello");
$rootScope.isChanged = true;
}
$rootScope.$on('callEvent', function(event, data) {
console.log(data);
$scope.checkSomethingChanged();
});
});
Following method worked for me perfectly :
<div ng-app="testApp" ng-controller="ControllerOne">
<button ng-click="methodA();"> Call Another Controller</button>
</div>
<script>
var app = angular.module('testApp', []);
app.controller('ControllerOne', function($scope, $rootScope) {
$scope.methodA = function() {
var arrayData = [1,2,3];
$rootScope.$emit('callEvent', arrayData);
}
});
app.controller('ControllerTwo', function($scope, $rootScope) {
$scope.reachMe = function() {
alert("Hello");
}
$rootScope.$on('callEvent', function(event, data) {
console.log(data);
$scope.reachMe();
});
});
</script>
A controller is not the right concept for sharing functionality. Use a Factory or Service for that.
var logicFactory = function () {
return {
methodA: function () {
},
methodB: function()
{
}
};
}
You can then inject that factory into each controller where it is needed like:
var ControllerA = function ($scope,logicFactory) {
$scope.logic = logicFactory;
}
ControllerA.$inject = ['$scope', 'logicFactory'];
Another option is to use the broadcast/emit Patern. But I would use that only where really necessary:
Usage of $broadcast(), $emit() And $on() in AngularJS
Below I have a simple form with a controller and service. Why is there an error thrown when I mention ['angucomplete-alt']
Html
<div ng-controller="Hell">
<input type="button" ng-click="Hello()" value="Save" />
</div>
MyApp.Js
var aap = angular.module('MyApp', ['angularUtils.directives.dirPagination','angucomplete-alt'])
aap.controller('myhmectrl', function ($scope) {
$scope.msg = "Hello Angular....";
})
Conroller.Js
(function () {
angular.module('MyApp', ['angucomplete-alt'])
.controller('Hell', function ($scope, Myservices) {
$scope.Hello = function () {
alert('ok ctrls');
var xx = Myservices.GetSer();
}
});
})
Service.Js
(function () {
angular.module('MyApp', ['angucomplete-alt'])
.service('Myservices', function ($http) {
this.GetSer = function () {
alert('pk Servicess....')
}
})
})
In controller if you are passing the dependencies it will create a new module and instantiate again, Remove the dependency
It shoule be
angular.module('MyApp').controller('Hell', function ($scope, Myservices) {
}
Maybe you didn't include js file for angucomplete-alt ?
You need to remove dependency array when you are accessing your module. Modify your files as belows:
Controller.js
(function () {
angular.module('MyApp')
.controller('Hell', function ($scope, Myservices) {
$scope.Hello = function () {
alert('ok ctrls');
var xx = Myservices.GetSer();
}
});
})
Service.js
(function () {
angular.module('MyApp')
.service('Myservices', function ($http) {
this.GetSer = function () {
alert('pk Servicess....')
}
})
})
I have integrated requirejs with my angular app.
But while loading app, it gives me an error 'Argument 'appCtrl' is not a function, got undefined'
Here is my controller code :
define(['Angular'], function (angular) {
function appCtrl($scope, pathServices) {
alert('sa');
}
function homeCtrl($scope, brandService) {
console.log('dfd');
}
});
And along with this, it gives error for 'unknown provider pathServices'
Service code is :
serviceConfig.js
define([
'Angular',
'common/Services/services',
'current/js/services'
], function(angular, commonServices, loacalStorageServices, currentServices) {
"use strict";
var services = {
commonServices : commonServices,
currentServices : currentServices,
};
var initialize = function (angModule) {
angular.forEach(services,function(service, name) {
angModule.service(name, service);
});
}
return {
initialize: initialize
};
});
common/services.js
define(['Angular'], function (angular) {
var app = angular.module('myApp.services', []);
app.factory('pathServices', function($http, $q, $rootScope) {
function pathServices() {
alert('as');
}
return new pathServices();
});
app.factory('anotherServices', function($http, $q, $rootScope) {
function anotherServices() {
alert('as');
}
return new anotherServices();
});
});
current/services.js
define(['Angular'], function(angular) {
var app = angular.module('myApp.services', []);
app.factory('brandsService', function() {
function brandsService() {
var autoCompleteData = [];
this.getSource = function() {
return autoCompleteData;
}
this.setSource = function(states) {
autoCompleteData = states;
}
}
return new brandsService();
});
});
in serviceConfig.js I have included 2 service files.. But the problem is, the last current/service.js file overwrites all files.. How can I include multiple service files ?
I am new to requirejs. How can I use controller function and services using requirejs ?
Can anyone help ?
You have to declare your functions in the global (window) namespace, or register them in your module with the moduleName.controller('controllerName',controllerFn)
So either
define(['Angular'], function (angular) {
window.appCtrl = function($scope, pathServices) {
alert('sa');
}
window.homeCtrl = function($scope, brandService) {
console.log('dfd');
}
});
or
define(['Angular'], function (angular) {
var module = angular.module('theModuleName');
module.controller('appCtrl', function($scope, pathServices) {
alert('sa');
});
module.controller('homeCtrl', function($scope, brandService) {
console.log('dfd');
}
});
should fix this error (I prefer the second approach).