I am working on angular js components there are two components that will called when their respective button is clicked
Issue: when the page loads both components are initialized , but in real they should be initialized when I click on the respective component
example:
index.html
<div class='breadcrumbs'>
<div class='inner'>
<ul class='cf' style=" margin-top: 0px;">
<li ng-click="WizardsViewerCtrl.createFirst()">
</li>
<li ng-click="WizardsViewerCtrl.createSecond()">
<span>Arrange Questions</span> </a>
</li>
</ul>
</div>
</div>
<div ng-show="viewFirstRoute">
<first-component></first-component>
</div>
<div ng-show="viewSecondRoute">
<second-component></second-component>
</div>
index.js
$scope.viewFirstRoute=false;
$scope.viewSecondRoute=false;
function createFirst() {
$scope.viewFirstRoute=true;
$scope.viewSecondRoute=false;
}
function createSecond() {
$scope.viewFirstRoute=false;
$scope.viewSecondRoute=true;
}
problem is when this page index.html is loaded ,both components are initialized
first.component.js
angular
.module('AOTC')
.component('firstComponent', {
templateUrl: 'first.html',
controllerAs: 'firstCtrl',
controller:(function ($scope) {
console.log("first component loaded");//it prints while index.html is loaded even i didn't clicked on this component
})});
second.component.js
angular
.module('AOTC')
.component('secondComponent', {
templateUrl: 'second.html',
controllerAs: 'secondCtrl',
controller:(function ($scope) {
console.log("second component loaded");//it prints while index.html is loaded even i didn't clicked on second component
})});
summarizing: two components on same route must be initialized when I click on the respective component , but behavior noticed components are loaded as soon as route is loaded it doesn't care about the click, see comments in the JS file
Use ng-if instead of ng-show. it should fix the issue.
<div ng-if="viewFirstRoute">
<first-component></first-component>
</div>
<div ng-if="viewSecondRoute">
<second-component></second-component>
</div>
ng-show: it will add component to DOM but set its display property to none.
ng-if: Will add or remove component from DOM conditionally.
Related
I am working of AngularJs v 1 app with ui routing.
My question simply how to set the home page active without clicking the ui-sref link.
I tried with ng-class="active" but it doesn't achieve the task.
<script>
angular.module("myApp",['ui.router'])
.config(function ($stateProvider,$urlRouterProvider,$locationProvider) {
$stateProvider
.state("home",{
url:"home",
views:{
'main':{templateUrl:"home.html"}
}
});
$locationProvider.hashPrefix('');
$urlRouterProvider.otherwise("/home");
</script>
<div class="container" style="margin-top: 60px">
<div ui-view="main"> </div>
</div>
Home page
<div class="row" style=" margin-top:100px; " ng-app="app" ng-class="active">
<h1>Home</h1>
</div>
What you are looking for is ui-sref-active
From the doc
A directive working alongside ui-sref to add classes to an element when the related ui-sref directive's state is active, and removing them when it is inactive. The primary use-case is to simplify the special appearance of navigation menus relying on ui-sref, by having the "active" state's menu button appear different, distinguishing it from the inactive menu items.
It will add the active for you if you're currently on the right state.
Markup should look something along the line of
<div class="some-navigation-class">
<a ui-sref="home" ui-sref-active="active">Home</a>
<!-- more nav goes here -->
</div>
I am trying to load different form based on user interaction in single page application. ng-view was helpful until i had to load/hide forms in different divs of same page.
div#1: it will have catalog names populated from ng-repeat.
div#2: should populate forms ( order / schedule / list ) based on button click from top nav.
div#3: should only populate sub catalog list when user selects catalog in div#1.
index.html
<div class="left_column">
<ul>
<li ng-repeat="catalog in catalogs">{{ catalog }}</li>
</ul>
</div>
<div class="top_row">
<ng-view></ng-view>
</div>
<div class="bottom_row">
<ng-view></ng-view>
</div>
app.js
myApp.config(function($routeProvider){
$routeProvider
.when('/orderForm', {
templateUrl: '/orderForm.html',
controller: 'orderFormController'
})
.when('/scheduleForm', {
templateUrl: '/views/html/parameterForm.html',
controller: 'parameterFormController'
})
.when('/subCataloglist', {
templateUrl: '/subCataloglist.html',
controller: 'subController'
})
});
How can i load different forms at a time in single page ? is there any better example for multi view logic ?
I think that this attempt isn't correct.
I have seen only one ng-view, which could change class attached according to view url.
But here i propose much simpler architecture.
Use one view. On this view do ng-repeat in div1 as it was.
in div2 do a ng-if statement and connect it with clicking on buttons.
div three simillar - you can use ng-show or ng-if. ng-if doesn't render in dom, ng-show renders but hide.
<div class="top_row">
<form id="form1" ng-if="selval=1">
</form>
<form id="form2" ng-if="selval=2">
</form>
</div>
menu:
<ul>
<li>order</li>
<li>schedule</li>
controller attached to current html view:
$scope.sel = function(n){
$scope.selval = n;
}
As two-way binding is implemented in angular, it will work automatically.
Can I have some constant part of the page in AngularJS, like logo, side menu, breadcrumb?
The question is how to have any part constant and navigate only content part?
Is the only way is to include html fragments?
<html>
<head>
</head>
<body ng-app="">
<div>
<p>CONSTANT PART</p>
</div>
<div ui-view>
content use ui router for changing content configure routes in app.js
</div>
</body>
</html
use ui-router the content in ui-view changes according to the state
https://github.com/angular-ui/ui-router
Angular Route Segment
bower install angular-route-segment
The library provides two pieces of code: $routeSegment service and app-view-segment directive. Both are placed in their own modules which you must include as dependencies in your app module:
var app = angular.module('app', ['ngRoute', 'route-segment', 'view-segment']);
$routeSegmentProvider.segment('s1', {
templateUrl: 'templates/section1.html',
controller: MainCtrl});
$routeSegmentProvider.within('s1').segment('home', {
templateUrl: 'templates/section1/home.html'});
$routeSegmentProvider.within('s1').segment('itemInfo', {
templateUrl: 'templates/section1/item.html',
controller: Section1ItemCtrl,
dependencies: ['id']});
$routeSegmentProvider.within('s1').within('itemInfo').segment('overview', {
templateUrl: 'templates/section1/item/overview.html'});
Then, any app-view-segment tags (which are similar to built-in ng-view) in the DOM will be populated with the corresponding route segment item. You must provide a segment index as an argument to this directive to make it aware about which segment level in the tree it should be linked to.
index.html:
<ul>
<li ng-class="{active: $routeSegment.startsWith('s1')}">
Section 1
</li>
<li ng-class="{active: $routeSegment.startsWith('s2')}">
Section 2
</li>
</ul>
<div id="contents" app-view-segment="0"></div>
Where ul code is static and will be displayed on every page
AngNoob here. I have some global navigation that uses the routeProvider to swap out external html pages inside the view. Within the view i set up a list type sub navigation (created with ng-repeat) that switches out divs in the external html file. I can get it to load up the page if I set it manually in the appCtrl:
//Here I set the initial value
$scope.page = 'Comfort Homes of Athens';
But when I click on the span that has the ng-click. I get nothing. I started to think it was a scope issue but when i put just an ng-click='alert()' it does nothing either.
I have read around other posts but most seem to be putting a ng-click inside of an ng-switch rather than the reverse. and aren't using routing in their examples either. Still new to angular so maybe its something I haven't come across yet.
App HTML:
<body ng-app="app">
<header ng-include="header.url" ng-controller="nav"></header>
<article ng-view></article>
<footer ng-include="footer.url" ng-controller="nav"></footer>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.16/angular.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.16/angular-route.js"></script>
<script type="text/javascript" src="js/data.js"></script>
<script type="text/javascript" src="js/model.js"></script>
</body>
External HTML File:
<div id="web" class="wrapper">
<aside class="boxModel">
<div id="controller" class="container">
<div class="topBox bluebg subNavBar"><h1 class="white">Projects</h1></div>
<div ng-controller="nav" id="controls" class="botBox whitebg">
<span ng-repeat='item in webProjects' ng-click="page='{{item.name}}'">{{item.name}}</span>
</div>
</div>
</aside><section ng-switch on="page" class="boxModel">
<div ng-switch-when="Comfort Homes of Athens" id="sandbox" class="container round box whitebg">
<h1>Here is link 1</h1>
</div>
<div ng-switch-when="Sealpak Incorporated" id="sandbox" class="container round box whitebg">
<h1>here is Link 2</h1>
</div>
</section>
</div>
JS:
var app = angular.module("app", ["ngRoute"]);
function nav($scope) {
$scope.templates = templates;
$scope.header = $scope.templates[0];
$scope.footer = $scope.templates[1];
$scope.mainNav = mainNav;
$scope.footNav = footNav;
}
app.config(function($routeProvider) {
$routeProvider.when('/',{
templateUrl: "templates/home.html",
controller: "AppCtrl"
}).when('/templates/web.html',{
templateUrl: "templates/web.html",
controller: "AppCtrl"
}).when('/templates/seo.html',{
templateUrl: "templates/seo.html",
controller: "AppCtrl"
}).otherwise({
template: "This doesn't exist!"
});
});
app.controller("AppCtrl", function($scope) {
$scope.webProjects = webProjects;
$scope.seoProjects = seoProjects;
//Here I set the initial value
$scope.page = 'Comfort Homes of Athens';
});
Unfortunately for you, ng-repeat creates child scopes which are siblings with each other and children of your parent controller (ng-controller="nav") while your <section> where ng-switch is on is not child scope of your ng-controller="nav", but AppCtrl.
You could try ng-click="$parent.$parent.page=item.name" just to understand scopes in angular.
<div id="web" class="wrapper">
<aside class="boxModel">
<div id="controller" class="container">
<div class="topBox bluebg subNavBar"><h1 class="white">Projects</h1></div>
<div ng-controller="nav" id="controls" class="botBox whitebg">
<span ng-repeat='item in webProjects' ng-click="$parent.$parent.page=item.name">{{item.name}}</span>
</div>
</div>
</aside><section ng-switch on="page" class="boxModel">
<div ng-switch-when="Comfort Homes of Athens" id="sandbox" class="container round box whitebg">
<h1>Here is link 1</h1>
</div>
<div ng-switch-when="Sealpak Incorporated" id="sandbox" class="container round box whitebg">
<h1>here is Link 2</h1>
</div>
</section>
I don't recommend using this solution as it's quite ugly. The solution of #link64 is better, but I think the inheritance of model is so implicit and creates a tightly-coupled code. Here I propose another solution which I hope is better by emitting an event:
<span ng-repeat='item in webProjects' ng-click="$emit('pageChange',item.name)">{{item.name}}</span>
I'm not sure if angular is able to resolve $emit('pageChange',item.name) expression in the template. If you run into any problems, you could write inside your controller:
<span ng-repeat='item in webProjects' ng-click="setPageChange(item.name)">{{item.name}}</span>
In your nav controller:
$scope.setPageChange = function (pageName) {
$scope.$emit("pageChange",pageName);
}
In your AppCtrl, listen to the event and update the page.
app.controller("AppCtrl", function($scope) {
$scope.webProjects = webProjects;
$scope.seoProjects = seoProjects;
//Here I set the initial value
$scope.page = 'Comfort Homes of Athens';
$scope.$on("pageChange", function (event, newPage){
$scope.page = newPage;
}
});
In addition to #KhanhTo's answer, I wanted to point you toward another tool to use instead of ngRoute; UI-Router. This is not the answer to your original question, but it is a better solution that avoids your issue entirely.
UI-Router enhances the page routing of ngRoute and is more centered around states. You transition to states that have templates and optional controllers. It emits its own events such as $stateChangeStart or $stateChangeSuccess. You can invoke these state transitions with the function command $state.go(stateName) or by a directive ui-sref="my.state({name: item.name})
UI-Router is a very powerful tool and I cannot go into all the details here but the documentation and community is great.
A simple rewrite of your code could look like the following.
Template for web.html
<div class="wrapper">
<aside class="boxModel">
<div id="controller" class="container">
<div class="topBox bluebg subNavBar"><h1 class="white">Projects</h1></div>
<div ng-controller="nav" id="controls" class="botBox whitebg">
<span ng-repeat='item in webProjects' ui-sref="app.web.page({name: {{item.name}})">
{{item.name}}
</span>
</div>
</div>
</aside>
<section class="boxModel">
<div ui-view class="container round box whitebg">
<!-- Page content will go here -->
</div>
</section>
</div>
JavaScript
app.config(function($stateProvider) {
$stateProvider
.state('app', {
abstract: true,
template: '<div ui-view></div>', //Basic template
controller: "AppCtrl",
}).state('app.home', {
templateUrl: "templates/home.html",
url: '/home'
}).state('app.web',{
templateUrl: "templates/web.html",
url: '/web'
}).state('app.web.page',{
templateUrl: "templates/page.web.html",
url: '/web/page/:name' //Note here the ':' means name will be a parameter in the url
}).state('app.seo',{
templateUrl: "templates/seo.html",
url: '/seo'
});
});
app.controller('AppCtrl', function($scope){
$scope.webProjects = webProjects;
$scope.seoProjects = seoProjects;
$scope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams){
if(newState.name == 'app.web.page'){
var pageName = newStateParams.name; //Variable name matches
$scope.linkText = fetchPageContent(pageName);
}
});
});
Template for page.web.html
<h1>{{linkText}}</h1>
With these changes you will be able to reuse the same instance of your controller. In addition to allowing your paging content to be more scalable.
Notes on $scopes
Every $scope has a parent except for the $rootScope. When you ask for an object in the view, it will look at its $scope to find the reference. If it does not have the reference, it will traverse up to its parent scope and look again. This occurs until you get to the $rootScope.
If you assign something to the $scope in the view, it will assign it to the current $scope as opposed to searching up the $scope chain for an existing property. That is why ng-click="model.page = ..." works; it looks up the $scope chaing for model and then assigns to the page property whereas ng-click="page = ..." assigns directly to the current $scope.
Notes on Controller re-use
To my knowledge, ngRoute does not support nested views. When you go to a new route, it will destroy the current view and controller as specified in the $routeProvider and then instantiate a new controller for the new view. UI-Router supports nested states (i.e. child states with child $scopes). This allows us to create a parent controller that can be re-used amongst all the child states.
I think this may be related to some misunderstanding of how scope works.
ng-repeat creates its own scope. When attempting to set page, angular creates it on the scope of the ng-repeat.
In your AppCtrl, create an object on the scope as follows:
$scope.model = {};
$scope.model.page = 'Comfort Homes of Athens';//Default value
On your ng-click, refer to model.page instead of just page. Angular will then traverse up the scope to find model.page instead of just create a property on the local scope of the ng-repeat.
<span ng-repeat='item in webProjects' ng-click="model.page='{{item.name}}'">{{item.name}}</span>
Also, your AppCtrl is going to be recreated every time you change pages. You should probably use a service to persist the state between page changes
Question:
How can I add a "Login" view/route to my angular app that hides an element that is outside the ng-view DOM?
Situation:
In my Angular page, I have a navigation tree view on the left and the main view in the center:
<div ng-app="myApp">
<div class="col-sm-3" ng-controller="TreeController">
<div treeviewdirective-here>
</div>
</div>
<div class="col-sm-9 content" ng-view="">
</div>
</div>
Each node in the treeview changes the location using something like window.location.hash = '#/' + routeForTheClickedItem;.
Using the standard routing, this works great, i.e. the tree is not reloaded each time, but only the main "window".
Problem:
I want to add a login functionality with a login view. For this view, the treeview should not be visible - only after the login. To achieve this with the normal routing, I know I could move the ng-view one level up, i.e. embed the treeview into each view - but this would result in the treeview being reloaded with every route change.
Is there an easy alternative that allows me to check what page is displayed in the ng-view? Or check some other variable set during the routing? Then I could use something like:
<div class="col-sm-3" ng-controller="TreeController" ng-show="IsUserLoggedIn">
You could listen for a routeChangeSuccess outside ng-view
$scope.$on('$routeChangeSuccess', function (event, currentRoute, previousRoute) {
//do something here
});
hope that helps, you can catch me on angularjs IRC - maurycyg
You could define a controller at the top div level.
Something like:
<div ng-app="myApp" ng-controller="MainController">
and in MainController inject a Session. Something like Session is enough to decide whether to show the tree.
Here's an example of MainController:
_app.controller('MainController', function ($scope, SessionService) {
$scope.user = SessionService.getUser();
});
Here's an example of SessionService:
_app.factory('SessionService', function() {
var user = null;
return {
getUser : function() {
return user;
},
setUser : function(newUser) {
user= newUser;
}
};
});
Of course, when you login you must set the user to the SessionService. Therefore, a SessionService has to be injected into your LoginController, too.
And finally, your html:
<div ng-app="myApp" ng-controller="MainController">
<div class="col-sm-3" ng-controller="TreeController">
<div ng-hide="user == null" treeviewdirective-here>
</div>
</div>
<div class="col-sm-9 content" ng-view="">
</div>
</div>