Angular Directive - how to create a dynamic "page-options" - angularjs

I have a div which appears in all the pages of my website.
I want to display it everywhere but with different data according to the current state (I'm using UI router for angular1)
So I decided I should use a directive.
pageoptions.html
<div class="page-options">
<!-- if state = 'dashboard' -->
<h1>Dashboard</h1>
<a ui-sref="restore">Some Action</a>
<a ui-sref="backup">Another Action</a>
<!-- if state = 'Edit' -->
<h1>Edit</h1>
<a ui-sref="restore">Delete</a>
<!-- if state = 'settings' -->
<h1>Settings</h1>
<a ui-sref="backup">Settings</a>
</div>
pageoptions-directive.js
myApp.directive('pageinfo', function() {
return {
restrict: 'A',
templateUrl: "./components/pageinfo/pageinfo.html"
};
});
But I don't know how to insert the logic that I wrote in comments in the HTML.
I would love to get some advice on this

You should be able to inject the $state service into your directive and use it in your markup to check the current state, using ng-hide or ng-show. Hopefully this gets you pretty close:
myApp.directive('pageinfo', ['$state', function($state) {
return {
restrict: 'A',
templateUrl: "./components/pageinfo/pageinfo.html"
link: function(scope) {
scope.currentState = $state.current.name;
}
};
});
template:
<div class="page-options">
<!-- if state = 'dashboard' -->
<div ng-show="currentState == 'dashboard'">
<h1>Dashboard</h1>
<a ui-sref="restore">Some Action</a>
<a ui-sref="backup">Another Action</a>
</div>
<div ng-show="currentState == 'Edit'">
<!-- if state = 'Edit' -->
<h1>Edit</h1>
<a ui-sref="restore">Delete</a>
</div>
<div ng-show="currentState == 'settings'">
<!-- if state = 'settings' -->
<h1>Settings</h1>
<a ui-sref="backup">Settings</a>
</div>
</div>

maybe this would help:
directive.js:
myApp.directive('pageinfo', function() {
return {
restrict: 'EA',
scope: {
state: '=' // if you want take a sting in directive , use '#'
},
templateUrl: "./components/pageinfo/pageinfo.html"
};
});
template.html:
<div class="page-options">
<!-- if state = 'dashboard' -->
<div ng-show="state == 'dashboard'">
<h1>Dashboard</h1>
<a ui-sref="restore">Some Action</a>
<a ui-sref="backup">Another Action</a>
</div>
<!-- if state = 'Edit' -->
<div ng-show="state == 'Edit'">
<h1>Edit</h1>
<a ui-sref="restore">Delete</a>
</div>
<!-- if state = 'settings' -->
<div ng-show="state == 'settings'">
<h1>Settings</h1>
<a ui-sref="backup">Settings</a>
</div>
</div>
use directive like this:
<pageinfo state="get your state and put it in here"></pageinfo>

Related

Separation components and codereview in AngularJS

I started read about modules, components, directives. I would like to ask you to check the code and say how I could improve it. The main thing for me is to figure out how I can improve the transparency of this code. I would also like to learn how to split this code into files, and how it should look like the file structure in this case. I heard that I should make one file for components, But how should it look in the inside?
I would really be grateful for help.
because the most important in the life of the programmer is the code review! :)
(function () {
angular.module('app.navbar', [])
.component('navbar', {
bindings: {
user: '<'
},
controller: function ($scope) {
var navbar = this;
this.$onInit = function () {
navbar.toggle = false;
};
this.activeMenu = function (name, $event) {
this.blockClosingList($event);
if (navbar.toggle === true && $scope.name == name) {
navbar.toggle = !navbar.toggle;
}
else if (navbar.toggle === false) {
navbar.toggle = !navbar.toggle;
}
$scope.name = name;
}
this.blockClosingList = function ($event) {
$event.stopPropagation();
}
},
controllerAs: 'navbar',
template: `
/////// MENU LEFT SIDE ///////
<div class="main-navbar">
<div class="menu-left">
<div class="btns">
<span class="glyphicon glyphicon-align-left btn__glyph"></span>
<span class="btn--name">Tablice</span>
</div>
<div class="btns navbar__search">
<span class="glyphicon glyphicon-search"></span>
</div>
</div>
<div class="navbar__logo">
<span class="navbar--logo">Tasker</span>
</div>
/////// MENU RIGHT SIDE ///////
<div class="menu-right">
<a href="#" ng-click="navbar.activeMenu('Create', $event);">
<div class="btns">
<span class="glyphicon glyphicon-plus"></span>
</div>
</a>
<!-- MENU CREATE -->
<div class="menu menu--create" ng-click="navbar.blockClosingList($event)" ng-class="{active : name === 'Create' && navbar.toggle === true}">
<!--close-->
<menu-create></menu-create>
</div>
<!---->
<!--MENU CREATE BOARD-->
<div class="menu cb__menu-coordinate menu--create-board" ng-click="navbar.blockClosingList($event)" ng-class="{active : name === 'menuCreateBoard' && navbar.toggle === true}">
<span class="settings-menu__header-title">Utwórz Tablicę</span>
<div class="menu__wrapper--create-board center-block">
<menu-create-board></menu-create-board>
</div>
</div>
<!---->
<a href="#" ng-click="navbar.activeMenu('Notice', $event);">
<div class="btns">
<span class="glyphicon glyphicon-bell"></span>
</div>
</a>
<!--NOTIFICATIONS-->
<div class="menu menu--notice" ng-click="navbar.blockClosingList($event)" ng-class="{active : name === 'Notice' && navbar.toggle === true}">
<notifications></notifications>
</div>
<!---->
<a href="#" ng-click="navbar.activeMenu('Profile', $event);">
<div class="btn__circle">
<span class="btn--circle">B</span>
</div>
</a>
<!--MENU PROFILE -->
<div class="menu menu--avatar" ng-click="navbar.blockClosingList($event)" ng-class="{active : name === 'Profile' && navbar.toggle === true}">
<profile-menu></profile-menu>
</div>
</div>
</div>
<!---->
`
})
.component('profileMenu', {
require: {
parent: '^navbar'
},
template: `
<!--close-->
<span class="settings-menu__header-title">
{{$ctrl.parent.user.username}} {{$ctrl.parent.user.role}}
</span>
<ul class="menu__avatar--list">
<li>
Profil
</li>
<li>
<a href="/{{$ctrl.parent.user.username}}">
Karty</a>
</li>
<li>
<a href="/{{$ctrl.parent.user.username}}">
Ustawienia</a>
</li>
<li>
<a href="/logout">
Wyloguj</a>
</li>
</ul>
`
})
.component('notifications', {
require: {
parent: '^navbar'
},
template: `
<!--close-->
<span class="settings-menu__header-title">Powiadomienia</span>
<div class="menu__field--placeholder">
<span>Brak powiadomień</span>
</div>
`
})
.component('menuCreateBoard', {
require: {
parent: '^navbar'
},
template: `
<form>
<div class="form-group">
<label for="">Tytuł</label>
<input type="text" class="form-control">
</div>
<div class="form-group">
<label for="">Zespół</label>
<input type="text" class="form-control">
</div>
<span class="bottom--create-board">Ta tablica będzie Prywatna. Zmień</span>
<button class="btn btn-success">Utwórz</button>
</form>
`
})
.component('menuCreate', {
require: {
parent: '^navbar'
},
template: `
<span class="settings-menu__header-title">Utwórz</span>
<ul class="menu__create--list">
<li>
<a href="#" ng-click="$ctrl.parent.activeMenu('menuCreateBoard', $event);">
<div class="menu__wrapper-create">
<span class="menu-create__heading">Utwórz tablicę</span>
<span class="menu-create__desc">Tablica składa się z kart uporządkowanych w listach. Użyj jej do zarządzania projektami, śledzenia informacji i organizowania wszystkiego.</span>
</div>
</a>
</li>
<li>
<a href="#">
<div class="menu__wrapper-create">
<span class="menu-create__heading">Utwórz zespół</span>
<span class="menu-create__desc">Zespół składa się z ludzi i tablic. Organizuj z jego pomocą firmę, swoją drugą pracę, plany dla rodziny i spotkania z przyjaciółmi.</span>
</div>
</a>
</li>
</ul>
`
})
})();
https://gist.github.com/Turqus/2a791c6b86adfc8b6732711eaec2e23d
in main app:
var App = angular.module('TodoListApp', ['dndLists', 'app.navbar']);
First I would advise you to go here and learn how to use webpack:
https://webpack.github.io/
After you learn how to bundle your files, you should separate templates out of your js files and use templateUrl to load template - you can webpack template loader - works very good.
Then to improve it even further you can learn to use javascript export (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export) functionality and separate your code in files.
I like the following directory structure (for example you could use navbar instead of my-component-name):
/my-component-name - directory
my-component-name.component.js - contains my component bindings and templateUrls, also here we import controller for that particular component.
my-component-name.controller.js - sets up controller for component
my-component-name.html - template
my-component-name.scss/less - less/sass file containing isolated styles for that particular component - wrapped in my-component-name {...}
index.js - contains all code put together declaration of angular module, all imports (component,services, directives) and dependencies.
index.js example (you can name it navbar-module.js):
import NavbarComponent from './navbar.component'; // <- here you import component
const NavbarModule = angular
.module('navbar', [])
.component('navbar', NavbarComponent) // <- here you define your component
.name;
export default NavbarModule;
navbar.component.js example:
import templateUrl from './navbar.html';
import controller from './navbar.controller.js'
const NavbarComponent = {
bindings: {
user: '<'
},
templateUrl, // just a shorthand for writing templateUrl: templateUrl
controller, // same as above
}
export default NavbarComponent;
and finally navbar.controller.js:
class NavbarController {
constructor($scope, $timeout) {
this.$scope = $scope;
this.$timeout = $timeout;
}
$onInit() {
// set up your on init code here
this.toggle = true;
}
// set up your functions on controller - they will behave same and you will be able to access them via $ctrl.activeMenu() or $ctrl.blockClosingList() in your templates
activeMenu (name, $event) {
this.blockClosingList($event);
if (this.toggle === true && this.$scope.name == name) {
this.toggle = !this.toggle;
}
else if (this.toggle === false) {
this.toggle = !this.toggle;
}
this.$scope.name = name;
}
blockClosingList($event) {
$event.stopPropagation();
}
}
NavbarController.$inject = ['$scope', '$timeout']; // inject here and put $scope parameter in your constructor
export default NavbarController;
navbar.html - just use your html code it should work fine.
If you need to create some inner functions and keep access to this (controller) you can use arrow functions instead of function (param) {...} just use (param)=>{...} and inside brackets you will have scope of your controller.
One more thing - isolate your components as much as you can and try to communicate to parent components via $emit/$broadcasts instead of requiring parent component like in your example.
Keep in mind what I wrote above is in no way tested and most likely has bugs.
De facto best read about component based architecture in AngularJS can be found here, Todd Motto's styleguide:
https://github.com/toddmotto/angularjs-styleguide

Show/Hide Angular Directive On Button Click

I have a HTML page with three buttons. I also have two Angular directives. What I'm trying to accomplish is when button a is clicked, I want directive 1 to show and directive 2 to hide. When button 2 is clicked, I want directive 1 to hide and directive 2 to show. Here is are my directives:
.directive('topPosts', function () {
return {
restrict: 'E',
templateUrl: 'topPosts.html',
controller: 'PostsController'
}
});
.directive('otherPosts', function () {
return {
restrict: 'E',
templateUrl: 'otherPosts.html',
controller: 'PostsController'
}
});
here is my controller:
.controller('PostsController', ['$scope', 'PostsFactory', function($scope, PostsFactory) {
$scope.posts = [];
$scope.showTopPosts = true;
$scope.showOtherPosts = false;
$scope.topPosts = function() {
$scope.showTopPosts = true;
$scope.showOtherPosts = false;
};
$scope.otherPosts = function() {
$scope.showTopPosts = false;
$scope.showOtherPosts = true;
};
$scope.areTopPosts = function(posts) {
return posts.privacy === 'public' && posts.comments > 10 && posts.views > 9000 && posts.title.length < 40;
};
$scope.areOtherPosts = function(posts) {
return posts.comments <= 10 && posts.views <= 9000 && posts.title.length >= 40;
};
var init = function() {
PostsFactory.getPosts.success(function(data) {
$scope.posts = data;
});
};
init();
}]);
here is my partial that holds both directives:
<div class="container">
<top-posts ng-show='showTopPosts'></top-posts>
<other-posts ng-show='showOtherPosts'></other-posts>
</div>
and here is my index.html:
<body ng-controller='PostsController'>
<nav class="navbar navbar-inverse navbar-static-top" role="navigation">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<a class="navbar-brand" href="/main"></a>
</div> <!-- END NAVBAR-HEADER -->
</div> <!-- END CONTAINER-FLUID -->
</nav>
<div class="container">
<div class="btn-group" role="group">
<button type="button" class="btn btn-default" ng-click="topPosts()">Top Posts</button>
<button type="button" class="btn btn-default" ng-click="otherPosts()">Other Posts</button>
<!-- <button type="button" class="btn btn-default" ng-click="dailyTopPost()">Daily Top Post</button> -->
</div>
</div>
<div ng-view class='slide-animation'></div>
What's currently happening is the functions are getting called, but the directives are not showing/hiding on the button clicks.
I put work code in a codepen and it seems to be working fine http://codepen.io/arthur_souviron/pen/pEMMaJ/
I think your issue come from the way you store $scope.showTopPosts
and $scope.showOtherPosts. Try to store them inside an object instead of setting them directly under $scope
Eg :
$scope.data = {
showTopPosts: true,
showTopPosts: false
}
and in your template : <top-posts ng-show="data.showTopPosts"></top-posts>
Simply add a div before the directive:
<div class="container">
<div ng-show='showTopPosts'><top-posts ></top-posts></div>
<div ng-show='showOtherPosts'><other-posts ></other-posts></div>
</div>

losing scope of ng-model when using ng-include (AngularJS)

I am creating android app using ionic and angularjs. In app i used ng-include for include html content in my page.
checkbox.html:
<ul>
<li ng-repeat="opt in $parent.checkboxOptions">
<h4>
<label><input type="checkbox" name="checkbox" ng-model="$parent.checkboxAnswer" value="{{opt.option_value}}">{{opt.option_value}}</label>
</h4>
</li>
</ul>
surveyCtrl.js
$scope.checkboxOptions = params.QuestAnswers;
$scope.next = function(){
console.log($scope.checkboxAnswer);
}
its showing undefined and another thing when i am click on one checkbox its selecting all checkbox's also.
surveyCtrls.js
.directive('question', function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.question, function(QuesHtml) {
ele.html(QuesHtml);
$compile(ele.contents())(scope);
});
}
};
})
.directive('description', function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.description, function(DescHtml) {
ele.html(DescHtml);
$compile(ele.contents())(scope);
});
}
};
})
.directive('answers', function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.answers, function(AnswerHtml) {
ele.html(AnswerHtml);
$compile(ele.contents())(scope);
});
}
};
})
.controller('surveyLoad', function($scope){
var QuestType = SurveyData[QuestionIndex].question_type;
var DrawHTML = {
'QuestionText': 'Some Text',
'QuestionDesc': 'Some Desc',
'QuestAnswers': [{
option_value: 'Red',
}, {
option_value: 'Blue',
}];,
'scope' : $scope
};
checkbox(DrawHTML);
}
})
.controller('nextQuest', function($scope){
$scope.QuestNext = function(){
console.log($scope);
}
});
function checkbox(params){
var $scope = params.scope;
$scope.QuesHtml = "<p>"+params.QuestionText+"</p>";
$scope.DescHtml = "<p>"+params.QuestionDesc+"</p>";
$scope.checkboxOptions = params.QuestAnswers;
$scope.AnswerHtml = "<div ng-include src=\"'surveyTemplate/checkbox.html'\"></div>";
}
survey.html
<div class="row">
<div class="col question_div">
<div class="qus_head">
<p>Question: 1/10</p>
</div>
<h4 class="para"><span question="QuesHtml"></span> </h4>
<div class="qus_footer">
<p>Maxime quis.</p>
</div>
</div>
</div>
<div answers="AnswerHtml">
</div>
<div class="row">
<div class="col button_div">
<ul>
<li><img src="../img/next.png" style="width:70px;float:right" alt="next" ng-controller="nextQuest" ng-click="QuestNext()"></li>
<!-- <center><li><button style="align:center">Stop</button></li></center> -->
<li><img src="../img/pre.png" style="width:70px;float:left" alt="previous" ></li>
</ul>
</div>
</div>
is there any way to get the value of checked checkboxes and prevent to check all other checkboxe's ?
Using ng-includes create it's own scope.
By using the controller as syntax you can overcome this issue.
https://docs.angularjs.org/api/ng/directive/ngController
<div id="ctrl-as-exmpl" ng-controller="Controller as ctrl">
...
<li ng-repeat="opt in ctrl.checkboxOptions">
<h4>
<label><input type="checkbox" name="checkbox" ng-model="ctrl.checkboxAnswer" value="{{opt.option_value}}">{{opt.option_value}}</label>
</h4>
</li>
...
</div>
and in your controller :
$scope.checkboxOptions = params.QuestAnswers;
becomes
this.checkboxOptions = params.QuestAnswers;
and so on.
AngularJS plunker about this syntax :
https://plnkr.co/edit/DB1CpoWLUxQ9U8y558m1?p=preview
Regards,
Eric
You need some correction in the html.
<label><input type="checkbox" name="checkbox" ng-model="opt.option_value">{{opt.option_value}}</label>
With ng-model="$parent.checkboxAnswer" ,all checkbox will share common model.
So,it will get updated at the same time.
So,when you check/uncheck,it will check/uncheck everyone.
And ,you don;t need to use value,angular is used ng-model property for its value evaluation and not value
Yupii...i found the solution..
here is solution:
<ul>
<label ng-repeat="opt in $parent.checkboxOptions">
<li>
<h4>
<label><input type="checkbox" name="checkbox" ng-model="$parent.$parent.selected[opt.option_value]" >{{opt.option_value}}</label>
</h4>
</li>
</label>
</ul>
(function(angular) {
'use strict';
angular.module('MyApp', [])
.controller('AppCtrl', SettingsController1);
function SettingsController1($scope) {
$scope.options = {
'checkboxAnswer': ''
};
$scope.checkboxOptions= [{
type: 'phone',
value: '917xxxxxxxx'
}, {
type: 'email',
value: 'sajankumarv#example.org'
}];
}
})(window.angular);
radio box template file #radiobox.html
<ul>
<li ng-repeat="opt in checkboxOptions">
<h4>
<label><input type="checkbox" ng-model="options.checkboxAnswer" ng-true-value="'{{opt.value}}'" ng-false-value="''">{{opt.type}}</label>
</h4>
</li>
<h1>{{options.checkboxAnswer}}</h1>
</ul>
//in main index.html you simply include this radiobox.html using ng-include directive
<body ng-app="MyApp">
<div ng-controller="AppCtrl">
<div ng-include='"radiobox.html"'></div>
</div>
</body>
I think you are not in isolate scope so you don't have to use $parent for your case.I made a simple working demo for you to understand kindly refer the below link.
https://plnkr.co/edit/aQMZBln5t0Fipwn1qloN?p=preview

In AngularJS, how do I generate a piece of content from template and insert it in the DOM when a link is clicked

I have a number of links on the page, dynamically generated like so:
<a href="#" class="more-info-item" ng-click="showStats(item, $event)">
More information
</a>
I also have a simple custom template that should show an item's name:
<script type="text/ng-template" id="iteminfo.html">
<div class="item-name">
{{item.name}}
</div>
</script>
What I would like to do is: when the link is clicked, to dynamically compile the template, and insert it in the DOM right after the link. I tried using $compile within showStats method to compile the template, but I got an error that $compile wasn't found. How would I go about doing this (and also provide item as part of the scope for the newly generated template)?
Here is a solution using a custom directive which injects the item dynamically using ng-if:
View Solution with Plunker
html:
<script type="text/ng-template" id="iteminfo.html">
<div class="item-name" ng-if="item.visible">
{{item.name}}
</div>
</script>
<div ng-repeat="item in items" >
<a href="#" class="more-info-item" more-info="item" ng-click="item.visible =!item.visible">
More information
</a>
</div>
script:
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.items = [{name:'apples', visible:false},{name:'pears', visible:false},{name:'oranges', visible:false}];
})
.directive('moreInfo', function($compile,$templateCache) {
return {
restrict: 'A',
scope: '=',
link: function(scope, element, attr) {
var itemInfo = angular.element($templateCache.get('iteminfo.html'));
var lfn = $compile(itemInfo);
element.parent().append(itemInfo);
lfn(scope);
}
};
});
You can use the built-in ngInclude directive in AngularJS
Try this out
Working Demo
html
<div ng-controller="Ctrl">
<a href="#" class="more-info-item" ng-click="showStats(item, $event)">
More information
</a>
<ng-include src="template"></ng-include>
<!-- iteminfo.html -->
<script type="text/ng-template" id="iteminfo.html">
<div class="item-name">
{{item.name}}
</div>
</script>
</div>
script
function Ctrl($scope) {
$scope.flag = false;
$scope.item = {
name: 'Manu'
};
$scope.showStats = function (item, event) {
$scope.item = item;
$scope.template = "iteminfo.html";
}
}

How to add active class inside header navigation directive inside a controller?

Since my app has a navigation menu repeated in several pages, I made a directive for it. Now, the .active class isn't applied to current page anymore. Do I need to put the controller inside the directive?
Though not elegant, here's my code so far. Thanks.
page.html
<div class="app-container">
<div class="header clearfix">
<div ng-controller="NavmenuCtrl">
<nav-menu></nav-menu>
</div><!-- END NavmenuCtrl -->
</div><!-- /header -->
<div class="container"></div>
</div><!-- /app-container -->
navmenu directive
'use strict';
var app = angular.module('tempApp');
app.controller('NavmenuCtrl', function ($scope, $route) {
$scope.navMenuState = function($scope) {
$scope.navState = $route.current.navState;
};
});
app.directive('navMenu', function($route) {
return {
scope: {},
restrict: 'E',
replace: true,
templateUrl: 'views/partials/navmenu.html',
link: function navMenuState(scope, element, attrs, controller){
// Watch for the $location
scope.$watch(function() {
// do I need a scope.$watch?
}
};
});
navmenu.html partial
<div>
<div class="nav-container">
<ul class="navbar">
<li class="col-xs-5" ng-class="{ active:route.current.navState === 'pg1Active' }">
<a href="/#/pg1/">
<img src="images/icon1.png" class="center-block">
</a>
</li>
<li class="col-xs-5" ng-class="{ active:route.current.navState === 'pg2Active' }">
<a href="/#/pg2">
<img src="images/icon2.png" class="center-block">
</a>
</li>
</ul><!-- /.navbar -->
<div class="overflow-menu pull-right dropdown" ng-controller="NavDropdownCtrl">
<a class="dropdown-toggle">
<img ng-src="images/icon3.png">
</a>
<ul class="dropdown-menu">
<li ng-repeat="name in MenuItems">
<h4>
<a ng-href="#/{{ name | lowercase }}/">
{{name}}
</a>
</h4>
</li>
</ul>
</div><!-- /.overflow-menu -->
</div><!-- /.nav-container -->
</div>
app.js
'use strict';
angular.module('tempApp', [
...
])
.config(function ($routeProvider) {
$routeProvider
.when('/pg1/', {
templateUrl: 'views/pg1.html',
controller: 'MainCtrl',
navState: 'pg1Active'
})
.when('/pg2/',
{
templateUrl: 'views/pg2.html',
controller: 'MainCtrl',
navState: 'pg2Active'
})
I also found these related links:
Behavior of controller inside directives
AngularJS - Handle repeated fragments like Header and Footer
http://coder1.com/articles/angularjs-managing-active-nav-elements
You need to inject the $location service into your directive and check for routes there.

Resources