We've decided to use Mean.io in order to get a quick MEAN stack installation, but we're finding some troubles to get things done.
I'm trying to show a picture from my header js controller. But it doesn't show up. In fact, what i see when i open the inspector is just:
<img ng-src>
This is my HTML code, located in header.html:
<div class="page-header" data-ng-controller="HeaderController">
<div class="logo pull-left">
<a class="navbar-brand" ui-sref="home"><img ng-src="{{image.logo}}"/></a>
</div>
</div>
As you may see, i've put "ng-src" and the var taken from the js controller.
This is the HeaderController:
'use strict';
angular.module('mean.system').controller('HeaderController', ['$scope', '$rootScope', 'Global', 'Menus',
function($scope, $rootScope, Global, Menus) {
$scope.global = Global;
$scope.menus = {};
$scope.image = {
logo: 'assets/img/logo.png'
};
// Default hard coded menu items for main menu
var defaultMainMenu = [];
// Query menus added by modules. Only returns menus that user is allowed to see.
function queryMenu(name, defaultMenu) {
Menus.query({
name: name,
defaultMenu: defaultMenu
}, function(menu) {
$scope.menus[name] = menu;
});
}
// Query server for menus and check permissions
queryMenu('main', defaultMainMenu);
$scope.isCollapsed = false;
$rootScope.$on('loggedin', function() {
queryMenu('main', defaultMainMenu);
$scope.global = {
authenticated: !! $rootScope.user,
user: $rootScope.user
};
});
}
]);
The template var is working correctly because if i put {{image.logo}} elsewhere it prints "assets/img/logo.png".
Suggestions? What am i missing?
Thanks in advance!
I had the similar problem, I did the same as you have done and was getting 404 error for the PNG. When reviewed the other requests that are made, I came up with,
you must pass the "package name" in the parameter logo as well.
Just as;
$scope.image = {
logo: 'system/assets/img/logo.png'
};
Than the logo is displayed as desired
Related
This is my first post/question here on StackOverflow so if you see any improvement I can made - please give me an advice :).
Now let me dive into the issue.
For the sake of simplicity I removed any irrelevant portions of code and presented only necessary files.
//app.modules.js
if (typeof window.app == "undefined") {
window.app = angular.module("AppModule", []);
}
//app.services.js
window.app
.service("settingsOverlaySvc", function($rootScope) {
this.broadcastToggle = function() {
$rootScope.$broadcast("toggle-conf");
};
});
//settings-ctrl.js
window.app.controller("SettingsController", ["$scope", "$window", "$sce", "settingsOverlaySvc",
function($scope, $window, $sce, settingsOverlaySvc) {
$scope.visible = false;
$scope.open = false;
$scope.toggleSettings = function() {
$scope.open = !$scope.open;
};
$scope.broadcastToggle = function() {
settingsOverlaySvc.broadcastToggle();
};
$scope.$on("toggle-conf", function() {
console.log("toggle-conf received");
$scope.visible = !$scope.visible;
});
}
]);
angular.bootstrap($("div[ng-controller='SettingsController']").parent(":not(.ng-scope)"), ["AppModule"]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- Appended by JS - control1.html -->
<div>
<div ng-controller="SettingsController" ng-init="visible=true">
<span class="glyphicon glyphicon-cog" ng-class="{'settings-open': open}" ng-click="broadcastToggle();toggleSettings()">COG</span>
</div>
</div>
<!-- Appended by JS - control2.html-->
<div>
<div ng-controller="SettingsController" ng-cloak>
<div ng-if="visible">
<span class="glyphicon glyphicon-cog" ng-class="{'settings-open': open}" ng-click="toggleSettings()">COG</span>
<div ng-if="open">
<div class="divControl_2">Content_2</div>
</div>
</div>
</div>
</div>
The above snippet works as I expected - the $broadcast is called, all controllers receive the message and reacts. In my application the broadcast is received only by controller sending it. I think the problem is caused by dynamic HTML insertions.
The requirement is to render controls dynamically on page load. I'm using the following approach to generate content.
AddWidgets: function () {
var controlContainer1 = $("<section>", {
class: "sidebar-section",
id: "ctrl1-container"
});
var controlContainer2 = $("<section>", {
class: "sidebar-section",
id: "ctrl2-container"
});
$("aside.sidebar").append(controlContainer1);
$("aside.sidebar").append(controlContainer2);
$("#ctrl1-container").load("..\\assets\\ctrls\\control1.html");
$("#ctrl2-container").load("..\\assets\\ctrls\\control2.html");
}
I'm using the same controller because it shares the same logic for all controls.
I've read a lot materials about $broadcast and $emit functionality, guides on creating controllers, defining modules and services (that one gives me idea about creating service with $rootScope injected).
Now I'm thinking that generating angular content outside angular framework (AddWidgets function) can cause the problem (#stackoverflow/a/15676135/6710729).
What raised my spider sense alarm is when I've checked the JSFiddle example for the similar situation (http://jsfiddle.net/XqDxG/2342/) - no parent-child relation of controllers. When I peek at angulat scope of the controllers I can see that $$nextSibling and $$prevSibling properties are filled. In my case these are nulled [here].
Can you give me some guidelines how can I resolve my issue? I'm fairly new to AngularJS and learning as I'm developing the application.
I'm working on a site, and I started building it before I realized I needed some dynamic framework. After learning about AngularJS, I decided to use it, where I needed (not the whole site).
I have a very long script in JS, and I want to be able to get and set the variables from within AngularJS directives and controllers.
I found this answer, and it was quite good - I was able to get the variable from within the function. But when the variable changed outside the function, AngularJS' variable won't update.
My code looked something like this:
JS:
var app = angular.module('someName', []);
var currentPage = 'Menu';
app.controller('PageController', ['$window','$scope', function($window,$scope){
this.currentPage = $window.currentPage;
this.isPage = function(page){
return (page == this.currentPage);
};
}]);
function button1onClick(){
currentPage = 'Game';
}
HTML:
<div ng-controller="PageController">
<div id="Game" ng-show="page.isPage('Game')">
...
</div>
<div id="Menu" ng-show="page.isPage('Menu')">
...
</div>
</div>
(button1onClick was called when I clicked some button on the page)
The idea is that I have two dives I want to switch between, using a globle variable. 'Menu' page was visible at first but upon clicking I was supposed to see only the 'Game' div.
The variable inside the controller didn't upadte, but was only given the initial value of currentPage.
I decided to use the $window service inside the isPage function, but this didn't work either. Only when I called a function that tested the $window.currentPage variable, the pages switched - like I wanted:
JS:
var app = angular.module('someName', []);
var currentPage = 'Menu';
app.controller('PageController', ['$window','$scope', function($window,$scope){
this.isPage = function(page){
return (page == $window.currentPage);
};
this.button2onClick = function() {
$window.alert($window.currentPage);
}
}]);
function button1onClick(){
currentPage = 'Game';
}
HTML:
<button onclick="button1onClick()">CLICK ME</button> //Button 1
<div ng-controller="PageController">
<button ng-click="page.button2onClick">CLICK ME</button> //Button 2
<div id="Game" ng-show="page.isPage('Game')">
...
</div>
<div id="Menu" ng-show="page.isPage('Menu')">
...
</div>
</div>
So the only way I was able to update the pages is to call a function that tests the variable, thus updating the variable in AngularJS.
Is there a way to access a global variable without needing to test it to update it?
Am I doing something wrong? I don't want to convert my whole site to AngularJS-style, I like the code the way it is. Is AngularJS not the framework for me?
EDIT:
some things to clear out:
I'm new to AngularJS, so if you could explain what your answer does it would be great.
The whole reason why I do this instead of redirecting to another page is not to shut down socket.io 's connection
OP, take a look at UI Router.
var app = angular.module("app", ['ui.router']);
app.config(['$urlRouterProvider', '$stateProvider', function($urlRouterProvider, $stateProvider) {
$urlRouterProvider.otherwise('/main');
$stateProvider.state('main', {
controller: 'MainCtrl',
templateUrl: 'main.html',
url: '/main/'
}).state('game', {
controller: 'GameCtrl',
url: '/game/',
templateUrl: 'game.html'
});
}]);
HTML links:
<a ui-sref="main">Go to Main</a>
<a ui-sref="game">Go to Game</a>
View injection
<div ui-view="">
</div>
You should not use $window as a map object.
You should probably create a PageService:
angular.module('someName')
.factory('Page', [function(){
var currentPage = 'Menu';
return {
getPage: function() {
return currentPage;
},
isPage: function(page) {
return page === currentPage;
},
setPage: function(page) {
currentPage = page;
}
}
}]);
app.controller('PageController', ['Page','$scope', function(Page,$scope){
this.currentPage = Page.getPage();
this.isPage = Page.isPage;
this.button10Click = function(){
Page.setPage('Game');
}
}]);
HTML
<div class="button" ng-click="page.button10Click()">Game</div>
After reading malix's answer and KKKKKKKK's answer, and after researching a bit, I was able to solve my problem, and even write a better looking code.
To switch divs as I wanted in the example, I used ui-router, almost exactly the way KKKKKKKK did. The only difference is that I change state programmaticly - $state.go('menu')
To access global variables in other places in my code, I had to re-structure my whole code to fit AngularJS's structure, and used a Service, similarly to malix's answer:
app.factory('Data', function(){
var Data = {};
//DEFINE DATA
Data.stateChange = function(){};
Data.menuData = {};
Data.randomColors = ['#35cd96', '#6bcbef', '#E8E1DA', '#91ab01'];
/RETURN DATA
return Data;
});
It can be done using $rootScope. Variable initialize once can be accessible in other controllers.
function Ctrl1($scope, $rootScope) {
$rootScope.GlobalJSVariableA= window.GlobalJSVariable; }
Any controller & any view can access it now
function CtrlN($scope, $rootScope) {
$scope.GlobalJSVariableA= $rootScope.GlobalJSVariableA;
}
I an writing a service that is to be used in multiple independent websites. However, at some points it needs to trigger different code depending on what website it is used in. I want to keep this per website code separate from the base service.
Here is some example code demonstrating the design I want (although it isn't working):
var baseModule = angular.module('baseModule', []);
baseModule.service('baseService', function() {
this.func = function() {
return ["first",
/* TODO somehow get from appropriate
service in website module */
"FIXME",
"end"];
};
});
var website1 = angular.module('website1', ['baseModule']);
website1.service('website1Service', function() {
this.someCustomValue = function() {
// Note that while this is a constant value, in
// the real app it will be more complex,
// so replacing this service with a constant provider won't work.
return "someValue";
}
});
// TODO : somehow link website1Service.someCustomValue to baseService
var website2 = angular.module('website2', ['baseModule']);
website2.service('website2Service', function() {
this.anotherValue = function() { return "anotherValue"; }
});
// TODO : somehow link website2Service.anotherValue to baseService
// Testing code:
function makeTestController(expected) {
return ['$scope', 'baseService', function($scope, baseService) {
var result = baseService.func();
if (angular.equals(result, expected)) {
$scope.outcome = "Test Passed!";
} else {
$scope.outcome = 'Test failed...\n' +
"Expected: " + angular.toJson(expected) + '\n' +
"But got : " + angular.toJson(result);
}
}];
}
website1.controller('TestController1',
makeTestController(['first', 'someValue', 'end']));
website2.controller('TestController2',
makeTestController(['first', 'anotherValue', 'end']));
// since this test uses multiple angular apps, bootstrap them manually.
angular.bootstrap(document.getElementById('website1'), ['website1']);
angular.bootstrap(document.getElementById('website2'), ['website2']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<h3>Website 1</h3>
<div id='website1'>
<div ng-controller='TestController1'>
<pre>{{outcome}}</pre>
</div>
</div>
<div id='website2'>
<h3>Website 2</h3>
<div ng-controller='TestController2'>
<pre>{{outcome}}</pre>
</div>
</div>
I've thought of a few solutions to this, but none seem optimal.
The most obvious way would be to replace the baseService service with a provider, and allow it to be configured in each module. This seems to be the standard way of configuring services in other modules. However, I cannot access the website1Service and website2Service in in the provider functions, as services cannot be accessed in provider functions. This is noted in the docs:
During application bootstrap, before Angular goes off creating all services, it configures and instantiates all providers. We call this the configuration phase of the application life-cycle. During this phase, services aren't accessible because they haven't been created yet.
Another solution to work around this is use angular.injector to find the right service. However, the docs for angular.injector imply that you really only need this for interacting with third party libraries. So it appears there is a better way.
Finally, I could add a dependency to a nonexistant service (eg "baseServiceActions") in baseModule, and require a service with that name be implemented in website1 and website2. The dependency injection should then bind it all together when baseService is used. However, this is a pretty weird way of working, and would result in poor error messages if the baseServiceActions module wasn't implemented in a new website that used the baseModule module.
Is there a better way of doing this? If so, is it possible to change the example code I posted to get all the tests passing? Ideally none of the testing code should be changed.
I eventually worked out a fairly good solution to this. I created a service named "<serviceName>Settings", and added a setup function to it. I then call that setup function in a module run block in the module I want to use it in. Finally I have a validate method that is called in the service that uses the settings to ensure it is setup, and throws a nice error message if it isn't. This solved all the problems I had with the other solutions.
This is how my example problem would look with this solution:
var baseModule = angular.module('baseModule', []);
baseModule.service('baseService', ['baseServiceSettings', function(baseServiceSettings) {
baseServiceSettings.validate();
this.func = function() {
return ["first",
baseServiceSettings.getValue(),
"end"];
};
}]);
baseModule.service('baseServiceSettings', function() {
this.setup = function(getter) {
this.getValue = getter;
};
this.validate = function() {
if (!this.getValue) {
throw "baseServiceSettings not setup! Run baseServiceSettings.setup in a module run block to fix";
}
};
});
var website1 = angular.module('website1', ['baseModule']);
website1.run(['baseServiceSettings', 'website1Service', function(baseServiceSettings, website1Service) {
baseServiceSettings.setup(website1Service.someCustomValue);
}]);
website1.service('website1Service', function() {
this.someCustomValue = function() {
// Note that while this is a constant value, in
// the real app it will be more complex,
// so replacing this service with a constant provider won't work.
return "someValue";
}
});
var website2 = angular.module('website2', ['baseModule']);
website2.service('website2Service', function() {
this.anotherValue = function() { return "anotherValue"; }
});
website2.run(['baseServiceSettings', 'website2Service', function(baseServiceSettings, website2Service) {
baseServiceSettings.setup(website2Service.anotherValue);
}]);
// Testing code:
function makeTestController(expected) {
return ['$scope', 'baseService', function($scope, baseService) {
var result = baseService.func();
if (angular.equals(result, expected)) {
$scope.outcome = "Test Passed!";
} else {
$scope.outcome = 'Test failed...\n' +
"Expected: " + angular.toJson(expected) + '\n' +
"But got : " + angular.toJson(result);
}
}];
}
website1.controller('TestController1',
makeTestController(['first', 'someValue', 'end']));
website2.controller('TestController2',
makeTestController(['first', 'anotherValue', 'end']));
// since this test uses multiple angular apps, bootstrap them manually.
angular.bootstrap(document.getElementById('website1'), ['website1']);
angular.bootstrap(document.getElementById('website2'), ['website2']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<h3>Website 1</h3>
<div id='website1'>
<div ng-controller='TestController1'>
<pre>{{outcome}}</pre>
</div>
</div>
<div id='website2'>
<h3>Website 2</h3>
<div ng-controller='TestController2'>
<pre>{{outcome}}</pre>
</div>
</div>
I work with angularjs and bootstrap (I'm a beginner with angular). I have a lot of list-group in my app and I need to display a badge on each of them if the user hasn't click on the item yet. Once clicked for the first time the badge is removed (and should appear again even if the user refresh the page).
Since it's really recurrent in the app, I don't know of to properly do this...
I don't want to work with tons of boolean or session storage to check if the user has click or not. Do you see a pretty way to do it with angular?
FYI, this is my HTML right now:
<div class="list-group">
<a ng-repeat="item in itemList"
ng-click="select(item)"
ng-class="{active: selected === item}"
class="list-group-item">
{{item.name}} <span class="badge">{{item.badge}}</span>
</a>
</div>
And my controller:
app.controller('myCtrl', ['$scope',
function ($scope) {
$scope.itemList = [{
name: 'Lorem',
badge: 'New'
}, {
name: 'Ipsum',
badge: 'New'
}];
$scope.select = function (item) {
$scope.selected = item;
};
}
]);
The property badge should became empty at the first click and stay empty even if the user leave the app.
Its recommended you do this via some database or some third party sessions provider that complements your web application.
Alternatively, if you are stuck to implementing this on the client browser you can utilize the browser's local storage.
I am only showing here the angular service implemented that stores the stuff to local storage. The rest of the code is in the plunk below:
.service('todosvc', function($window) {
var stg = $window.localStorage.getItem('todos');
if(!stg || (typeof stg === 'string' && stg.length === 0)) {
todos = []
}
else {
todos = angular.fromJson(stg);
}
this.get = function() {
return todos;
};
this.update = function() {
$window.localStorage.setItem('todos', angular.toJson(todos));
};
})
Now if you happen to refresh the page, you will find that the same items remain.
Plunk: http://plnkr.co/edit/nvDmA9ipCNhL2DD14AGz?p=preview
Very new to Angular and after searching all over the show I simply cannot find a solution to my problem.
I have the following function in a directive/controller:
ModalIssueController.prototype.openModal = function (e, issue) {
this._dataService.getMain().then(function (model) {
this._$scope.modalIssue.open = true;
this._$scope.modalIssue.issue = model.getIssueById(issue);
this._windowService.setModalOpen(true);
}.bind(this));
};
The above function is called each time the user clicks on a different issue from a list. This opens a modal and shows the content related to issue.
When the modal is closed via a close button, the following is called:
ModalIssueController.prototype.closeModal = function () {
this._$scope.modalIssue.open = false;
this._windowService.setModalOpen(false);
this._$timeout(function () {
this._$location.url('/');
}.bind(this));
};
The problem is, even though I can see that the value of this._$scope.modalIssue.issue changes to reflect the new issue that was clicked, the content in the modal never changes, but instead, continues to show the data from the first selected issue ;(
Am I missing something here? Is there an additional step I need to add to ensure that the data in the template is updated?
Here is the directive 'set-up':
var ModalIssueDirective = function () {
return {
restrict: 'A',
replace: true,
scope: true,
controller: ModalIssueController,
templateUrl: '/app/lorax/directives/modal-issue.tpl.html'
};
};
And here is the template I am populating:
<section class="modal modal--fade-show modal--issue" ng-show="modalIssue.open" >
Close
<h1 class="detail-header-title">{{::modalIssue.issue.getTitle()}}</h1>
<div class="detail-main__copy">{{::modalIssue.issue.getNarrative()}}</div>
<header class="detail-link__header">
<h1>{{::modalIssue.issue.getMiscLocale().mozDoingLabel}}</h1>
</header>
<p class="detail-link__copy">{{::modalIssue.issue.getMozActionCopy()}}</p>
<a ng-if="::modalIssue.issue.getMozActionLink().length === 1" href="{{::modalIssue.issue.getMozActionLink()[0].url}}" class="btn detail-link__btn">{{::modalIssue.issue.getMozActionLink()[0].copy}}</a>
<a ng-if="::modalIssue.issue.getMozActionLink().length > 1" ng-repeat="link in ::modalIssue.issue.getMozActionLink()" href="{{link.url}}" class="detail-link__multiple">{{link.copy}}<span class="icon-arrow-right"></span></a>
<header class="detail-link__header">
<h1>{{::modalIssue.issue.getMiscLocale().yourDoingLabel}}</h1>
</header>
<p class="detail-link__copy">{{::modalIssue.issue.getYourActionCopy()}}</p>
<a ng-if="::modalIssue.issue.getYourActionLink().length === 1" href="{{::modalIssue.issue.getYourActionLink()[0].url}}" class="btn detail-link__btn">{{::modalIssue.issue.getYourActionLink()[0].copy}}</a>
<a ng-if="::modalIssue.issue.getYourActionLink().length > 1" ng-repeat="link in ::modalIssue.issue.getYourActionLink()" href="{{link.url}}" class="detail-link__multiple">{{link.copy}}<span class="icon-arrow-right"></span></a>
</section>
Thank you in advance for any assistance that can be provided here.
So, turns out :: in Angular templates defines a one-time binding. This essentially means that as soon as, for example, the following expression has been run:
{{::modalIssue.issue.getTitle()}}
and it returned a value that is not undefined, it is considered stable and the expression will never be run again. So, removing :: from each of the relevant lines in the template resolved the issue.
Docs: https://docs.angularjs.org/guide/expression (#see One-Time Binding)