If I am using a state with multiple views, how do I destroy one of the view's scopes? I tried the following and it did not work:
<div ng-if='showFilters' ui-view="filters"></div>
<div ui-view="tabledata"></div>
<div ui-view="graph"></div>
Even when $scope.showFilters set to false, the scope for the 'filters' view still exists. I wonder if the ng-if is only destroying its child scope, but doesn't know about the ui-view's scope. I appreciate the help.
I'd recommend using a hierarchy of views so you can easily destroy the scope of any 'child' view:
.state('parent', {
controller: "parentController as parent",
templateUrl: '../parent.html'
})
.state('child1', {
controller: "child1Controller",
templateUrl: '../child1.html'
})
.state('child2', {
controller: "child2Controller",
templateUrl: '../child2.html'
})
Note that you don't have to use 'parent.child2' (nested states) unless you want to. The key is that the child scopes will inherit the parent scope as long as they are within the html frame of the parent, such as:
<div ui-view="parent">
<div ui-view="child1"></div>
<div ui-view="child2"></div>
</div>
Then from the children, you can simply reference something like {{parent.filters.filter1}} and if you need to destroy the scope, you can do so from the parent controller using this.filters = undefined.
Related
what iam trying is to get the child views for different states
my states code
routeConfig.$inject = ['$stateProvider'];
function routeConfig($stateProvider) {
$stateProvider.state('home', {
url: '/',
templateUrl: 'home.html'
}).state('home.companies.products',{
abstract: true,
url: 'companies',
views: {
'allcompanies#home': {
templateUrl: 'companiesList.html',
controller: 'CompanyController',
controllerAs: 'vm'
}
}
}).state('home.companies.products.detail', {
url: '/:companyId',
views: {
'single-company': {
templateUrl: 'single-company.html',
controller: 'SingleCompanyController',
controllerAs: 'vm',
resolve: {
companyResolve: getCompany
}
}
}
});
}
home.html
<div ui-view="allcompanies">
<h2>Home template</h2>
</div>
companiesList.html
<div ui-view="single-company">
<div ng-init="someFunc()">
<h2>Companies List</h2>
</div>
</div>
single-company.html
<div>
<h2> single company </h2>
</div>
my problem is when i call the "home.companies.products.detail" state it will bind in the ui-view="single-company" template instead of companies list while "single-company" is binding on "companies List" the init function is calling in the background i dont need to call that function bcz of that the function is calling twice if i again go to "allcompanies" view again the function is calling third time so iam getting the issues when again and again the init function is calling so how can manage the child states without calling that function again and again
Thanks in advance
Do not use ng-init. According to angularjs documentation:
There are only a few appropriate uses of ngInit, such as for aliasing special properties of ngRepeat, as seen in the demo below; and for injecting data via server side scripting. Besides these few cases, you should use controllers rather than ngInit to initialize values on a scope.
https://docs.angularjs.org/api/ng/directive/ngInit
If the purpose of ng-init="someFunc()" is to be executed only once when Companies list is loaded in the beginning, then call the function inside CompanyController. It will execute the first time the controller is created.
As long as you have wrapped your controller in an anonymous function like below:
(function() {
angular.module('example', []){
someFunc(); //Function call
//Add function declaration here
});
Then the function will be executed when the controller is created. If you remove the ng-init directive and place the function here this would solve your issue and would be better practice according to the angular documentation.
Hope that helps.
Imagine some html as follows:
<body ng-app="blocksApp">
Some content goes here
<div ui-view="monty">
<div ui-view="dave">Aaa</div>
<div ui-view="pete">Bbb</div>
<div ui-view="steve">Ccc</div>
</div>
</body>
Using ui-router, is there any way to code a state that will set "dave" to a new snippet of html, whilst leaving everything else untouched.
e.g. I'd like to do this:
$stateProvider
.state('daveonly',{
url: "/dave",
views:{
'dave':{template:"Dave now has content"}
}
})
I can't get it to work. The reason I want to do this is that sometimes I'd like to replace 'Dave' with a partial update, other times I'd like to replace the entire 'monty' with a partial update. It seems that ui-router does not like having nested ui-views in the same snippet of html.
From one point of view I'd like to suggest:
move html code to '.tpl.html' files
use 'templateUrl' instead of 'template'
And check if the following is suitable for you:
$stateProvider.state("daveonly", {
views: {
"dave": {
templateUrl: 'daveonly.tpl.html',
},
"pete": {
templateUrl: 'pete.tpl.html',
},
"steve": {
templateUrl: 'steve.tpl.html',
},
}
});
Take a look at page1 and page2 for more details.
But from another point of view it could be more useful to use only one ui-view and to redesign current ui-views to become the appropriate directives with controllers/services: usage of directives with controllers/services could help to manage partial reload correctly and to write unit-tests.
Yes it can be done easily with the help of abstract states and yes you are correct ui-router doesn't like direct nested views directly but it works fine if the views are in any child template.
Now consider this main page(index.html)
<body ng-app="app">
<div ng-view=""></div>
</body>
Now this template which will appear in this unnamed view. (parent.html)
<h3>This is the parent template</h3>
<div ng-view="child1"></div>
<div ng-view="child2"></div>
Now the JS file
$stateProvide.state('home',{
url:'/',
abstract:true,
views:{
"":{
templateUrl:'parent.html'
}
}
})
.state('home.child',{
url:"",
views:{
'child1#home':{
templateUrl:'child1.html'
},
'child2#home':{
template:'Child2'
}
}
})
.state('home.child.child1',{
url:"child1#home.child",
views:{
'child1#home':{
templateUrl:'child1viewchange.html'
}
}
});
(Now the manipulation part)
(child1.html)
<button ui-sref="home.child.child1">Child</button>
Now child1viewchange.html pe jana padega and wo dekhne wali hai kaunsi kaisi thi/......
(child1viewchange.html)
<h3>Child1's view change</h3>
So now when we click on the button in child view1 the content in the first view changes and if we assign controllers then they can use them to control data.
I'm trying to decide on an Angular Structure for my app. I started with the Angular Full Stack Generator.
In UI-Router, you can nest states. It seems logical for me to nest related states. ex: Registration, Registration.form, Registration.confirmation.... but those nested states seem to rely on each child view and parent view being viewed on the same page.
Meaning the parent will have an <ui-view> tag that will insert the child view when called.
Is it possible to have a nested state replace the parent?
Or do I have just make unnested states?
Or is this a case for an abstract state?
There is a working example
Child replacing parent is almost "native" UI-Router solution. Let's have this state definitions:
.state('parent', {
url: "/parent",
templateUrl: 'tpl.html',
})
.state('parent.child', {
url: "/child",
templateUrl: 'tpl.html',
controller: 'ChildCtrl',
})
And this would be the tpl.html, in our case used for both states:
<div ui-view>
<h3>current state name: <var>{{$state.current.name}}</var></h3>
...
</div>
So, what we can see, parent root HTML element, does have attribute ui-view. That means, that this element also plays a role of a target for a child.
Check it in action here
I have a layout set up for one of my pages that is then seeded with a ton of little views that I use to populate with date. My states currently looks like so:
.state('eventLayout', {
templateUrl: 'partials/layouts/event.html',
controller: 'EventCtrl',
})
.state('event', {
parent: 'eventLayout',
url: '/event/{eventUrl}',
views: {
'event.video': {
templateUrl: 'partials/views/event.video.html'
},
'event.info': {
templateUrl: 'partials/views/event.info.html'
},
'card.event': {
templateUrl: 'partials/views/card.event.html'
},
'card.clip': {
templateUrl: 'partials/views/card.clip.html'
},
'card.upcoming': {
templateUrl: 'partials/views/card.upcoming.html'
},
'card.banner': {
templateUrl: 'partials/views/card.banner.html'
},
'card.comment': {
templateUrl: 'partials/views/card.comment.html'
},
'card.notification': {
templateUrl: 'partials/views/card.notification.html'
},
'card.cube': {
templateUrl: 'partials/views/card.cube.html'
},
'card.mix': {
templateUrl: 'partials/views/card.mix.html'
},
'card.score': {
templateUrl: 'partials/views/card.score.html'
},
'card.sponsor': {
templateUrl: 'partials/views/card.sponsor.html'
},
'card.nobroadcasters': {
templateUrl: 'partials/views/card.nobroadcasters.html'
},
'card.link': {
templateUrl: 'partials/views/card.link.html'
},
'card.suggest': {
templateUrl: 'partials/views/card.suggest.html',
controller: 'SuggestblockCtrl'
},
'card.footer': {
templateUrl: 'partials/views/card.footer.html'
}
}
})
As you can see the parent layout holds my Controller for the page which is called EventCtrl . Now I would expect that all the views now have access to this controller, but that doesn't seem to be the case. Instead I have to wrap the main parent template from eventLayout in a div where I then just use the old school:
<div ng-controller="EventCtrl"></div>
I'd like to at least understand why this is happeneing and what the proper method is to make sure all views have access to the states main controller. Thanks!
EDIT:
To add more context to how im using the views in my current app I have detailed the current set-up below.
From the file partials/layouts/event.html in parent $state eventLayout
<div ng-controller="EventCtrl">
<div ui-view="event.video"></div>
<div ng-repeat="activity in activities.results">
<div ng-if="activity.card_type == 'event'" ui-view="card.event"></div>
<div ng-if="activity.card_type == 'clip'" ui-view="card.clip"></div>
<div ng-if="activity.card_type == 'upcoming'" ui-view="card.upcoming"></div>
</div>
</div>
</div>
As you can see a views are nested within the parent layout. I'm having to wrap it all with ng-controller="EventCtrl" in order to allow each view access to its scope.
The overall angular's ui-router design, is about the view / $scope inheritance - not base controller accesibility. The detailed info could be found here:
Scope Inheritance by View Hierarchy Only
small cite:
Keep in mind that scope properties only inherit down the state chain if the views of your states are nested. Inheritance of scope properties has nothing to do with the nesting of your states and everything to do with the nesting of your views (templates).
It is entirely possible that you have nested states whose templates populate ui-views at various non-nested locations within your site. In this scenario you cannot expect to access the scope variables of parent state views within the views of children states...
Also these are good reading, which content would be hardly any better explained here:
AngularJS Inheritance Patterns by Minko Gechev
AngularJS–Part 3, Inheritance by Gabriel Schenker
So, let's summarize a bit.
1) We know, that from any template we can access only its own $scope.
2) What is available in the view/template $scope, is a job of its Controller which can extend it with some functions, objects, etc.
3) If any parent controller (from view-nesting perspective) will inject anything into its own/parent scope - we will have access to it as well (so called prototypical inheritance)
Having this, we can create an Events object in the parent $scope, managed by EventCtrl - and consume it in any a child view template:
// the reference (reference to object Events)
// to be shared accross all child $scopes
$scope.Events = {};
// objects
$scope.Events.MyModel = { FirstName: "....
// functions
$scope.Events.save = function () {....
And now in any child template we can use it like this
<div>{{Events.MyModel.FirstName}}</div>
Another technique would be to place the controller into $scope's Events object:
$scope.Events = this; // controller
And then have full access to controller's methods, properties...
Following this tutorial: https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views, i'm able to create an application with multiple views defined in a root template. I need to modify that scheme a bit by putting tabledata and graph to a child view called content. I want my views to look like that:
index.html
<body>
<div ui-view="header"></div>
<div ui-view="content"></div>
</body>
content.html
<div ui-view="tabledata"></div>
<div ui-view="graph"></div>
And my routes looks like that:
# ...
.state('videos',
url: '/videos'
templateUrl: 'content.html'
views:
'tabledata':
templateUrl: 'tabledata.html'
controller: '...'
'sidebar':
templateUrl: 'graph.html'
controller: '...'
)
However, when pointing my browser to /videos, tabledata.html and graph.html templates are not loaded to corresponding views. Everything works great though, if i'll put tabledata and graph views to index.html
I'm sure there is something really wrong with my code but i'm not able to figure out what exactly nor google anything up.
As far as I'm aware you can only have multiple ui-view's in multiple named views, i.e.when you explicitly declare a views property on your state definition. I'm not entirely sure what you're after, but if you would like to have control over where hese ui-views load their states then you can use an abstract state, from the link you provided:
Views override state's template properties
If you define a views object, your state's templateUrl, template and
templateProvider will be ignored. So in the case that you need a
parent layout of these views, you can define an abstract state that
contains a template, and a child state under the layout state that
contains the 'views' object.
This is what I suggest:
.state('videos',{
url: '/videos',
templateUrl: 'content.html',
abstract: true})
.state('videos.xyz',{
url: '/xyz',//you can leave this empty if you like
{
views:{
'tabledata':{
templateUrl: 'tabledata.html'
controller: '...'
},
'sidebar':{
templateUrl: 'graph.html'
controller: '...'
}
}
})
If you don't want that xyz appended to your url's, just pass in an empty string for the url property of the state videos.xyz. I use this approach all the time, let me know if it's what you're after.