How do I get a dynamically loaded template and controller injected? After loading my .html partial and .js controller, I would assume the next step is $injector? But how do I use it? something like this...?
My progress so far:
http://plnkr.co/edit/rB5zteYTZ2L1WB5RzlHg
data is returned from a $http.get()
var $injector = angular.injector(['ng']);
$injector.invoke(function($rootScope, $compile, $document) {
$compile(data)($rootScope);
$rootScope.$digest();
});
What format does the Controller.js file need to be in for the injector/compiler to wire it up correctly? Can I simply do what I did in Plunker?
Controller1.js
app.controller('Controller1', function ($scope, $famous) {
console.log("Inside Controller1");
});
Note: I am specifically trying to avoid using requirejs, ng-route, ui-route, ocLazyLoad, etc. I would like to understand the very basics of what these packages accomplish for routing and dynamic loading of a view/controller.
I am not sure if I totally understand your question but it looks like you want to load views and controllers dynamically. I am using a combination of angular ui router and angularAMD. It works very smoothly and with that approach you get a nice separation and on-demand loading.
From angular ui router webpage:
templateUrl: "partials/state1.list.html",
controller: function($scope) {
$scope.items = ["A", "List", "Of", "Items"];
}
With that configuration the specified controller will get loaded and connected to the state1.list.html.
Does that help?
Related
Hello Angular experts,
I have to preload a certain dataset (factory call to the database) to the controller. I don't use angular views so stateProvider or routeProvider cannot be used to resolve. Basically I need the dataset readily available before loading the controller.
Is there a way to achieve this?
I have a controller and a view. The view also has a widget. The widget has an attribute that expects a dataset. By the time the controller is done fetching data the view is already rendered so the widget input parameters are empty. So I need the widget dataset to be filled much before getting to the controller. By the way the app.run solution doesn't work as there is a promise involved.
You can't say as before loading controller, i correct it with before binding controller
angularjs has app.run and i think you know it, it work just when application run (first time) and every time you refresh it.
var app = angular.module("app", []);
app.run(function($http, $rootScope) {
$http.get("url").then(function(response){
console.log(response.data)
$rootScope.data = response.data; // as global scope
})
})
app.controller('ctrl', function($scope) {
$scope.$watch('data', function(newValue, oldValue) {
if(newValue){
console.log(newValue) // you will get `rootscope.data` when it's ready
}
})
})
You can add factory to the app run too.
please fill free to ask question.
Based on your comment, what you are trying to do shouldn't require any changes to the Controller.
What you are actually trying to do is delay rendering of the widget component until you have data that the widget needs. This is commonly handled by ng-if in your HTML.
For Example (pseudocode):
<div ng-if="myData">
<Widget input="myData"></Widget>
</div>
Angular will not render this div until myData has a value, and all your Controller has to do is make sure myData is added to $scope.
I have an angular app where we have 4 different pages.Each page has its own controller. There is an home page which has a controller which routes to each page and its controller using
when('/a',{
templateUrl: './components/a.html',
controller:'aCtrl'
}).
when('/b',{
templateUrl: './components/b.html',
controller:'bCtrl'
}).
when('/c',{
templateUrl: './components/c.html',
controller:'cCtrl'
}).
when('/d',{
templateUrl: './components/d.html',
controller:'dCtrl'
}).
when('/home',{
templateUrl: './components/Home.html',
controller:homeCtrl'
}).
Now I want to share some data or some common functions between these controllers/pages. How can we do this? I googled it they say to use SERVICE. But I don't know in which controller I need to write the service. Can anybody give a good example for this.
A service in AngularJS is not written within a controller. It is bound to your app directly and can be used anywhere within your application. This is why Services are the recommended means of communication between controllers in AngularJS.
What you need to do is write a service like so:
angular.module('yourApp').service('serviceName', function () {....});
Within the service, you can:
Fetch data from an API end point (You can use the $http provider for this)
Define constant data (You can use Angular's constant provider for this)
Define some code that takes in some data and manipulates it and returns new data
Pretty much anything else you want to do with your data
Now, include the service in your controller as a dependency like so:
angular.module('yourApp').controller('yourController', function (serviceName) {
console.log(serviceName.getData());
// Do something with your data
});
Now within this controller, you have access to the data that the service has returned. Of course, the same service can be injected into multiple controllers, thereby making it possible to share data across controllers.
There are many ways you can share data.
event
services
$rootScope
Services provide an easy way for us to share data and functionality throughout our app. The services we create are singletons that can be injected into controllers and other services, making them the ideal place for writing reusable code.
var app = angular.module('app', []);
app.controller('leftCtrl', function ($scope,userService) {
left.btnClicked = function (object) {
userService.saveData(object);
}
});
app.controller('rightCtrl', function ($scope, userService) {
$scope.getData = userService.getData();
});
app.service('userService', function () {
var data = {};
this.saveData = function(object){
data = object;
}
this.getData = function(){
return data;
}
});
Dustin has the right approach. However there are times when you could use a different approach and that is to wrap the application in an AppController.
Everything that is in AppController can now be accessed. You could use this approach to put functions or constants that you want the child controllers of the application to have access to and don't have to inject services everywhere.
<body ng-controller="AppController">
<div ng-view></div>
</body>
I'm using angular-ui router and I have a very simple todo app I'm making to learn more about angular 1.4. If I have my <ui-view></ui-view> tags for my home controller, what is the best way to have a click in the header (to add a task effect my home view + controller?)
(My header HTML just has some anchor buttons, like add task, refresh list)
Header controller :
angular.controller('headerController', ['$scope', function($scope) {
$scope.addTask = function(){
//add a task or show toggle task form or call to something below in home controller
}
}]);
My home controller:
angular.controller('homeController', ['$scope', 'getTasks', function($scope, getTasks) {
$scope.todos = getTasks.load;
}]);
Should a service be made for something so simple? Or is this a job for rootscope, I know that rootscope should be avoided though as it pollutes global namespace. Thank you
I am trying to run an $http function when my AngularJS application first loads.
This $http function needs to finish before any of the controllers in my application could properly function. How would I go about doing this? This sounds like a promise, but it sounds like I would be creating a promise in each controller...
I currently have the function that I want to run first like this:
app.run(function() {
$http.get('link').success(function(data) {
// success function. The data that I get from this HTTP call will be saved to a service.
}).error(function(error) {
});
});
However, sometimes the controller will load before the http call finishes.
The problem
Angular is not dynamic, you cannot add controller dynamically neither factory, etc. Also you cannot defer controller bootstrap, angular loads everything together, and it's quite disadvantage (will be fixed in Angular 2)
The cure
But javascript itself has very important feature - closure, which works anywhere, anytime.
And angular has some internal services that can be injected outside of angular ecosystem, even into browser console. Those services injected as shown below. We technically could use anything else (jQuery.ajax, window.fetch, or even with XMLHttpRequest), but let's stick with total angular solution
var $http_injected = angular.injector(["ng"]).get("$http");
The act
First of all, we defer whole angular app bootstrap, inject http service. Then you make your needed request, receive data and then closure get's to work, we pass received data into some service, or we could also assign in to some angular.constant or angular.value but let's just make demo with angular.service, so when your service has data, bootstrap whole app, so that all controllers get initialized with your needed data
Basically that kind of tasks solved like this
<body>
<div ng-controller="Controller1">
<b>Controller1</b>
{{text}}
{{setting.data.name}}
</div>
<hr>
<div ng-controller="Controller2">
<b>Controller2</b>
{{text}}
{{setting.data.name}}
</div>
<script>
//define preloader
var $http_injected = angular.injector(["ng"]).get("$http");
$http_injected.get('http://jsonplaceholder.typicode.com/users/1').then(function(successResponse) {
//define app
angular.module('app', []);
//define test controllers
//note, usually we see 'controller1 loaded' text before 'settings applied', because controller initialized with this data, but in this demo, we will not see 'controller1 loaded' text, as we use closure to assign data, so it's instantly changed
angular.module('app').controller('Controller1', function($scope, AppSetting) {
$scope.text = 'controller1 loaded';
$scope.setting = AppSetting.setting;
$scope.$watch('setting', function(e1 ,e2){
$scope.text = 'settings applied'
});
});
angular.module('app').controller('Controller2', function($scope, AppSetting) {
$scope.text = 'controller2 loaded';
$scope.setting = AppSetting.setting;
$scope.$watch('setting', function(e1 ,e2){
$scope.text = 'settings applied'
});
});
//define test services, note we assign it here, it's possible
//because of javascript awesomeness (closure)
angular.module('app').service('AppSetting', function() {
this.setting = successResponse;
});
//bootstrap app, we cannot use ng-app, as it loads app instantly
//but we bootstrap it manually when you settings come
angular.bootstrap(document.body, ['app']);
});
</script>
</body>
Plunker demo
You can do this when you configure your routes
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
controller: 'MainCtrl',
templateUrl: 'main.html',
resolve: {
data: ['$http',
function($http)
{
return $http.get('/api/data').then(
function success(response) { return response.data.rows[0]; },
function error(reason) { return false; }
);
}
]
}
});
}]);
Similar question:
AngularJS - routeProvider resolve calling a service method
AngularJS: $routeProvider when resolve $http returns response obj instead of my obj
Heres a plunkr I found using a service, which is what I would recommend.
http://plnkr.co/edit/XKGC1h?p=info
I am new to Angular JS. What I am doing is to bind ui-sref on JQuery loaded data.
All the JQuery plugins and rest of Angular is working perfectly fine. What I have for now looks like:
app.controller("FeedController", ['$scope', '$http', '$compile', function($scope, $http, $compile) {
var feed = this;
feed.years = [];
feed.getYears = function() {
$http.get('/timeline/years').success(function(data) {
feed.years = data;
});
};
feed.getYears();
$scope.$watch('sliderWrapper', function() {
applyTreemap(); // jquery treemap layout plugin
applyKnob(); // jquery knob plugin
});
// I was trying to compile externally loaded DOM by that plugin here.
// Didn't figure out how to do it.
$scope.refresh = function() {
// #slider is main content wrapper
$compile( $("#slider").html())($scope);
};
}]);
Please don't suggest to use AngularJS instead of JQuery. Actually this is a Treemap Layout plugin and already integrated into existing website.
Okay so $compile works as in my code but there are some problems I faced. Here's one. Consider the following code.
<div id="slider">
<div ng-repeat="slide in slides">
<!-- html loaded by jquery ajax will go here -->
</div>
</div>
In angular I was doing
$compile( $("#slider").html())($scope);
So, I was compiling html of #slider in angular and it already has angular bindings besides ajax loaded content. So angular compiler will re-render them and you will run into problems.
So keep in mind that you never $compile html that already has angular bindings.
So I solved my problem by putting
href="#/path/to/state"
instead of doing
ui-sref="home.child()"
into ajax loaded conent.
Sometimes you know something and its not in your mind when you are stuck. :-D