I have checked some of the topics for this matter and i got an understanding of controllers are there to initiate scope and i need to use services for this matter but i dont know how.
so here is the problem. i have index page which body has only one div and inside the div i have ng-include listening to a function called viewFile() which is described on controllerA. on the first initial attempt i load a view called login.html and display it. when users logs in and its successful, which are handled in controllerB, i return a token and now i want to load main.html page using viewFile() in controllerA. is there a call back function or notify controller or something for this? or can i write a service that takes care of this for me?
I'm not using ngRoute because i dont want my URL to change to mysite.com/#/login.html and then mysite.com/#/main.html
.controlle("A", function ($scope, sharedVariable){
$scope.token = sharedVariable.getToken();
$scope.viewFile = function(){
if($scope.token == "")
return "view/Login.html";
else
return "view/main.html";
}
}
.controller("B", function ($scope, $http, sharedVariable)){
http({
get ...
.success: function(data){
$scope.token = sharedVariable.setToken();
// INVOKE viewFile from above controller
}
})
}
and here is the index.html body part
<body>
<div ng-controller="A"><ng-include src="viewFile()"></ng-include></div>
</body>
look at this simple example http://jsfiddle.net/derkoe/T85rg/presentation/ here personService.person is shared between two controllers similarly you can write your viewFile function in one service like personService. Then call personService.viewFile from any controller. You can pass $scope as its argumen. Something like below
var myModule = angular.module('myModule', []);
myModule.factory('myService', function($rootScope) {
var sharedService = {};
sharedService.viewFile = function($scope) {
if($scope.token == "")
return "view/Login.html";
else
return "view/main.html";
};
return sharedService;
});
If you want to change the view using different condition define you viewFile function in some service or put it in routescope. Then you can call it from multiple controllers. But I don't think without refresh angularjs will be able to load a different view html
Related
I'm new on Angularjs and I'm trying to build my first application. Let's say I have to routes that loads two different views:
127.0.0.1:8080/site
127.0.0.1:8080/site_details
Maybe having two different routes is not the right procedure but that it is another problem.
I have two controllers:
Controller 1:
app.controller('controller_1', function($scope, $http, user) {
user.set('Test Example')
});
and Controller 2
app.controller('controller_2', function($scope, $http, user) {
var xxx = user.get()
});
What I want to do is to share data between these two controllers. To do that I did a service in this way:
app.factory('user', function($rootScope) {
var savedData = {}
function set(data) {
savedData = data;
}
function get() {
return savedData;
}
return {
set: set,
get: get
}
});
By looking around it seems that having a service built like this should solve the problem. However, what I obtain with the function get() in controller 2 is always an empty return.
By setting breakpoints I can see that both set() and get() functions enters in their respective function in the service.
Is this a correct procedure to share data between controllers belonging of different routes?
EDIT1
The two views are built in the same ways and the are loaded inside ng-view
<html ng-app="app" ng-controller='controller_1'>
CONTROLLER 1
</html>
First, sharing data between a service is a correct approach.
In your case, you need to ensure the order of getting data is after setting data.
Using a $timeout is not a good approach, i think there should be another way, it depend on your detail code.
If your data is set after some event, you just need to pay attention to the order sequence like 'get after data has been set'
If you have to set data in initialization of controller_1, and controller_2 is sibling of controller_1, you can put the initialization logic of user data before bother controller_1 and controller_2 is entered.
I think you had giving factory reference to both html where first and
second controller you given have. in that case you have to give factory referee to main single page where your also loading sub pages(where you kept ng-view)
The problem occurs because, controller_1 was not created before the creation of controller_2. You can modify the controller_2 to introduce some delay using $timeout:
app.controller('controller_2', function($scope, $timeout, $http, user) {
// The time out is added to check your code working,
// You can replace the code or can use, its up to your requirement
$timeout(function(){
var xxx = user.get();
console.log(xxx);
}, 500);
});
Using $timeout will allow some time for creation of controller_1.
Also instantiate the controller_2:
<html ng-app="app">
<body>
........
<div ng-controller='controller_1'>
<div ng-controller='controller_2'>
</div>
</div>
</body>
</html>
You can use rootscope like below.
app.controller('controller_1', function($scope, $http, $rootScope) {
$rootScope.UserInfo ="Test Example";
});
app.controller('controller_2', function($scope, $http, $rootScope) {
var xxx = $rootScope.UserInfo;
console.log(xxx)
});
I'm working with Angular(1.x) and just encountered a strange behaviour (which I do not exclude being a result of my code).
Here is the setup:
var module = angular.module('module_name');
module.service('service_name', function() {
this.function_name = function() { ... }
});
module.controller('controller_name', ['$scope', 'service_name',
function($scope, service_name) {
$scope.function_name = function() { ... }
}])
And in the view :
<div ng-controller='controller_name'>
<button ng-click="function_name()">Test</button>
</div>
The function in service_name is accessible in the controller via service_name.function_name() as expected. But here is the strange behaviour, (once again it occurs in a more complex setting, not saying this portion of code will reproduce the described scenario)
When clicking the button in the view the function called is not the function_name from the controller but the function_name from the service.
Eventhough they have the same name how can the view access a function directly in the service, shouldn't it be limited to its controller scope ?
This simply can not happen unless and until somewhere in your code you write
$scope.function_name = service_name.function_name
Services do not have any local scope.In angular view side on-click event expects
function in controller's (read local) scope.
What I suspect in your case is , As JS is all reference, You must be doing something like this in large file :
var dummy = service_name.function_name
...
$scope.function_name = dummy
If you do not want to redefine the function name in your controller you can do something like this.
module.service('service_name', function() {
this.function_name = function() { ... }
return {
function_name : function_name
}
});
module.controller('controller_name', ['$scope', 'service_name',
function($scope, service_name) {
$scope.utils = service_name;
}]);
and then in your view call the function directly.
<div ng-controller='controller_name'>
<button ng-click="utils.function_name()">Test</button>
</div>
That's it.
It is better to give your controller and service name using for example
abcCtrl for controller and abcService for service and so on.
Why, because it will be easier for you to call it. and its not confusing
I'm using nested controllers and UI-Router. My top level controller, called MainCtrl, is set in my app's index.html file. If the MainCtrl uses a service, to pass data around, how can I change an instance of an object in the MainCtrl from a child controller without using $scope?
This is basically what I have (typed from memory):
var mainCtrl = function (ProfileSvc) {
var vm = this;
vm.profile = ProfileSvc.profile;
};
var loginCtrl = function (ProfileSvc, AuthSvc) {
var vm = this;
vm.doLogin = function (form) {
if (form.$error) { return; }
AuthSvc.login(form.user, form.pass).
.then(function(response) {
ProfileSvc.profile = response.data.profile;
}, function(errResponse) {
// error
}
};
};
User #shershen posted a reply to another question that gave me the idea to use $scope.$on and an event, however I really do not want references to $scope in my code:
Propagating model changes to a Parent Controller in Angular
I think without using $scope you may want to use the Controller as ctrl in your views. So...
var mainCtrl = function (ProfileSvc) {
var vm = this;
vm.profile = ProfileSvc.profile;
vm.updateProfile = function(profileAttrs) {
vm.profile = ProfileSvc.update(profileAttrs);
}
};
Then in the view, something along the lines of:
<div ng-controller="mainCtrl as main">
<button ng-click="main.updateProfile({ name: 'Fishz' })">
</div>
Hope this helps!
I had to do something similar on a project and ended up using $cacheFactory. First just load it up as a service with something like:
myApp.factory('appCache', function($cacheFactory) {
return $cacheFactory('appCache');
});
Then make sure you inject appCache into your controllers and then in your controllers you can call the cache service's put and get methods to store and retrieve your object.
In my case the parent view and child view both can change the object I'm caching, but the user only can commit from the parent.
I'm trying to lazy-load components. The component is an html fragment with an embedded script tag that contains the controller.
<script>
... controller code .....
</script>
<div>
... template ....
</div>
The fragment is generated in ONE html request so I cannot use templateUrl AND componentURL in the state definition.
I have tried to use the templateProvider to get the component, than extract the script code for the function and register it using a reference to the controllerProvider.
I'm sure there must be a better way to do this than the ugly solution I have come up with. I make a global reference to the controllerpovider, then I read the component thru the templateProvide using a getComponent service. Next I extract the script and evaluate it, which also registers the controller.
See the plunker for the way I'm trying to solve this.
.factory('getComponent', function($http, $q) {
return function (params) {
var d = $q.defer();
// optional parameters
$http.get('myComponent.html').then(function (html) {
// the component contains a script tag and the rest of the template.
// the script tags contain the controller code.
// first i extract the two parts
var parser = new window.DOMParser();
var doc = parser.parseFromString(html.data, 'text/html');
var script = doc.querySelector('script');
// Here is my problem. I now need to instantiate and register the controller.
// It is now done using an eval which of cours is not the way to go
eval(script.textContent);
// return the htm which contains the template
var html = doc.querySelector('body').innerHTML;
d.resolve(html);
});
return d.promise;
};
})
Maybe it could be done using a templateProvider AND a controllerProvider but I'm not sure how to resolve both with one http request. Any help / ideas would be greatly appreciated.
Here's a working plunkr
You do not have access to $controllerProvider at runtime, so you cannot register a named controller.
UI-Router doesn't require named/registered controller functions. { controller: function() {} } is perfectly valid in a state definition
However, if you need the controller to be registered, you could use a tool like ocLazyLoad to register it.
UI-Router links the controller to the template, so there's no need for ng-controllersprinkled in the html.
Here's how I hacked this together. getController factory now keeps a cache of "promises for components". I retained your eval and template parsing to split the response into the two pieces. I resolved the promise with an object containing the ctrl/template.
component factory
.factory('getComponent', function($http, $q) {
var components = {};
return function (name, params) {
if (components[name])
return components[name];
return components[name] = $http.get(name + '.html').then(extractComponent);
function extractComponent(html) {
var parser = new window.DOMParser();
var doc = parser.parseFromString(html.data, 'text/html');
var script = doc.querySelector('script');
// returns a function from the <script> tag
var ctrl = eval(script.textContent);
// return the htm which contains the template
var tpl = doc.querySelector('body').innerHTML;
// resolve the promise with this "component"
return {ctrl: ctrl, tpl: tpl};
}
};
})
myComponent.html
<script>
var controller = function($scope) {
$scope.sayHi = function() {
alert(' Hi from My controller')
}
};
// This statement is assigned to a variable after the eval()
controller;
</script>
// No ng-controller
<div>
Lazyloaded template with controller in one pass.
<button ng-click="sayHi()">Click me to see that the controller actually works.</button>
</div>
In the state definition:
I created a resolve called 'component'. That resolve asks the getComponent factory to fetch the component called 'myComponent' (hint: 'myComponent' could be a route parameter)
When it's ready, the resolve is exposed to the UI-Router state subtree.
The state is activated
The view is initialized
The controller provider and template provider inject the resolved component, and return the controller/template to UI-Router.
Smell test
I should mention that fetching a controller and template in a single html file and manually parsing smells wrong to me.
Some variations you could pursue:
Improve the getComponent factory; Split the component into html/js and fetch each separately. Use $q.all to wait for both fetches.
Use ocLazyLoad to register the controller globally with $controllerProvider
Fetch and store the template in the $templateCache.
I have a directive which is associated with one controller and the functions in my controller defined as
MyFormController.prototype.addNewRow = function addNewRow() {
//Adding row code
};
I want to call this method from another controller, possible ways?
I ve user the service and moved the code into that service which is shared across the controllers, however the service code does the DOM manipulation, and then i guess the next question would be that can we use $compile in a service test case
service or factory is used to share data between controller.so it would be best to define function in service and factory.
demo:
(function() {
angular.module('app', [])
.service('svc', function() {
var svc = {};
svc.method = function() {
alert(1);
}
return svc;
})
.controller('ctrl', [
'$scope', 'svc', function($scope, svc) {
svc.method();
}
]);
})();
You should not!!!
That defeats the whole purpose of modularity.
If possible try to make the function generic and create a service/factory. Now both the places where you need, use the same generic function defined in service and do their stuff.
Otherwise you can also look at events to make changes accordingly.
Look at this blog post:
http://ilikekillnerds.com/2014/11/angularjs-call-controller-another-controller/
Last but the worst solution is (avoid using this, this is literally an aweful way) is catching the element inside directive and getting its scope and taking the function from it.
Example,
var otherControllerFunc = $(".inside-directive").scope().yourfunc;