Massimo Massimo - 1 month ago 26
AngularJS Question

Angularjs 1.5 - CRUD pages and Components

I'm working in a small Angularjs 1.5 project and I'm quite new.

I have to create an object or edit an existing object and the template is the same, the only difference in the logic is the call to an http service for data retrieve in case of update.
I use ngRoute.
How can I do to use the same component for both operations ?

EDIT

JS COMPONENT CODE

angular.
module('editProject').
component('editProject', {
templateUrl: 'app/edit-project/edit-project.template.html',
controller:['$http','$routeParams', function EditProjectController($http,$routeParams) {
var self=this;
$http.get('rest/projects/' + $routeParams.id ).then(function(response) {
console.log(JSON.stringify(response.data));
self.project = response.data;
});


}
]
});


ROUTE CONFIG

angular.
module('myApp').
config(['$locationProvider', '$routeProvider',
function config($locationProvider, $routeProvider) {
$locationProvider.hashPrefix('!');

$routeProvider.
when('/projects', {
template: '<project-list></project-list>'
}).
when('/projects/:id', {
template: '<project-detail></project-detail>'
}).
when('/projects/edit/:id', {
template: '<edit-project></edit-project>'
}).
when('/projects/new', {
template: '<edit-project></edit-project>'
}).
otherwise('/projects');
}
]);

Answer

1. Quick fix

Simple solution would be using a conditional checking if the $routeParams.id param is defined, if so, then it need a request to feed the project informations, otherwise not.

if($routeParams.id){
    $http.get('rest/projects/' + $routeParams.id ).then(function(response) {
        console.log(JSON.stringify(response.data));
        self.project = response.data;
    });
}

2. Component Router

Even though the previous solution looks simple and functional, it might not be the best. A propper solution is to use a separate component for each route, but you can reuse the form part of it. Also, assuming that you are building a completelly component-based app you should use ngComponentRoute instead of ngRoute.

.component('app', {
  template: '<ng-outlet></ng-outlet>',
  $routeConfig: [
    {path: '/projects', name: 'Projects', component: 'projectList', useAsDefault: true},
    {path: '/projects/:id', name: 'Project Detail', component: 'projectDetail' },
    {path: '/projects/edit/:id', name: 'Edit Project', component: 'editProject' },
    {path: '/projects/new', name: 'New Project', component: 'newProject' }
  ]
});

Then you can create a project editor component and reuse it on both edit and new project page by just adding <project-editor-form project="{}" on-save="$ctrl.mySave()"></project-editor-form>. For example:

.component('projectEditorForm', {
    template:
      'Project Name: <input type="text" ng-model="$ctrl.project.name">' +
      '<button ng-click="$ctrl.onSaveProject($ctrl.project)">Save</button>',
    bindings: {
        project: '<',
        onSave: '&'
    },
    controller: function () {
        var $ctrl = this;
        $ctrl.onSaveProject = function (project) {
            // general save logic goes here
            // if you want to reuse this too
            // then emit the on save for onSave binding
            $ctrl.onSave($ctrl.project);
        }
    },
  })

.component('editProject', {
    template:
      '<project-editor-form project="$ctrl.project" on-save="$ctrl.mySave()">' + 
      '</project-editor-form>',
    bindings: {
        project: '<',
        onSave: '&'
    },
    controller: function ($http) {
        var $ctrl = this;

        // consider using a Service to do such task
        // instead of request directly from the controller
        $http.get('rest/projects/' + $routeParams.id).then(function(response) {
            $ctrl.project = response.data;
        });

        $ctrl.mySave = function (project) {
            // save logic here
        }
    },
  })

3. ngRoute with resolve

Another approach that doesn't deppend on ngComponentRoute, is to use the route resolve property, it's very usefull when using component as route template. You add a resolve property to your route, that would be the project, and bind to your form component.

$routeProvider
  .when('/projects/edit/:id', {
    template: '<project-editor-form project="$resolve.project"></project-editor-form>',
    resolve: {
      project: function ($route, $q) {
        var id = $route.current.params.id;
        // again, consider using a service instead
        return $http.get('rest/projects/' + id).then(function (response) {
          return response.data;
        });
      }
    }
  })
  .when('/projects/new', {
    template: '<project-editor-form project="$resolve.project"></project-editor-form>',
    resolve: {
      project: {
        id: 0,
        name: ''
      }
    }
  })