Just wanted to know if there is any way to resolve data for a controller not assigned with any route (BaseController common for all actions)
App.controller('BaseCtrl',function($http, Service){
$scope.data = null;
Service.getData(function(data){
$scope.data = data;
});
});
Can I can resolve Service.getData(); before the base controller is loaded and inject the same into it like
var Base = App.controller('BasCtrl', function($http, BaseData){
$scope.data = BaseData;
});
Base.resolve = {
BaseData:function(Service){
return Service.getData();
}
}
Please help if it is possible.
PS: I just want to prepare the data from service before the BaseCtrl is loaded and inject the same into it.
If your BaseCtrl is loaded with your home page then you have to fetch data before loading the javascript page containing BaseCtrl and put that into window.BaseData, other wise whichever controller is loaded first load data in there.
If basectrol is first page then you can implement it by immediately-invoked function in separate JS page and put that JS before BaseCtrl js file.
e.g. fetchdata.js
(function() {
window.BaseData= function() {...});
})();
You can access BaseData from your angular controller through Global parameter injected to your controller.
Now put files this order
<script src= "fetchdata.js"></script>
<script src="basecontroller.js"></script>
You see you have to break the rule to do that, but anyway Angular $resource in your Service is also nothing but Ajax implementation.
Related
I want to know what's that proper way to handle the business logic in terms of loading the appropriate views and controllers.
I have a bunch of factories to load resources, read and write user progress (to a local file) and so forth. The user will spend no more than several seconds on a given view (there are 6-7 different ones in total) and will switch to another one with dynamically loaded resources based on his progress.
My current idea is to have a service/factory that keeps track of progress, which is loaded on the index page and then every controller sends a request to it, once it's finished. Thereafter the service changes the $state and loads the appropriate data.
I'm building my first app in AngularJS and I have tried to search StackOverflow and Google, but I still can't figure out how to approach this problem.
Even pointing me to the right direction or reading material would be greatly appreciated.
If you are creating AngularJs app first time then follow simple steps.
1. Create one index.JSP file where you should run your app using ng-app directive and add all scripts and files.
2. create one Js file app.js.
add all module name in app.js and run your js using .run method.
3. maintain services, controllers and filters, directives, templates seperately in different folders and in different files.
And dont forget to add module name in app.js and add path in index.jsp
4. In your services files write only sharing business logic.
All other business logic related to particular file write it in controller.
Here your are maintaining ajax calls so dont meshup it with controllers.
.Service
.factory('angularService', function () {
return {
// Write business logic
}
})
Declare $starteProvider and define .states in your controller.
For ex.
$stateProvider.state('xyz_state', function () {
// add url,
// templateUrl,
// controller
})
.controller('myFirstController', function () {
// Add your business logic
// scope variables
});
6. Maintain view pages separately.
7. Maintain Directives and Filters separately.
In angular views are by default loaded through ajax call when it is required. If you want to do the same in controller then use require.js. which will load the controller dynamically when it is required. In require.js you can also specify your other dependency library for every view & controller which will loaded through ajax when view is called.
index.html
<script data-main="js/main.js" type="text/javascript" src="js/require.js"></script>
main.js
require.config({
urlArgs: 'v=1.0',
});
require(
[
'app'
],
function() {
angular.bootstrap(document, ['UConnect']); //Add your module
}
);
app.js
'use strict';
define([],function() {
var app = angular.module('UConnect',['ngRoute','ngAnimate','ui.materialize','yaru22.angular-timeago']);
app.config(['$routeProvider', '$controllerProvider', '$provide',function($routeProvider, $controllerProvider, $provide) {
// Register your component
app.register = {
controller: $controllerProvider.register,
factory: $provide.factory,
service: $provide.service
};
// Add resolver for load controller through require.js
function resolveController(dependencies) {
return {
load: ['$q', '$rootScope', function ($q, $rootScope) {
var defer = $q.defer();
require(dependencies, function () {
defer.resolve();
$rootScope.$apply();
});
return defer.promise;
}]
}
};
$routeProvider
.when("/Pages", {
templateUrl : "templates/Pages.html",
controller: 'PagesCtrl', // Add controller name.
resolve: resolveController(['controller/PagesCtrl']) // Call resolver to load controller.
})
.when("/ContactUs", {
templateUrl : "templates/ContactUs.html",
controller: 'ContactUsCtrl',
resolve: resolveController(['controller/ContactUsCtrl'])
})
;
$routeProvider.otherwise('/Pages');
}]);
angular.element(document).ready(function () {
angular.bootstrap(document, ['Uconnect']);
});
return app;
});
I usually use the $templateCache & store all my views there. if your on Node environment i'd suggest add ng-html2js to your build process.
as for controllers & other JS you should minify and concat all and load just that file in the beginning.
I am trying to fetch some data from server before controller get render.
I have found many answers for it with respect to routeProvider.
But my main issue is my controller does not bound with any route.
So is there any way to make this possible?
I have controller in following ways...
<!-- HERE I WANT TO BLOCK RENDERING TILL DATA GET LOAD -->
<AppController>
<ng-view>
</AppController>
It sounds like a resolve is what you are looking for, but if you are not using a routing table for this controller, you'll not have this option. Why not just resolve an asynchronous call in your controller, and set scope variables inside the callback. This is what I interpret your desire to await controller "rendering", whereas a resolve through a route table would await controller instantiation. Observe the following...
module.controller('ctrl', function($scope, $http) {
$http.get('/uri').then(function(response) {
// set $scope variables here
});
console.log('executed first');
});
You could also set a variable to prevent the associated view from rendering if your data call is lengthy. This would prevent the UI from "dancing." Observe the following changes to the above example...
<div ng-controller="ctrl" ng-show="resolved"></div>
module.controller('ctrl', function($scope, $http) {
$http.get('/uri').then(function(response) {
$scope.resolved = true; // show rendering
});
});
JSFiddle Link - simplified demo
JSFiddle Link - demo ng-if
One idea will work
in html controller:
<p ng-if="notLoadedContent">Wait</p>
<div ng-if="!notLoadedContent">Content fetched</div>
And in Controller all controller is inside one function will start all, and the controller will be :
fetch(init)
$scope.notLoaded = true;
function init(){
$scope.notLoaded=false;
}
hope it help you
I am using a jsonn object to load in data between objects. currently using a factory to return the object and bind it between the controllers. Right now I am making a copy like so :
var LevelsHere = $http.get("my.json")
.success(function(data){
var dataCopy = angular.copy(data);
return dataCopy;
});
return {
all: function() {
return LevelsHere;
}
};
This works fine, but I have a button that I want to call this function and refresh it so It gets a clean copy from the my.json (so any changes are reverted).
Just to clarify, in each controller I call it in into a scope within the controller like so
UserService.all().then(function(data){
$scope.storeHere= data.data;
});
I am thinking maybe something like a $rootscope might be the way to go because I am sharing between controllers. So - have the root scope (which is a copy of the json) be shared between controllers. Then when I press my refresh button it would refresh that $rootscope with a fresh copy of my.json so changes would revert back.
Maybe I could use the method I am trying now? I tried the having the refresh button call the $get again but it wasnt binded to both places so it was only refreshing in one controller.
To quickly review - I have json I'm bringing in and using in 2 controllers with a factory calling it. I want to be able to refresh that so it refreshes in both places.
Here is my attempt at the refresh :
$scope.cancelProcedure = function() {
//refresh data
UserService.all().then(function(data){
$scope.levels = data.data;
};
The problem with this is it calls the current data, and doesn't refresh with a new call. I'm not sure how to make it refresh in both places. Thanks!!
To give you an answer I assume the following:
There's one resource you want to get my.json and update from time to time.
You want to access and update the data from two (or more) controllers
You don't want to pollute your $rootScope
In that case the ideal solution would be to store the method to get/update the data in the factory as well as the current data. In each controller, where you need that data, you simply inject the factory and assign it to the $scopeof that controller.
Here's one example:
angular.factory('dataFactory', ['$http', function ($http) {
var dataFactory={};
dataFactory.currentData = null;
dataFactory.update = function () {
return $http.get("my.json")
.success(function(data){
dataFactory.currentData = data;
return data;
});
};
return dataFactory;
}]);
angular.controller('firstCtrl', ['$scope', 'dataFactory', function ($scope, dataFactory) {
$scope.data = dataFactory;
}]);
angular.controller('secondCtrl', ['$scope', 'dataFactory', function ($scope, dataFactory) {
$scope.data = dataFactory;
}]);
In your HTML you can then use e.g. ng-bind="data.currentData" and ng-click="data.update()".
Further thoughts: If you don't want to put the factory in your controllers $scope you might even consider to further break down your logic and create one or two directives that are based on that factory. If that makes sense is not easy to tell with the given information, however.
I've been working on a Chromium extension which sends data from a content script to another extension page which runs an Angular app. Since I separated my app into several controllers relying on a data service, I've run into a bug where sometimes my app's templates will all display correctly but other times it will not. In this simplified example, my app might display the data value on the page on a run and then not display it after refreshing the page and running the same code.
In the case when the data is not displayed, I can still inspect the DataService object at runtime and find the data value to be instantiated correctly.
app.js
angular.module('angularApp', [])
.controller('AppController', ['$scope', 'DataService',
function($scope, dataService) {
$scope.dataService = dataService;
}]);
service.js
var angularApp = angular.module('angularApp');
angularApp.service('DataService', function() {
var data = [];
chrome.runtime.onMessage.addListener(function(message, sender) {
var messageData = $.extend([], message.data);
// populate data array based on messageData
});
};
view.html
<html ng-app="angularApp">
<body ng-controller="AppController">
{{dataService.data}}
</body>
</html>
This seems to be an issue with the asynchronous arrival of the message from the content script, but I'm not sure of the appropriate way to solve this within my Angular service. An ideal answer would explain what is going on to cause my bug and the best practice to build my service and controllers to work as expected every time.
Your controller and view are fine. Your service should work if you do this:
angular.module('angularApp').service('DataService',['$timeout',
function($timeout){
var self = this;
this.data = [];
var tmp;
function handleMessage(){
//tmp == message.data
//populate self.data however..
}
chrome.runtime.onMessage.addListener(function(message,sender){
tmp = message.data;
$timeout(handleMessage);
});
}]);
The reason it isn't working currently is because the event handler isn't executing in angular's event loop. By doing the data manipulation in a $timeout it will force angular to update the view with the data that has changed.
Using $timeout essentially has the same effect as $scope.$apply() in this scenario
I have an AngularJS app which grab data from PHP via AJAX and permit user to edit it through few steps.
Structure of the app is very simple :
I have a main controller which is loaded from ng-controller directive.
<div ng-controller="MainCtrl">
<!-- All my app take place here, -->
<!-- so all my others controllers are in MainCtrl scope -->
<div ng-view></div>
</div>
I have one controller by editing steps (ex. general info, planner, validation, etc.). Each controller is loaded by the $routeProvider (inside MainCtrl scope).
My problem is when I load (or refresh) the page, MainCtrl make an AJAX request to retrieve data to edit. The controller attached to $routeProvider is loaded before AJAX request is finished, so I can't use data grabbed by MainCtrl.
I want to defer $routeProvider loading route while AJAX request is not ended. I think I have to use the $q provider, but I can't prevent route loading.
I have tried this (in MainCtrl) and controller is still rendered premature :
$scope.$on('$routeChangeStart', function(event, current, previous) {
$scope.pathLoaded.promise.then(function() {
// Data loaded => render controller
return true;
});
// Stop controller rendering
return false;
});
And AJAX call is defined like this :
$scope.pathLoaded = $q.defer();
// Edit existing path
$http.get(Routing.generate('innova_path_get_path', {id: EditorApp.pathId}))
.success(function (data) {
$scope.path = data;
$scope.pathLoaded.resolve();
})
.error(function(data, status) {
// TODO
});
So the question is : is it the good way to achieve this ? And if yes, how can I defer controller rendering ?
Thanks for help.
You can use the resolve property of routes, execute the AJAX there and pass the result to your controller. In the route definition:
$routeProvider.when("path", {
controller: ["$scope", "mydata", MyPathCtrl], // NOTE THE NAME: mydata
templateUrl: "...",
resolve: {
mydata: ["$http", function($http) { // NOTE THE NAME: mydata
// $http.get() returns a promise, so it is OK for this usage
return $http.get(...your code...);
}]
// You can also use a service name instead of a function, see docs
},
...
});
See docs for more details. The controller for the given path will not be called before all members in the resolve object are resolved.