angular app plugin

Plugin Development
  • Hello,
    I'm working on a plugin that is an angular app. I started to work with nodebb 0.6.1 and everything works nicely.
    Today I upgrade to 0.7.0 Now when I click on my icon header the main template is well loaded but not the one inside my ng-view directive. If I reload the page (F5) everything is well loaded.

    All my javascprit files (angular.js, angular-route.js, controllers, etc.) are defined in the script section of my plugin.json.

    Doesn't anyone know what are the changes between 0.6.1 and 0.7.0 that causes this behaviour and how can I correct my code.

    Thanks in advance and let me know if you need to post part of the code

  • Can you paste your angular code? I'm not an expert but I have a feeling I know what the problem may be

  • Here is the angular app main file with routes, I only include the two first one

    "use strict";
     var fbapp = angular.module("francoBowlApp", 
     ["ngRoute", 
     "angularFileUpload", 
     "ngDragDrop",
     "ui.bootstrap",
     "angularUtils.directives.dirPagination",
     "services"]);
    
    // get global variable
    fbapp.run(["$rootScope", function($rootScope) {        
        $rootScope.fb_webservice = document.getElementById("fbws").textContent;}]);
    
    // configure routes
    fbapp.config(["$routeProvider'","$httpProvider",
        function($routeProvider, $httpProvider) {
           var rootFolder = "templates/angular",
           token = document.getElementById("token").textContent;
    
          $routeProvider
           // route de la page index
          .when("/", {
                templateUrl : rootFolder+"/index.tpl",
                controller  : "fbCtrl"
           })
          // route de la page mon franco bowl
         .when('/monfranco', {
               templateUrl : rootFolder+"/francobowl/index.tpl",
              controller : "fbMonFrancoIndexCtrl"
          });
         // include bearer token in each web service call
         $httpProvider.defaults.headers.common.Authorization = "Bearer"+ token;
    }]);
    

    Then the main template

    <div class="row" 
        ng-app="francoBowlApp" 
        ng-controller="fbCtrl">
        <span hidden="hidden" id="token">{token}</span>
        <span hidden="hidden" id="fbws">{fbws}</span>
        <span hidden="hidden" id="username">{username}</span>
    
        <div class="col-sm-2">
            <div class="sidebar-nav">
                <div class="navbar navbar-default" role="navigation">
                    <div class="navbar-header">
                        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".sidebar-navbar-collapse">
                            <span class="sr-only">Toggle navigation</span>
                            <span class="icon-bar"></span>
                            <span class="icon-bar"></span>
                            <span class="icon-bar"></span>
                        </button>
                        <span class="visible-xs navbar-brand">Site menu</span>
                    </div>
                    <div class="navbar-collapse collapse sidebar-navbar-collapse">
                        <ul class="nav navbar-nav">
                            <!-- IF uid -->
                            <li><a href="#/monfranco">Mon Franco Bowl</a></li>
                            <!-- ELSE -->
                            <li><a href="#">Accueil</a></li>
                            <!-- ENDIF uid -->
                            <li class="dropdown">
                                <a href="#" class="dropdown-toggle" data-toggle="dropdown">Administrer <b class="caret"></b></a>
                                <ul class="dropdown-menu">
                                    <li><a href="#/admin/competition">Compétitions</a></li>
                                    <li><a href="#/admin/mot_clef">Mots clefs</a></li>
                                    <li><a href="#/admin/image">Images</a></li>
                                    <li><a href="#/admin/jeu">Jeux</a></li>
                                </ul>
                            </li>
                            <li><a href="#/equipe">Equipes</a></li>
                        </ul>
                    </div><!--/.nav-collapse -->
              </div>
            </div>
        </div>
    
        <div class="col-sm-10 col-xs-12">
            <div ng-view>         
            </div>
        </div>
    </div>
    

    And my library.js

    "use strict";
    
    var plugin = {},
        user = module.parent.require('./user'),
        async = module.parent.require('async'),
        meta = module.parent.require('./meta'),
        http = require('http'),
        jwt = require('jsonwebtoken');
    
    function renderAdminPage(req, res, next) {
        res.render('admin/plugins/francobowl', {});
    }
    
    function renderCustomPage(req, res, next) {
        var jwt = require('jsonwebtoken'),
            err,
            userInfo = {},
            url = req.body.url,
            config;
    
        // recuperer les informations sur l utilisateur
        async.parallel({
            // definition des données utilisateur
            userData : function(next) {
                if (req.user && req.user.uid) {
                    // si l utilisateur est authentifie appel a la base de donnees
                    user.getUserData(req.user.uid, next);
                } else {
                    // si l utilisateur n est pas authentifie definir comme anonyme
                    next(err, {uid : 0, username : 'anonymous'});
                }  
            },
            // verifier si l utilisateur est un administrateur
            isAdministrator : function(next) {
                if (req.user && req.user.uid) {
                    // si l utilisateur est authentifieappel a la base de donnees
                    user.isAdministrator(req.user.uid, next);
                } else {
                    // si l utilisateur n est pas authentifie definir comme 
                    // non administrateur
                    next(err, false);
                }  
            },
            // recuperer les donnees de configuration du franco bowl
            config : function(next) {
                meta.settings.get('francobowl', function(err, settings){
                    if (err) {
                        next(err);
                    }
                    config = settings;
                    next();
                });
            }
        }, function(err, data) {
            // en cas d erreur redirection vers la page d erreur
            if(err) {
                return res.redirect(url + '?error=' + err.message);
            };
    
            // definir les donnees de l utilisateur en fonction des donnees gerees
            userInfo =  {
                uid : data.userData.uid,
                username : data.userData.username,
                admin : data.isAdministrator
            };
    
            // creer le token pour les appels aux webservice
            userInfo.token = jwt.sign(userInfo, config.secret_key, {
                expiresInMinutes: 60 * 5
            });
    
            // charger l url des webservices francobowl
            userInfo.fbws = 'http://' + config.fb_ws + config.fb_root;
    
            // charger la page
            res.render('francobowl-page', userInfo);
        });
    }
    
    plugin.addNavigation = function(header, callback) {
        // ajouter le lien dans le menu de navigation principal
        header.navigation.push({
            class: '',
            route: '/francobowl',
            iconClass: 'fa fa-fw fa-shield',
            title: 'FrancoBowl',
            text: 'Franco Bowl'
        });
        callback(null, header);
    };
    
    plugin.addAdminNavigation = function(custom_header, callback) {
        // ajouter le lien dans le menu de navigation de la page d admin
        custom_header.plugins.push({
                route: '/plugins/francobowl',
                icon: 'fa-file-archive-o',
                name: 'Franco Bowl'
            });
    
        callback(null, custom_header);
    };
    
    plugin.init = function(params, callback) {
        // ajouter les routes
        var app = params.router,
            middleware = params.middleware;
    
        app.get('/FrancoBowl', middleware.buildHeader, renderCustomPage);
        app.get('/templates/FrancoBowl.tpl', renderCustomPage);
        app.get('/api/FrancoBowl', renderCustomPage);
    
        app.get('/admin/plugins/francobowl', middleware.admin.buildHeader, renderAdminPage);
        app.get('/api/admin/plugins/francobowl', renderAdminPage);
    
        callback();
    };
    
    // creer le coach dans la base du francobowl
    plugin.userData = function(newUser) {
    
        // recuperer les donnees d acces au web service du francobowl
        meta.settings.get('francobowl', function(err, settings){
            if (err) {
                next(err);
            }
            // recuperer les donnees de l adminitsrateur
            user.getUserData(1, function(err, user){
                if (err) {
                    next(err);
                }
                // definir les donnees à transferer
                var data = JSON.stringify({uid: newUser.uid}),
                    // donnees administrateur pour le JWT
                    userInfo =  {
                        uid : user.uid,
                        username : user.username,
                        admin : true
                    },
                    // les donnees pour la requête
                    options = {
                        host: settings.fb_ws,
                        path: settings.fb_root + '/coach',
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                            'Content-Length': data.length,
                            'Authorization': 'Bearer ' + jwt.sign(userInfo, settings.secret_key, {
                                        expiresInMinutes: 60 * 5
                            })
                        }
                    },
                    // definir la requete post
                    req = http.request(options, function(res) {
                        var msg = '';
                        res.on('data', function(chunk){
                            msg += chunk;
                        });
                        res.on('end', function(){
                            if (res.statusCode !== 200) {
                                console.log('Error : ' + res.msg);
                            }
                        });
                    });
                // lancer la requete post
                req.write(data);
                req.end();            
            });
        });
    };
    
    module.exports = plugin;
    

    Hope you can help and let me know if you want something else.
    Thanks in advance

  • Ok, so to reproduce the problem I prepare a basic angular page loaded as nodebb pluging. It will certainly be easiest to check what's going wrong with the 0.7.0 version.

    Here the plugin.json
    {
    "id": "nodebb-plugin-angtest",
    "name": "Test angular app within nodebb",
    "description": "An angular app to test if it works with nodebb 0.7",
    "library": "./library.js",
    "hooks": [
    { "hook": "static:app.load", "method": "init" },
    { "hook": "filter:header.build", "method": "addNavigation" }
    ],
    "staticDirs": {
    "static": "./static"
    },
    "less": [
    "static/style.less"
    ],
    "scripts": [
    "static/lib/angular/angular.js",
    "static/lib/app.js"
    ],
    "templates": "./templates"
    }

    The library.js

    "use strict";
    
    var plugin = {};
    
    function renderCustomPage(req, res, next) {
        // charger la page
        res.render('angular-page');
    }
    
    plugin.init = function(params, callback) {
        // ajouter les routes
        var app = params.router,
            middleware = params.middleware;
    
        app.get('/angular', middleware.buildHeader, renderCustomPage);
        app.get('/templates/angular.tpl', renderCustomPage);
        app.get('/api/angular', renderCustomPage);
    
        callback();
    };
    
    plugin.addNavigation = function(header, callback) {
        // ajouter le lien dans le menu de navigation principal
        header.navigation.push({
            class: '',
            route: '/angular',
            iconClass: 'fa fa-google',
            title: 'angular',
            text: 'angular'
        });
        callback(null, header);
    };
    
    module.exports = plugin;
    

    The app.js

    'use strict';
    
    var myApp = angular.module('myApp',[]);
    
    myApp.controller('myCtrl', ['$scope',function($scope) {
        $scope.firstName= "John";
        $scope.lastName= "Doe";
    }]);
    

    With this basic example I have the same behaviour. When I click on the google icon in the head menu angular module and controller are not loaded. I need to refresh the page (F5) to load the angular part.

    Thanks in advance for your help.

  • I would love to see a fix for this and maybe a barebone angular plugin to see how it all ties together? 🙂

  • Sorry, I forgot to get back to this. Can you publish your nodebb-plugin-angtest and I'll try running it first on 0.6x and then 0.7x and see if I can isolate the problem

  • hello,
    here you will find the code https://github.com/ricoud/nodebb-plugin-angtest
    Thanks in advance

  • @psychobunny did you have time to test the plugin?
    Thanks

  • any luck? @ricoud @psychobunny

  • Same with 0.7.1
    Additional info when calling directly the page (typing the address) everything works. It seems that angular isn't loaded when calling the page through menu.

  • Same behaviour with 0.8.0 😥

  • Ok I tried to use filter:scripts.get hook to load angularjs same thing
    Then I include angularjs inside the scripts object of the js.js file find in src/meta directory, same behaviour. 😞
    Can someone help to resolve this issue as I really want to include my site as a nodebb plugin.
    Thanks in advance 😄

  • Yikes. I always forget stuff I promise on community forum haha. Your best bet always is to create an issue on our github tracker.

    "Angular not working with nodebb" or something. I'm sorry for the delay

  • Don't forget to add as much information as you can 🙂


Suggested Topics


  • 0 Votes
    9 Posts
    190 Views

    @eeeee Probably local to the user computer 🙂

    You can generally use non-http URIs for launching different applications. If you were ever redirected from a desktop or mobile app to a browser to log in you've probably seen this in action. Windows actually uses some internally and usually transparently for the user (for example, to abuse their monopoly, MS started using microsoft-edge: scheme instead of https: in some links in Windows to only allow opening them in Edge. They didn't have to build some highly custom mechanism, just restrict other apps from registering this scheme), but usually can be just registered by applications you install. What they do also depends on the application - for example, I think calculator: only launches the calculator app (or at least the obvious way to write math doesn't work), but others can launch specific actions and even pass some information (for example, authentication token for the web login use case I mentioned). For example Spotify allows linking to artists, playlists, albums etc. via spotify: scheme and steam supports doing a ton of things via URI, including launching and even installing/uninstalling games.

    All you need is an <a> tag with the right href= set. So yeah, you can put that kind of a link in a widget, but if they wanted to have it be an action under a post, especially if it was supposed to include some information from the post, it wouldn't be that simple.
    (side note: NodeBB doesn't allow links using non-standard schemes in markdown, so you can't just put something like this in a post or signature)

  • 0 Votes
    3 Posts
    160 Views

    The main issue for me is that vscode will incorrectly highlight older templates.js syntax inside of an html tag, but that is mostly gone when you use benchpress syntax.

    A vscode plugin for highlighting the benchpress braces would be neat 😄

  • 0 Votes
    12 Posts
    364 Views

    do I have to read the source for each object to learns its methods?

    Yes, you will have to read the source code to see what methods are available.

  • Custom plugin

    Plugin Development
    3
    0 Votes
    3 Posts
    465 Views

    Do i need to run the link commands?

  • 0 Votes
    3 Posts
    1k Views

    Of course, however I just felt more comfortable trying to understand what each line of code did! This was more of a learning experience ~