I have an Angular application in which I'm creating a header to a page using uiRouter separate views:
Route config:
$stateProvider.state('content', {
url: '/content/{idContent}',
views: {
'header#content': {
templateUrl: 'templates/content/header.partial.html'
},
'mainContent': {
templateUrl: 'templates/content/index.html'
}
}
});
Content template:
<section ng-controller="ContentController">
<div ui-view="header"></div>
{{Here I display the content}}
</section>
Header template:
<section ng-controller="ContentHeaderController">
{{Here I render the header}}
</section>
The problem is, I need to access an object that is resolved in ContentController on the ContentHeaderController. I see that they don't share a parent scope:
ContentController: 4
ContentController's parent: 3
ContentHeaderController: 6
ContentHeaderController's parent: 5
How can I pass an object from the content view to it's header?
The problem of sharing data between controllers can be solved by using events (don't recommend it) and using services as models, which is a recommended way to do that because your data will be synchronized automatically and you will not be using soon to be deprecated API such as $broadcast.
The gist of this approach is to store data in service in an objects, not just as plain primitives on service intself, and then pass reference to this objects into controller where they are bound in template. Check this blog post about model pattern with code examples.
Related
In a component I would like to associate templates to CRUD actions, but use a single controller that handles data for all all of them. Does this make sense with angular components, or should I use several components ?
How would the ui-router state configuration look like ?
EDIT:
Components have templates, and ui-router states too. I am confused about the articulation of those 2 concepts.
EDIT 2:
trying to clarify my understanding so far :
component are controller + template + bindings. Controller can be omitted.
states are url + template + controller or url + component. Controller can be omitted.
So it seems that components are taking away some of the responsabilities that use to belong to ui-router.
My goal here:
- url1 --> controller foo + template x;
- url2 --> controller foo + template y;
- url3 --> controller foo + template z;
should I do :
components:
component x --> controller foo + template x;
component y --> controller foo + template y;
component z --> controller foo + template z;
and then routing:
url 1 --> component x
url 2 --> component y
url 3 --> component z
?
EDIT 3:
quote from https://docs.angularjs.org/guide/component :
"In a component-based application, every view is a component"
quote from https://ui-router.github.io/guide/ng1/route-to-component :
"The component model enforces separation of concerns and encapsulation by using an isolate scope. Data from parent scopes can no longer be directly accessed. Instead, it needs to be explicitly wired in"
Yes, it is possible. Your ui-router config would look something like this: (Multiple states having same controllers.)
.state('add', {
url: '/add',
templateUrl: 'templates/add.html',
controller: 'contactCtrl'
})
.state('edit', {
url: '/edit',
templateUrl: 'templates/edit.html',
controller: 'contactCtrl'
})
Here's a working example of having multiple states and templates using same controller.
Edit: You don't have to use components. Why create three different extra components while you can achieve the same thing without them? I would still recommend the approach I mentioned above. Getting the same outcome with lesser code should always be chosen. :)
Quoting Eric Elliot on twitter,
Code is temporary. It exists while it is useful.
If code is replaced by better code, good!
If code can be deleted, celebrate!
Your state provider will look like this
JS code
$stateProvider
.state('report', {
views: {
'filters': {
templateUrl: 'report-filters.html',
controller: 'ctrl'
},
'tabledata': {
templateUrl: 'report-table.html',
controller: 'ctrl'
},
'graph': {
templateUrl: 'report-graph.html',
controller: 'ctrl'
}
}
})
in single state you can load multiple views
HTML
<body>
<div ui-view="filters"></div>
<div ui-view="tabledata"></div>
<div ui-view="graph"></div>
</body>
refer multiple views
angular.module('', [])
// Route handler touches this
.component('route1', {
template: `<shared-behaviour>
<route-view-1></route-view-1>
</shared-behaviour>`
})
// This is a purely visual component that links actions with the shared behaviour component
.component('routeView1', {
require: {
shared: '^sharedBehaviour'
},
template: '<button ng-click="$ctrl.onClick()></button>',
controller: class RouteView2Ctrl {
onClick() {
this.shared.onSharedClick()
}
}
})
// Contains the shared behaviour that is shared across the multiple routes
.component('sharedBehaviour', {
// Tell Angular to render children passed to this component
transclude: true,
template: '<div ng-transclude></div>',
controller: class SharedBehaviourCtrl {
onSharedClick() {
/// do something
}
}
})
// Newest version of UI-Router
.state('route1', {
component: 'route1'
})
Further to our comments, you could use something like the above. It's pretty verbose, but that's Angular 1.5 for you. Haven't tested that it works, but it's a basic idea of how it might work.
The main take away being that you have the route1 component which only handles linking route stuff (like the url, resolves, etc) and passes that information to its children (of which there aren't any).
routeView1 just renders how the route would work, and uses require to talk to some parent API that is shared (you could also use one-way bindings for this if you wanted, but with lots of methods this leads to a messy template).
sharedBehaviour only contains the shared behaviour you want renders any child passed to it.
This is admittedly pretty messy and I would prefer that you instead used one-way bindings for route-view-1 and handled the linking in shared-behaviour, but this might be quite a bit of repetition. You could use some transclusion magic to solve that, but it's.. not really worth it.
I would really recommend against sharing controllers like other answerers have suggested. In Angular 2 and above, a controller is the component. You share behaviour there by doing something similar to what I have done (or by using one-way bindings with callbacks) and using composition.
BTW as mentioned in my answer, newer versions of UI-Router for Angular 1.5 allow you to specify a component to be rendered rather than a template. See here for more info.
I'm using UI Router as a router for my application, and I have a situation where I need to have all instances of one resource available all the time.
These instances are listed in sidebar of my application.
I starter with something like this in my routes.js file:
.state('auth', {
url: '/',
abstract: true,
controller: RootCtrl,
template: '<div ui-view autoscroll="true"></div>',
resolve: {
campaigns: function(UserCampaignsCollection, activeUser) {
return (new UserCampaignsCollection).query({user_id: activeUser.id});
}
}
})
This means that I had to create separate controller, just for keeping all the campaigns and sharing those between other controllers, like this:
_app.controller('RootCtrl',
function($rootScope, campaigns) {
$rootScope.campaigns = campaigns;
});
This works fine, since I resolve it on only one place, and then all of my other states just inherit base state (as auth.account, auth.inovice etc.), but I would like to avoid need for attaching all of my campaigns on rootScope.
Is there some other way to pass data to other controllers, which would not involve creating dummy controller like this, and attaching data to rootScope?
If all other states inherit from auth then (via https://github.com/angular-ui/ui-router/wiki/Nested-States-and-Nested-Views#what-do-child-states-inherit-from-parent-states) their controllers can also be injected with resolved campaigns.
I am trying to setup my app with ui-router. I am familiar with basic nested views but I am wanting to do something more complex. I have my basic setup for the main views. I would like to have a chat popup that has its own views that are independent from the main views. I want to be able to navigate the main views and not affect the states in the chat popup. So how is this done? Do i need to have a abstract state for the chat? and then have nested views from there?
here is a visual.
and here is a plunker
plunker
$stateProvider
.state('root', {
abstract: true,
views: {
'#': {
template: '<ui-view />',
controller: 'RootCtrl',
controllerAs: 'rootCtrl'
},
'header#': {
templateUrl: 'header.html',
controller: 'HeaderCtrl',
controllerAs: 'headerCtrl'
},
'footer#': {
templateUrl: 'footer.html',
controller: 'FooterCtrl',
controllerAs: 'footerCtrl'
}
}
})
.state('root.home',{
parent:'root',
url:'/home',
templateUrl:'home.html',
controller: 'HomeController',
controllerAs:'homeCtrl'
})
.state('root.about',{
parent:'root',
url:'/about',
templateUrl:'about.html'
});
});
I suggest that, don't use footer as a ui-view, because it is completely independent of your states.
Then how?
Make your footer part as a template and use ng-include to render your footer part.
<footer ng-include="'/footer.html'"></footer>
And within footer.html you can specifies the controller for the footer view.
Benefits
No need to handle footer on each state
No need to pass chat history on every change in state.
Create Chat service/function with controllers in different js files and inject to the index.html and script.js. use bootstrap collapsible modal for pop-up chats.
Looking # your plunkr, you're on right track,though injecting controller from script.js via controllerAs is not scalable for larger app.
Instead you can create js files for each controller and service and separate partial views, just need to inject the services and controllers to index.html and mention partial views in stateprovider function.
I am not sure if You want to use route for the chat but there are two ways for you may be more
Use modals that can collabse and open when clicked like that of facebook here
Modals for bootstrap
Use angulars ngHide ngShow
For your navigation while using at sub elements on chat you can create one state for the chat and nest chat navigation in to you chat state so that any state change will not change your other chat states.
That means you will need to use substate concepts of ui-router
I have an app with a directive called fightcard. In my app configuration, I'm using ui-router to change the state. This code works...
$stateProvider
.state('matches', {
url: '/matches',
template: '<fightcard matches="matches"></fightcard>'
})
But... I was wondering if there is a property on state like controller to simply pass in the directive instead of an html template. I'd like to do something like this:
$stateProvider
.state('matches', {
url: '/matches',
directive: 'fightcard',
directiveModels: ['matches']
})
The html template option isn't that terrible - it may actually be superior - just wondering if there is an alternative in more "angular way" or perhaps the html template is the preferred approach. Each match has sub views for the best of games... probably it's better to have the directives contained a simple html templates like so:
$stateProvider
.state('matches', {
url: '/matches',
templateUrl: 'partials/matches.html'
})
.state('matches.games', {
url: '/games',
templateUrl: 'partials/games.html'
})
matches.html template
<fightcard matches="matches"></fightcard>
games.html template
<h6>BEST OF 5 GAMES</h6>
<div ng-repeat="gameModel in games">
<game gamemodel="gameModel" class="centerText"></game>
</div>
Putting the directive in a template is THE way to use the directive in AngularJS.
If you wanted to get fancy, you could extend .state() using the .decorator() method of $stateProvider to parse your special config and create that template for you, but it would really be a round-about way to go about using the directive.
I am using angular-ui-router to control my states in my AngularJS application. In one partial I use jquery-file-upload. The problem that I have is that when I use the example given by jquery-file-upload, is that it defines the controller in the template like this:
<!-- The file upload form used as target for the file upload widget -->
<form id="fileupload" action="//jquery-file-upload.appspot.com/" method="POST"
enctype="multipart/form-data" data-ng-app="demo"
data-ng-controller="DemoFileUploadController" data-file-upload="options"
data-ng-class="{'fileupload-processing': processing() || loadingFiles}">
...
</form>
The form tag has data-ng-controller="DemoFileUploadController". In my controller this creates all the file upload methods needed (like $scope.submit() and $scope.queue)
The problem is that I define my controllers angular-ui-router style, like this in my app.js:
$stateProvider.state('upload', {
url: "/upload",
views: {
"upload": {
templateUrl: "app/partials/upload.html",
controller: 'DemoFileUploadController'
},
}
})
But this piece of code causes my file upload methods, not to be loaded (or bound) in my controller (there is no $scope.submit() and $scope.queue).
Is there a way I can use angular-ui-router and jquery-file-upload together?
My fix is to simply omit the controller when defining the state:
$stateProvider.state('upload', {
url: "/upload",
views: {
"upload": {
templateUrl: "app/partials/upload.html"
},
}
})
This way I assign the controller in my template (like in the example data-ng-controller="DemoFileUploadController"). I am still looking for a way to assign my controller via the stateProvider.