0

I'm trying to create a simple blog website using AngularJS. I'm just starting out, so what I'm thinking my not be the best way to do this, so any alternative suggestions are welcome.

I have a controller.js file with two blog controllers. One to display a list of blog posts, and the other that displays the post content by including an HTML file.

controller.js

myAppControllers.controller('BlogListCtrl', ['$scope', '$http', function ($scope, $http) {
    $http.get('articles/articles.json').success(function (articles) {
        $scope.articles = articles;
    });
}]);

myAppControllers.controller('BlogPostCtrl', ['$scope', '$routeParams', function ($scope, $routeParams) {
    $scope.includeFile = 'articles/' + $routeParams.blogPostId + '.html';
}]);

articles.json

[
{
    "id": "test-article-one",
    "title": "Test Article one",
    "author": "Gareth Lewis",
    "datePosted": "2015-06-23",
    "summary": "This is a test summary"
},
{
    "id": "test-article-two",
    "title": "Test article two",
    "author": "Gareth Lewis",
    "datePosted": "2015-06-23",
    "summary": "This is a test for article two"
}
]

app.js

when('/blog', {
            templateUrl: 'partials/blog-articles.html',
            controller: 'BlogListCtrl'
        }).
        when('/blog/:blogPostId', {
            templateUrl: 'partials/blog-post.html',
            controller: 'BlogPostCtrl'
        }).

blog-post.html

<ng-include src="'partials/header.html'"></ng-include>

<!-- Want to add title, author, datePosted information here... -->

<article class="content">
    <ng-include src="includeFile"></ng-include>
</article>

This blog listings work fine. When I click into a blog post, it also serves up the content from the HTML file OK as well. However, I want to be able to reuse the title, author and datePosted properties from the selected article in the blog-post.html partial view. What's the best way to do this? Would I need to pass them to the Controller somehow to then pass to the view? I don't really want to pass these as routeParams. Or would I need to do a $http.get on articles.json and iterate through to find the selected article and then pass the property values back to the view?

Thanks for the help.

Diptendu
  • 2,120
  • 1
  • 15
  • 28
Gareth Lewis
  • 751
  • 2
  • 15
  • 36
  • 3
    Your first controller logic should really be a Service instead. https://docs.angularjs.org/guide/services – Matthew Green Jun 26 '15 at 16:30
  • Possible duplicate of [Pass variables to AngularJS controller, best practice?](http://stackoverflow.com/questions/11703477/pass-variables-to-angularjs-controller-best-practice) – T.Todua Sep 20 '16 at 11:29

3 Answers3

3

This is maybe a common question in angular. What you have to understand is that Scope is defined per controller... In order to share data across controller you still have the option to use $scope.$parent or $rootScope to link controllers but I would use those carefully.

It is better to use Angular Services which are based on singleton patterns therefore you can use them to share information between controllers and I think it will be a better approach.

I found that this has been previously discussed and here are some good examples:

AngularJS Service Passing Data Between Controllers

Community
  • 1
  • 1
Dalorzo
  • 19,834
  • 7
  • 55
  • 102
3

You said that suggestions are welcome, so here it goes.

1 - Transport all your Blog logic to a service;

2 - Provide the data on resolving routes. This is a better approach to handle errors during the load time, 404s, and so on. You can provide a listener to $routeChangeError and deal with it there;

3 - On the service declared below, you have the methods to call your data and a method to retrieve the list cached on the service:

// services.js
myAppServices
    .service('BlogService', ['$http', '$q', function ($http, $q) {
        var api = {},
            currentData = {
                list: [],
                article: {}
            };

        api.getSaved = function () {
            return currentData;
        };

        api.listArticles = function () {
            var deferred = $q.defer(),
                backup = angular.copy(currentData.list);

            $http.get('articles/articles.json')
                .then(function (response) {
                    currentData.list = response;

                    deferred.resolve(response);
                }, function () {
                    currentData.list = backup;

                    deferred.reject(reason);
                });

            return deferred.promise;
        };

        api.getArticle = function (id) {
            var deferred = $q.defer(),
                backup = angular.copy(currentData.article),
                path = 'articles/' + id + '.html';

            $http.get(path, {
                cache: true
            })
                .then(function (response) {
                    currentData.article = {
                        path: path,
                        response: response
                    };

                    deferred.resolve(currentData.article);
                }, function (reason) {
                    currentData.article = backup;

                    deferred.reject(currentData.article);
                });

            return deferred.promise;
        };

        return api;
    }]);

The BlogService.getSaved() will retrieve the stored data, made after each call.

I've made a method to call the ng-include path too, so you can verify if it exists, with cache === true, the browser will keep a copy of it, when calling it again on the view. A copy of the response of the blog article is made too, so you can access its path and the response whenever you need.

On the controllers below, they were adaptated to supply the current needs:

// controller.js
myAppControllers
    .controller('BlogListCtrl', ['$scope', 'articles',
        function ($scope, articles) {
            $scope.articles = articles;

            /* OTHER STUFF HERE */
        }
    ])
    .controller('BlogPostCtrl', ['$routeParams', '$scope', 'article' 'BlogService',
        function ($routeParams, $scope, article, BlogService) {
            // On `article` dependency, you have both the original response
            // and the path formed. If you want to use any of it.

            $scope.includeFile = article.path;

            // To get the current stored data (if any):
            $scope.articles = BlogService.getSaved().list;

            // Traverse the array to get your current article:
            $scope.article = $scope.articles.filter(function (item) {
                return item.id === $routeParams.id;
            });

            /* OTHER STUFF HERE */
        }
    ]);

And the route declarations were changed to load the data when resolving the routes.

// app.js
$routeProvider
    .when('/blog', {
        templateUrl: 'partials/blog-articles.html',
        controller: 'BlogListCtrl',
        resolve: {
            articles: ['BlogService', '$routeParams', function (BlogService, $routeParams) {
                return BlogService.listArticles();
            }]
        }
    })
    .when('/blog/:id', {
        templateUrl: 'partials/blog-post.html',
        controller: 'BlogPostCtrl',
        resolve: {
            article: ['BlogService', '$routeParams', function (BlogService, $routeParams) {
                return BlogService.getArticle($routeParams.blogPostId);
            }]
        }
    })
Mateus Leon
  • 1,381
  • 1
  • 14
  • 21
0

You can use a global scope to set this data, or you can use service to communicate between the controllers. There is a lot of ways to resolve this problem read a little bit more about services in the link bellow and see if you can find how to resolve your problem.

AngularJS: Service vs provider vs factory

Community
  • 1
  • 1
Alexandre Mendes
  • 119
  • 1
  • 1
  • 11