I am trying to set up custom themeing on my app, so what I am doing is letting the user choose certain themes and it will change the apps theme holistically. I have a service which sends a piece of json and listens for it changing inside the controller of each view. Now this works fine within the view itself - for reference here's some snippets of the working code.
my factory controlling the theme -
angular.module('demoApp')
.factory('templatingFactory', function () {
var meaningOfLife =
{
'h1': '#ea6060',
'bg': '#ffffff'
};
return {
setTheme: function(theme) {
meaningOfLife = theme;
},
getTheme: function() {
return meaningOfLife;
}
};
});
One of my example controllers showing and changing the theme (and listening for changes)
$scope.themeStore = templatingFactory.getTheme();
console.log($scope.themeStore);
//send new themes
$scope.themeOne = function () {
var newT1 = { 'h1': '#8A6516',
'bg': '#000000'};
templatingFactory.setTheme(newT1);
};
$scope.themeTwo = function () {
var newT2 = { 'h1': '#ffffff',
'bg': '#ea6060'};
templatingFactory.setTheme(newT2);
};
$scope.themeThree = function () {
var newT3 = { 'h1': '#ea6060',
'bg': '#ffffff'};
templatingFactory.setTheme(newT3);
};
//listen for new themes
$scope.watchThemes = templatingFactory.getTheme();
$scope.$watch(templatingFactory.getTheme, function (newTheme) {
$scope.themeStore = newTheme;
});
and then on the template/view itself i do something like this -
<h3 ng-style="{ 'color' : themeStore.h1 }">Title</h3>
So my issue is that this works fine inside the view. However the ng-view tag is inside the body and outside of it are the body containers, as well as the header and footer menus that I would like to be able to hook onto with this theme object. So my quesiton is, is there any way to use that scope outside of the ng-view? I don't think it's possible but I'm not sure how else I could access and put a ng-style on the header footer and body to change some css on it with this method I am using.
So for a simple reference it looks like this -
<body ng-app="myApp">
<div class="container">
<div class="header" ng-style="{ 'background-color' : themeStore.bg }">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<i class="fa fa-bars"></i>
</button>
<div class="headerLogo"></div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
</ul>
</div>
</div>
<div ng-view class="velocity-opposites-transition-slideUpIn" data-velocity-opts="{ duration: 500 }" ng-style="{ 'background-color' : themeStore.bg }"> </div>
<div class="footer">
<p></p>
</div>
</div>
</body>
So as you can see - I'm trying to hook onto the header to change the background color, which does not work like this. What I noticed though, is if I put it on the ng-view div itself, it works alright.
I would much appreciate any input as I've been stuck on this for a while. Thank you for taking the time to read!
The DOM elements outside of your ng-view must have controllers of their own, with templatingFactory injected as a dependency.
First I would modify the html like so:
<div class="header" ng-controller="headerController" ng-style="{ 'background-color' : themeStore.bg }">
Then add headerController to your module:
angular.module('demoApp').controller('headerController', function($scope, templatingFactory){
$scope.themeStore = templatingFactory.getTheme();
$scope.$watch(templatingFactory.getTheme, function (newTheme) {
$scope.themeStore = newTheme;
});
});
A more reusable solution would be to create a directive that adds this controller functionality to whatever DOM element it is applied to, but the above is a little more straight forward.
I think the best way to have angular functions and variables outside ui-view or ng-view is to use a global service. in this case you should do your theming logic inside 'templatingFactory'. Then inject this service not in your controllers, but in your module.
angular.module('demoApp').run(['$rootScope', 'templatingFactory', function($rootScope, templatingFactory){
$rootScope.templatingService = templatingFactory;
}]);
So your service will be avaible in the $rootScope. now you can use it this way.
<body ng-app="myApp">
<div class="container">
<div class="header" ng-style="{ 'background-color' : templatingService.getTheme().bg }"> </div>
</div>
</div>
ps: I'm relative new in angular too, so I don't know nothing about good/wrong practices!
For the directive approach, a simple example might look something like this:
demoApp.directive('themeHeader', function (templatingFactory) {
return {
restrict: 'A',
link : function (scope, element, attrs) {
scope.$watch(templatingFactory.getTheme, function () {
element.css('background-color', newTheme.bg);
});
}
}
});
and the html would look like this:
<div theme-header>
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"><i class="fa fa-bars"></i></button>
<div class="headerLogo"></div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right"></ul>
</div>
Related
I'm new in Angularjs and I have an app, with some "projects", which have a local menu displayed on some pages. Index.html contains the main navbar with footer :
<body ng-app="ysi-app" ng-controller="MainController">
<div class="page-content">
<div class="row">
<div class="col-md-2">
<div class="sidebar content-box" style="display: block;">
<ul class="nav">
<!-- Main menu -->
<li ng-if="isAuthenticated()">{{name}}</li>
<li class="current">Dashboard</li>
<li>Projects</li>
</ul>
</div>
<div ng-if="isAuthenticated() && displayProjectMenu == true" ng-include="'views/localMenu.html'" ng-controller="LocalMenuController">
</div>
</div>
<div ng-view></div>
</div>
So I have a nested controller LocalMenuController for the local menu and a main controller. The project controller sets the datas :
angular.module('ProjectCtrl',[]).controller('ProjectController',function($scope,$location, ProjectService,$route, AuthenticationService, $rootScope){
$scope.setProjectDatas = function(projectName, projectId){
ProjectService.setName(projectName);
$rootScope.projectId = projectId;
};});
I set the id of one project to the $rootScope for testing (I have a Service which will do that better) and get it in the LocalMenuController :
angular.module('LocalMenuCtrl',[]).controller('LocalMenuController', function($scope, ProjectService, $rootScope) {
$scope.projectId = '';
$scope.projectId = $rootScope.projectId;
});
I display projects in a table and when I clicked on one of it, the function setProjectDatas(name,id) is called. The problem is when I clicked on one project, the id of the project is correct and set but when I go previous and clicked on another project, the id is the old id of the project previously clicked. The datas are not updating. I googled my problem but found nothing on it.
I think the LocalMenuController is called only one time but not after.
What am I doing wrong ?
Thank you
UPDATE
I've created a Directive which displays the template but it's still not updating the partial view localMenu.
LocalMenu Directive :
angular.module('LocalMenuCtrl',[]).controller('LocalMenuController', function($scope, ProjectService, $rootScope) {
console.log('-> LocalMenu controller');
})
.directive('localMenu', function($rootScope){
return {
templateUrl: '/YSI-Dev/public/views/partials/localMenu.html',
link: function(scope){
scope.projectId = $rootScope.projectId;
}
};
});
A part of index.html
<div ng-if="isAuthenticated() && displayProjectMenu == true" ng-controller="LocalMenuController">
<div local-menu></div>
</div>
Partial view localMenu :
<div class="sidebar content-box" style="display: block;">
<ul class="nav">
<li><i class="glyphicon glyphicon-list-alt"></i> Backlog</li>
<li><i class="glyphicon glyphicon-user"></i> My team </li>
</ul>
</div>
I'm trying to get the projectId from the $rootScope and inject it in the <a href="#/project/{{projectId}}" but I have some troubles. What's the way to do that ?
First of all, try using directives instead of ng-controller. You can encapsulate your code and template into a unit. You can also try creating a component. Pass some data to the directive/component and Angular will take care of updating the template and running whatever needs to run within the directive/component. (Given that you used two-way data-bindings)
From the code above, I cannot see what would trigger LocalMenuController to run again.
I would like a directive that dynamically knows if I'm following the user in my App.
I have a resource to get the currentUser
this.following = function () {
var defer = $q.defer();
var user = $cookies.getObject('currentUser');
UserResource.get({id: user.id}).$promise.then(
function (data) {
defer.resolve(data.following);
});
return defer.promise;
};
This is in one of my services. It returns all users that I'm following.
When instantiating my controller I fetch the users I follow within my app:
UserService.following().then(
function (data) {
$scope.following = data;
});
I would like to move that into a directive so that I can easily reuse it somewhere else in my app.
This is the HTML I am using right now (and it's not really beautiful) :
<div class="item" ng-repeat="user in users">
<div class="right floated content">
<div ng-show="isFollowing(user)" class="ui animated flip button" tabindex="0"
ng-click='unFollow(user)'>
<div class='visible content'>
Following
</div>
<div class="hidden content">
Unfollow
</div>
</div>
<div ng-hide="isFollowing(user)" ng-click="follow(user)" class="ui button">Follow</div>
</div>
</div>
But instead something like :
<div class="item" ng-repeat="user in users">
<um-follow-button um-user="user"></um-follow-button>
</div>
then depending if I'm following the user or not then render one of the two options.
I don't know if I will have to use a controller in my directive.
I have looked at : https://gist.github.com/jhdavids8/6265398
But it looks like a mess.
I'm creating my first Angular app and ran into a couple things that I just can't figure out. Whenever I include this:
<button ng-hide="results.length === projects.length" ng-click="limit = limit +3; gotoBottom()">Show More</button>
Inside of my template the app refuses to load but if I paste it anywhere outside of the template it works fine. I'd like to keep the button inside the template if at all possible so what on earth am I doing wrong?
Also, I'd like that button to also scroll to the #footer div and the ng-click doesn't seem to run this bit code:
$scope.gotoBottom = function() {
$location.hash('footer');
$anchorScroll();
};
I've created a Plunker of my code that can be found here:
https://plnkr.co/edit/MP4Pp4WLcn5EFb3pTEXx
By "template" if you are talking about projects template. Here is what you need to do.
Explanation:
The projects template need to have only one root element, so I added a div to wrap your project listing and show more button.
<div>
<div class="cards" ng-init="limit = 3">
<div class="card" ng-repeat="project in projects | limitTo: limit as results">
<div class="card-image">
<img src="{{project.img}}" alt="{{project.name}}" />
</div>
<div class="card-copy">
<h2>{{project.name}}</h2>
<p>{{project.desc}}</p>
<p><i class="fa fa-location-arrow"></i></p>
</div>
</div>
</div>
<button ng-hide="results.length === projects.length" ng-click="limit = limit +3; gotoBottom()">Show More</button>
<div id="footer" name="footer"></div>
</div>
For auto scroll: inject $timeout service
Explanation:
You did not had any div named footer so I added one just below the show more button and added a 100ms timeout, so that after your 3 projects load, it will scroll to the footer div. $timeout is very necessary because need to first render your projects and then scroll.
$scope.gotoBottom = function() {
$timeout(function() {
$location.hash('footer');
$anchorScroll();
}, 100);
};
Working Plunker: https://plnkr.co/edit/U3DDH57nh0Mqlpp2Txi4?p=preview
Hope this helps!
change the below code in projects.js
angular.module('portfolioApp')
.directive('projects', function() {
return {
templateUrl: 'projects.html',
controller: 'mainCtrl',
replace: true // remove directive tags
};
});
to
replace: false
it should do the trick. Plunker Link here
I had a hard issue figuring out on how to hide and show icon/text with angular code. I am completely new to angular and tried hard on the below fiddle code. How do I hide + or minus icon with .closest in such dom scenarios.
<div ng-controller="MyCtrl">
{{name}}
<div data-toggle="collapse" aria-expanded="true" data-target="#list-item-line-0" id="expandCollapseChild" ng-click="addExpandCollapseChildIcon()">
<div>
<div>
<label>
<div>
<span class="icon-expand">-</span>
<span class="icon-collapse">+</span>
</div>
<div>
Click me to hide minus icon
</div>
</label>
</div>
</div>
</div>
</div>
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.name = 'Superhero';
$scope.addExpandCollapseChildIcon = function() {
alert('');
if (angular.element('#expandCollapseChild').hasClass('collapsed')) {
angular.element(this).closest('.icon-collapse').css('display', 'none');
} else {
if (angular.element('#expandCollapseChild').hasClass('collapsed')) {
angular.element(this).closest('.icon-collapse').css('display', 'block');
}
}
}
In Angular, this is the wrong approach. You shouldn't actually show or hide elements inside the controller. That's applying a jQuery style (working directly on the DOM) to Angular.
In Angular, you'd use something like ng-if, ng-show or ng-class, all of which can link back to a property on the scope object that is accessible via the controller.
Here are some examples:
<div ng-if="myProp === 'ShowMe'">
<div ng-show="myProp === 'ShowMe'">
<div ng-class="{myCssClass: myProp === 'ShowMe'">
Inside your controller, you'd have something like this:
function MyCtrl($scope) {
$scope.myProp = 'ShowMe';
$scope.addExpandCollapseChildIcon = function(newPropValue) {
$scope.myProp = newPropValue;
}
}
Here's some links to documentation on ng-if, ng-show and ng-class:
https://docs.angularjs.org/api/ng/directive/ngIf
https://docs.angularjs.org/api/ng/directive/ngShow
https://docs.angularjs.org/api/ng/directive/ngClass
AngularJS has a bunch of angulary ways of doing things, your question for example might look like this:
var app = angular.module("app", []);
app.controller("ctrl", function($scope) {
$scope.collapsed = true;
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="ctrl">
<span ng-bind="collapsed ? '+' : '-'"></span>
</div>
</div>
It watches a model and changes it's appearance based on that model using the ternary operator within ng-bind.
The way you defined your app and controller was incorrect. There's a bunch of different ways to do this as you can see from the answers.
I took this approach:
<div ng-app='myApp' ng-controller="MyCtrl">
{{name}}
<div>
<div>
<div>
<label>
<div>
<span ng-show='(collapsed != false)' class="icon-expand">-</span>
<span ng-show='(collapsed == false)' class="icon-collapse">+</span>
</div>
<div ng-click='collapsed = !collapsed'>
Click me to hide minus icon
</div>
</label>
</div>
</div>
</div>
</div>
<script>
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', function ($scope) {
$scope.name = 'Superhero';
$scope.collapsed = false;
});
</script>
Create a scoped variable that indicated whether or not it is collapsed . Then change that variable and the ng-shows will react.
im new to angular and was struck linking dropdown selected to ng-click button
<div class="col-xs-2">
<select name="cars" ng-model="dropdown_data">
<option>email</option>
<option>phone</option>
<option>username</option>
</select>
</div>
<br />
<div class="col-xs-4">
<button type="button" class=" " data-ng-click="search_{{dropdown_data}}()">Search</button>
</div>
<script>
var ng = angular.module('myApp', []);
ng.controller('ctrl', function($scope, $http) {
$scope.search_phone = function() {
alert("phone")
}
$scope.search_email = function() {
alert("email")
}
})
</script>
this seems to be fairly simple but im not sure what im doing wrong...Im not able to show alerts depending on selected dropdown
Plunker link http://plnkr.co/edit/Iicm9tvfizXxNl3MwtZI?p=preview
any help is much appriciated...thanks in advance
There were few things that you needed in the plunkr.
Firstly you need to define on the HTML that it is in fact an Angular Application (via the ngApp attribute).
Secondly you need to define a controller for your view (via the ngController attribute).
Once you have those things in place, you need to understand what this would do
ng-click="search_{{dropdown_data}}()"
If you think about how ng-click works, it registers a function on click. This happens on the compile phase of a directive (as you can see on its sourcecode).
This means that when the directive compiles, it will register the function with the name search_{{dropdown_data}} and even though the dropdown_data will be interpolated later on when its value changes, the originally bound function won't update.
However if you had dropdown_data as an attribute or as a key to a map of functions that will work. Here an example of how you may do that:
$scope.search = {
phone: function() {
alert("phone")
},
email: function() {
alert("email")
}
};
and on the button: data-ng-click="search[dropdown_data]()"
Here a working plunkr: http://plnkr.co/edit/u4vJj2a0r1a95w64crHM?p=preview
I am also new in angular but have used same functionality without search button direct given anchor link try this if you need,
<div ng-app="myApp">
<div ng-controller="setContent">
<div class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
Subject
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
<li ng-repeat="a in subjects">{{a}}</li>
</ul>
</div>
var app = angular.module('myApp', []);
app.controller('setContent', function($scope, $http) {
$scope.subjects = ['Math', 'Physics', 'Chemistry', 'Hindi', 'English'];
});
});
The problem is this data-ng-click="search_{{dropdown_data}}()". Better to pass a value to the function like this:
<button type="button" data-ng-click="search(dropdown_data)">Search</button>
$scope.search = function(type) {
alert(type)
}
also dont forget ng-app and ng-controller.
see the plunker