I have noticed in a few tutorials and code examples floating around the internet developers using a global AppController in their applications and modules.
Is it best practice to create a global AppController in AngularJS?
I do see some benefits such as being able to handle events in a "global" scope such as:
app.controller('AppController', function($scope, $rootScope, $route, $location){
$rootScope.$on('$routeChangeStart', function(event, current, previous) {
console.log('Do something...');
});
$rootScope.$on('$routeChangeSuccess', function(event, current, previous) {
console.log('Do something...);
});
});
are there any other advantages or disadvantages to this pattern?
Purely in context of situation. Let's take an example of dynamically changing title tags and page view:
.config(['$routeProvider','$locationProvider',function($routeProvider,$locationProvider){
$routeProvider.when('/', {
template: '/views/home.html',
title:'Home'
});
$locationProvider.html5Mode(true);
}]);
.controller('app', ['$scope','$route','$location',function($scope,$route,$location){
$scope.$on("$routeChangeSuccess",function($currentRoute,$previousRoute ){
$scope.title = $route.current.title;
$scope.page = $route.current.template;
});
}]);
Now both our title and page view are being dynamically loaded in through app level controller that wraps our application. This can be very useful.
<html lang="en" ng-controller="app">
<head>
<title>{{title}}</title>
</head>
<body>
<ng-include src="page"></ng-include>
</body>
</html>
Here's an example of when not to use it. Let's say one of our partial pages return data from an API:
<!-- search.html -->
<div ng-repeat="item in items">
{{item.title}}
</div>
And in our app level controller we are pulling data via broadcast:
$scope.$on('searchComplete',function(d){
$scope.items = d
});
That partial will show the data as we intended however - problems could arise when other child partials use items where scope is being overwritten.
<!-- other-search.html -->
<div ng-controller="OtherSearch" ng-click="search()">
<div ng-repeat="item in items">
{{item.title}}
</div>
</div>
In this partial, ng-click is guiding the users request. So if the app level controller already binded items in the parent, the user will see a list of items when toggling to this partial even if they never triggered the action of search().
Related
Basically I want to achieve like this scenario:
https://i.stack.imgur.com/LLTcK.png
My research led me to $compile, $trustAsHtml,at last directive.
In $compile and $trustAsHtml I can only append static template or only html but can't use dynamic things such ui-sref, ng-click etc.
So, I tried to create directive it is not working and also I am unable to add multiple template on click.
controller :
app.controller('Ctrl', ['$rootScope', '$scope',function ($rootScope, $scope)
{
$rootScope.enableDirective=false;
if(userHasOneApp){// checking some at least one app then only do action
$rootScope.appicon="img_url"; // data which i am passing
$rootScope.appname="App_name"; // data which i am passing
$rootScope.enableDirective=true;
}
}]);
custom directive:
app.directive('headerTemplate', function () {
return {
template:'<a ui-sref="/event" ng-click="editIt()">'
+'<img src="{{appicon}}"></a>'
+'<span>{{appname}}</span>',
scope:{
appname:'=',
appicon:'='
}
};
});
Header view :
<div> class="headerdiv">
<ul ng-if="enableDirective">
<li header-template appicon="appicon">
</li>
</ul>
</div>
Main view :
<div> class="maindiv">
<ui-view></ui-view> <!--basically I want to append template here -->
<button>Add next template</button>
</div>
Where I am doing wrong ?
Well i was facing the same issue
Check out the following link
This will surely help you.
I have implemented this and it worked in my scenario where i wanted to serve a directive when required i.e Lazy Loading of directive
https://www.codeproject.com/Articles/838402/Lazy-loading-directives-in-AngularJS-the-easy-way
I am working on a app that switches between different screens. Each screen collect data from the user by means of user selecting a button and clicking on it or selecting a radio button. There will be at least 5-6 screens. i need ideas on how and where to store the data collected and what's the best way to move between different screens: switch what is displayed inside a div on the page or go to an entirely different page. The data on each screen is coming from the different database tables.
So far I have one screen done - it pulls data from the database(via Web API call) and based on the data pulled, displays a different image in the hyperlink.
What I want to do is capture which hyperlink is clicked by the user and store that value somewhere(a location that will persist until all data is collected and stored in the database) and then move on to the next screen. I am sort of stuck here and need some help and ideas.
here is what i got so far:
UI for screen 1:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Types</title>
<script src="../../Scripts/angular.js"></script>
<script src="TypesCtrl.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="myController">
<h3>Select a type</h3>
<br />
<table>
<tr>
<td ng-repeat="Type in Types">
<img src="Images/type1.png" ng-show="Type.TypeId=='1'" />
<img src="Images/type2.png" ng-show="Type.TypeId=='2'" />
<img src="Images/type3.png" ng-show="Type.TypeId=='3'" />
<img src="Images/type4.png" ng-show="Type.TypeId=='4'" />
</td>
</tr>
</table>
</div>
</body>
</html>
and here is my angular controller:
(function () {
angular.module("myApp", []).controller("myController", TypeCtrlFunction);
TypeCtrlFunction.$inject = ["$scope", "$http"];
function TypeCtrlFunction($scope, $http) {
$http.get('http://localhost:49358/api/myAPI/GetTypes').
then(function (result) {
$scope.DeviceTypes = result.data;
});
};
})();
I'd appreciate some help to get me moving forward
You can share data between the different states/controllers either by setting up a service or by using rootScope. Typically it is better practice to use a service. Then you can change views using uiRouter. An example for setting up states:
app.config(['$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/main");
$stateProvider
.state('main', {
url: '/main',
templateUrl: 'main.html'
})
.state('main.mainstuff', {
url: '/mainstuff',
templateUrl: 'mainstuff.html'
})
.state('main.secondstuff', {
url: '/secondstuff',
templateUrl: 'secondstuff.html'
});
}]);
Here is a plunker showing how implement using nested states. This example uses rootScope but you should consider using a service as AndyHasIt mentioned.
You coude use ui-routing for swithing between different views. As for storing data there are two ways i would do it. One is using services to store values or you could also use local storage or cookies in browser for the same
To share data between controllers, use a service:
app = angular.module('MyApp', []);
app.service('SharedDataService', function() {
return {
someVar: 'hello'
}
})
app.controller('Controller1', function(SharedDataService, $scope) {
$scope.value1 = SharedDataService.someVar;
})
app.controller('Controller2', function(SharedDataService, $scope) {
$scope.value1 = SharedDataService.someVar; //The same value
})
To load different pages within the same page I suggest you use ngRoute or uiRouter
I am beginner in angular js
HTML
<div ng-app="myapp">
<div ng-controller="maincontrol">
<div ng-show="!vis">show</div>
<div ng-show="vis">hide</div>
</div>
<div ng-view></div>
</div>
JS
var app = angular.module('myapp', ['ngRoute'])
app.controller('maincontrol', function ($scope) {
$scope.vis = true;
$scope.fun = function () {
if ($scope.user == "home" && $scope.pass == "home") {
console.log($scope.user, $scope.pass);
$scope.vis = false;
}
}
})
app.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'home.html'
})
.when('/contact', {
templateUrl: 'contact.html'
})
});
and also i have two html pages like
home.html
<div ng-controller="maincontrol">
<input ng-model="user"/>
<input ng-model="pass"/>
<div ng-click="fun()">
click
</div>
</div>
contact.html
<div>
contact
</div>
my expectation is after entering home into user and pass. if i click 'click' i need to show 'show' label instead of 'hide'. pls help me.
Each controller has its own scope, when you wrote $scope.vis=false on fun(), you actually created a new variable on maincontroler1 scope. If you expected this variable to affect the view which is binded to maincontroler scope, it won't happen.
I suggest 2 options:
You can use one controller for entire app (If you use same controller in two tags it will still create a new scope although it is the same controller), this way the fun() method that was called from the first view will change the boolean in the single controller and will affect the second view. Please note when you use ng-view you will have to get the variable from the parent.
So I used this code:
$parent.user
$parent.pass
Create this working plunker for you.
Share the vis boolean between 2 controllers using a service. You can
use this post for this option.
You can also use reach parent controller scope from child controller, that can be done if ng-view will be nested in the outer controller. You can use this post for option 3.
I am using angularjs and the angular ui router to load nested templates. The child's controller inherits the parent's $scope for it's contents. This is all working fine except when I try to load the child scope as the initial view, the child scope is empty.
www.domain.com/products - parent view - works
www.domain.com/products/product-child view - works when navigating from parent.
When I try to load www.domain.com/products/product directly, the child template appears to load before the parent is complete leaving the child scope empty.
Here is the ui router info.
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state('products', {
url: "/products",
templateUrl: "/views/products.html",
controller: "ProductsCtrl"
})
.state('products.product', {
url: "/:description",
templateUrl: "/views/product.html",
controller: "ProductCtrl"
})
});
What am I missing?
Parent View:
<section class="product-container">
<a ui-sref="products.product({description:product.description})" ng-repeat="product in products | orderBy : orderObjectBy(products,'active',true)" class="product" ng-click="" ng- class="{inactive:!filterProducts(product, products.keywords) && isFilterChecked() , 'selected- product':(product.selected)}" ng-style="{left: ((product.pos_left*360) + 'px'),top: ((product.pos_top*360) + 'px')}">
<img ng-src="http://www.tiempotimepiece.com/catalog/{{product.SmallImageUrl}}">
<p>{{product.description}}</p>
{{product.price | currency}}
<p>{{product.order}}</p>
</a>
<div ui-view></div>
</section>
Child View:
<div ng-repeat="product in items | filter: {description:description}">
Sku:{{product.Sku}}<br>
IdProduct:{{product.IdProduct}}<br>
price:{{product.price}}<br>
description:{{product.description}}<br>
SmallImageUrl:{{product.SmallImageUrl}}<br>
</div>
The child controller inherits the parent $scope by using $scope.$parent. When loading the child view as the initial page, the child view/controller appears to load before the parent has completed populating the scope.
What am I missing here? Thanks!
UPDATE: I do think a service is the quickest fix here and probably the best solution, but I am adding links to the Gists of the two controllers if anybody is interested in looking into this further to see if that is the best approach. These are a work in progress and have not been refactired at all and are frankly a mess at this point. There is a lot of crazy filtering and sorting going on.
Products (Parent) : https://gist.github.com/bennewton999/8257080
Product (Child) : https://gist.github.com/bennewton999/8257058
Ok, so I came in Monday morning with a fresh mind and realized I didn't need a different controller for this view at all. I just changed the controller to use the products controller for the product view and that was it.
Why isn't {{ username }} variable (in example below) updated when I'm navigating using in-page links? The variable is only set on initial page load (equal to Anonymous) and isn't updated no more, why is that? Code i plain and simple (http://jsbin.com/OTApeYI/1/):
<!doctype html>
<html lang="en" ng-app="examp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"> </script>
</head>
<body ng-controller="ExampleCtrl">
Home, Admin
Your user name is: {{ username }}
<script>
angular.module('examp', ['examp.controllers']).
config(function($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider.when('/:username', {controller: 'ExampleCtrl'});
$routeProvider.otherwise({redirectTo: '/'});
});
angular.module('examp.controllers', [])
.controller('ExampleCtrl', ['$scope', '$routeParams', function($scope, $routeParams) {
$scope.username = $routeParams.username ? $routeParams.username : "Anonymous";
}]);
</script>
</body>
</html>
First, I think you need the ngView directive to activate routing. Second, ExampleCtrl is only created once and you are setting $scope.username to the current value at that time. $routeParams.username is a simple string, you might as well set $scope.username = '' You can however store the $routeParams object on your scope and bind to the username property (SAMPLE). I also disabled html5 or I don't think it works with jsbin.
<div ng-view></div>
<p>Your user name is: {{ username }}</p>
<p>params.username is: {{ params.username }}</p>
<p>eventusername is: {{ eventusername }}</p>
<p>JSON:</p><pre>{{ params|json }}</pre>
And in your controller:
$scope.params = $routeParams;
Another option would be to look for route change events and handle them:
$scope.$on("$routeChangeStart", function(event, next, current) {
$scope.eventusername = next.params.username;
});
Because you're always using the 'ExampleCtrl' controller, you're only ever reading the $routeParams once. The $routeParams object is only created the one time that the controller is created. It doesn't update dynamically. So even though your route may have changed from say /macadamia to /macadamia123, the $routeParams object is still the same as it was when the controller was created for your /macadamia route (so $routeParams.username still equals '/macadamia'). You need to reload the controller in order to read the $routeParams again.
I've wanted the $routeParams object to be dynamic for a long time, but unfortunately angular's $routeProvider doesn't work that way.