Olivvv
Olivvv

Reputation: 1200

With angular component and ui-router, can a single controller have multiple templates?

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 :

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"

Upvotes: 0

Views: 1065

Answers (3)

Dan
Dan

Reputation: 10538

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.

Upvotes: 0

tanmay
tanmay

Reputation: 7911

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!

Upvotes: 2

Jayant Patil
Jayant Patil

Reputation: 1587

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

Upvotes: 0

Related Questions