Unable to change ng-href onClick event - angularjs

I have a footer with a button in my angular app. The button has an ng-href attribute which should change during the ng-click event to affect the routing mechanism. For some reason I cant make this work. The ultimate goal is to append numbers, 1 to five each button click.
The footer is a component:
app.component('footerx', {
bindings: {
},
templateUrl: 'views/footer.html',
controller: function () {
this.buttonText = "Next";
var self = this;
var i = 1;
this.changeHref= function () {
self.questionIndex=i;
i++;
}
}
});
Footer HTML:
<footer class="footer">
<div class="container">
<a class="btn btn-primary" ng-click="$ctrl.changeHref()" ng-href="#/quiz/{{questionIndex}}" id="btn">{{$ctrl.buttonText}}</a>
</div>
</footer>
Routing JS part:
...
.when("/quiz/:index", {
templateUrl: "views/questionPage.html",
controller: "questionController"
})
...
EDIT:
Right now the url does not fully change. This means it does not have the questionIndex. It looks like this:
http://localhost/myApp/#/quiz/

Use "#/quiz/{{$ctrl.questionIndex}}
<footer class="footer">
<div class="container">
<!-- REMOVE
<a class="btn btn-primary" ng-click="$ctrl.changeHref()"
ng-href="#/quiz/{{questionIndex}}" id="btn">
{{$ctrl.buttonText}}
</a>
-->
<!--ADD -->
<a class="btn btn-primary" ng-click="$ctrl.changeHref()"
ng-href="#/quiz/{{$ctrl.questionIndex}}" id="btn">
{{$ctrl.buttonText}}
</a>
</div>
</footer>

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

Controller doesn't get parent data

I'm doing a tutorial over angular 1.5 and I've gotten far into it and one of the sections seems broken concerning matching a current user to the author username. The class injects the User service and I think assumes I can inherit from a parent controller for the author but it comes up undefined. I tried injecting $scope then setting a variable to $scope.$parent.article (article is the object that has the author name in it) but this was still undefined. I checked the parent controller doing a console log on article and it does have the data that I am trying to get. Here is a link to my project if you want to look at the entire thing but I'll try to post just the relevant code below. https://github.com/RawleJuglal/flow_news_app/tree/front_end/src/js
Parent Controller (article.controller.js)
import marked from 'marked';
class ArticleCtrl {
constructor(article, $sce, $rootScope) {
'ngInject';
this.article = article;
console.log(this.article);
//THIS IS CONSOLE LOG
//{title: "Juglal For StackOverflow",
slug: "juglal-for-stackoverflow-ba400n",
body: "<p> Need the goods</p>",
createdAt: "2017-04-25T14:51:42.131Z",
updatedAt: "2017-04-25T14:51:42.131Z",
author:{
bio:"I'm a MEAN stack developer. But if I don't find a job in Oklahoma soon, I'll be learning C++/Sharp."
following:false
image:"https://media.licdn.com/mpr/mpr/shrinknp_200_200/p/6/000/1e9/0e2/3cd7175.jpg"
username:"RawleJuglal",....
}
// Update the title of this page
$rootScope.setPageTitle(this.article.title);
this.article.body = $sce.trustAsHtml(marked(this.article.body, { sanitize: true }));
}
}
export default ArticleCtrl;
Child Controller (article-actions.components.js)
class ArticleActionsCtrl {
constructor(Articles, User, $state) {
'ngInject';
this._Articles = Articles;
this._$state = $state;
//Code that causes the error because this.article.author.username is undefined
if (User.current) {
this.canModify = (User.current.username === this.article.author.username);
} else {
this.canModify = false;
}
}
}
let ArticleActions = {
bindings: {
article: '='
},
controller: ArticleActionsCtrl,
templateUrl: 'article/article-actions.html'
};
export default ArticleActions;
HTML(article.html) //Just in case this the problem
<div class="article-page">
<div class="banner">
<div class="container">
<h1 ng-bind="::$ctrl.article.title"></h1>
<article-actions article="$ctrl.article"></article-actions>
</div>
</div>
<div class="container page">
<div class="row article-content">
<div class="col-xs-12">
<div>
<div ng-bind-html="::$ctrl.article.body"></div>
</div>
<ul class="tag-list">
<li class="tag-default tag-pill tag-outline"
ng-repeat="tag in ::$ctrl.article.tagList">
{{ tag }}
</li>
</ul>
</div>
</div>
<hr />
<div class="article-actions">
<article-actions article="$ctrl.article"></article-actions>
</div>
<div class="row">
<div class="col-xs-12 col-md-8 offset-md-2">
<div>
<form class="card comment-form">
<div class="card-block">
<textarea class="form-control"
placeholder="Write a comment..."
rows="3"></textarea>
</div>
<div class="card-footer">
<img class="comment-author-img" />
<button class="btn btn-sm btn-primary" type="submit">
Post Comment
</button>
</div>
</form>
</div>
<div class="card">
<div class="card-block">
<p class="card-text">This is an example comment.</p>
</div>
<div class="card-footer">
<a class="comment-author" href="">
<img class="comment-author-img" />
</a>
<a class="comment-author" href="">
BradGreen
</a>
<span class="date-posted">
Jan 20, 2016
</span>
</div>
</div>
</div>
</div>
</div>
</div>
In fact, your example will work with angular 1.5 but not >1.6.
here is the reason :
Starting with angular 1.6, bindings are not yet set in the constructor. If you need them, move your code to the $onInit function.
Here is your new ArticleActionsCtrl :
class ArticleActionsCtrl {
constructor(Articles, User, $state) {
'ngInject';
this._Articles = Articles;
this._$state = $state;
this.User = User;
}
$onInit() {
if (this.User.current) {
this.canModify = (this.User.current.username === this.article.author.username);
} else {
this.canModify = false;
}
}
}
let ArticleActions = {
bindings: {
article: '='
},
controller: ArticleActionsCtrl,
templateUrl: 'article/article-actions.html'
};
export default ArticleActions;
I did not test it, do not hesitate to tell me if you have any problem with it.

Angular Scroll on Click

I am having an issue getting Angular Scroll to work.I am trying to scroll from the landing div to another section on the page with a button click. My code formatted really strangely, so let me know if further clarification is needed.
HTML
<div class="cover">
<div class="big-logo">
<i class="fa fa-trello"></i>
<span> My Kanban</span>
<br>
<button class="arrow" ng-click="bc.toLists()" du-smooth-scroll>
<i class="fa fa-angle-double-down fa-sm animated flash infinite" aria-hidden="true"></i>
</button>
</div>
</div>
<div class="story-board content">
<button class="add-list" ng-click="bc.addingList = !bc.addingList">
Add List
</button>
<div ng-if="bc.addingList">
<form ng-submit="bc.addList(bc.newList)">
<input style="margin-left: 5px" ng-model="bc.newList.name"/>
<button type="submit">+</button>
</form>
</div>
<div class="list" ng-repeat="list in bc.lists">
<button style="font-size: 10px;background: none;border:none; color: black" ng-click="bc.removeList(list)">x</button>
<list-component list-obj="list"></list-component>
</div>
</div>
init.js
angular.module('kanban', ['duScroll'])
app.js
angular.module('kanban')
.component('boardComponent', {
templateUrl: 'app/components/board/board.html',
controller: BoardController,
controllerAs: 'bc'
})
BoardController.$inject = ['EsService']
function BoardController(EsService) {
var bc = this;
bc.lists = EsService.getLists();
bc.addingList = false;
bc.removeList = function(list){
EsService.removeList(list.id);
}
bc.addList = function(list){
EsService.createList(list);
bc.newList = {};
}
bc.toLists = function() {
bc.cover = angular.element(document.getElementsByClassName('cover'));
bc.content = angular.element(document.getElementsByClassName('content'));
bc.cover.scrollTo(bc.content, 0, 1000);
}
}
For a JQuery free answer, you can use $anchorScroll
Create your anchor link:
<button ng-click="$ctrl.scrollTo('anid')">Scroll</button>
Create the anchor to scroll to:
<div id="anid">Land here</div>
Then your controller:
controller: function($anchorScroll) {
this.scrollTo = function(id) {
$anchorScroll(id);
}
}
I would recommend letting your controller handle the scrolling as opposed to the directives. You will have much tighter control that way and can therefore debug any issues.
Here's an example using the scrollToElement method. Once you have this logic in place you can switch it out to any method you need.
Here's a working demo
angular
.module('app', ['duScroll'])
.component('cmpExample', {
templateUrl: 'path/to/template.html',
controller: function($document) {
var vm = this;
vm.scrollTo = function(id) {
$document
.scrollToElement(
angular.element(document.getElementById(id)), 0, 1000
);
}
}
});
html
<button ng-click="$ctrl.scrollTo('target')">
<div id="target">Content further down the page</div>

Is it possible to use angular aria for aria-expanded?

I searched on the Internet and the Angular Aria documentation - it didn't mention about aria-expanded or aria-selected?
Is there a way of implementing this?
<a href ng-aria={'expanded': selected} & ng-aria={'selected': selected}></a>
which will make:
<a href aria-expanded="true" aria-selected="true"></a>
Thanks.
I used ng-bootstrap to do it. For example:
HTML
<p>
<button type="button" class="btn btn-outline-primary" (click)="isCollapsed = !isCollapsed"
[attr.aria-expanded]="!isCollapsed" aria-controls="collapseExample">
Toggle
</button>
</p>
<div id="collapseExample" [ngbCollapse]="isCollapsed">
<div class="card">
<div class="card-body">
You can collapse this card by clicking Toggle
</div>
</div>
</div>
TYPESCRIPT
import { Component } from '#angular/core';
#Component({
selector: 'ngbd-collapse-basic',
templateUrl: './collapse-basic.html'
})
export class NgbdCollapseBasic {
public isCollapsed = false;
}
Source: https://ng-bootstrap.github.io/#/components/collapse/examples
No need for a directive to set aria-expanded. Just use an expression:
link
<p ng-show="selected">content<p>

ui.bootstrap modal loading html but not showing anything

Its loading opening modal and also loading template specified. But not showing anything.
Check out the demo here : http://demo.hupp.in/food-admin
Go to [Products] and Search EnegiKcal >= 3500. Then click on manage. It will open pop up but template content is not loaded.
Also one other thing I noticed is that it returns HTTP 304 for template sometimes.
This is how I open modal :
/** Open Modal For add edit tags */
$scope.open = function (productId) {
var modalInstance = $modal.open({
templateUrl: 'views/some.html',
controller: tagsModalInstanceCtrl,
size: 'lg'
});
modalInstance.result.then(function (msg) {
}, function () {
// $log.info('Modal dismissed at: ' + new Date());
});
};
var tagsModalInstanceCtrl = function ($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close("hi");
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
Here is template code :
<div class="modal-header">
<h3 class="modal-title">I'm a modal!</h3>
</div>
<div class="modal-body">
<h3>Well, Hello there!</h3>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
Ok, it is pretty strange but it seems that your template is based on the master branch of https://github.com/angular-ui/bootstrap/blob/master/template/modal/window.html
and your sources on the tag 0.11.
https://github.com/angular-ui/bootstrap/blob/0.11.0/src/modal/modal.js
It is visible when you type $('.modal-content') in the console, you will see that it needs a modal-transclude directive, but in the sources there is no trace of this directive. Because, on 0.11 it directly uses the ng-transclude directive which is part of angular.
So, your code is correct, but the lib is not, try retrieving a correct version of it (maybe the last build of their repo is broken)
As a matter of fact, I did have a similar problem when switching to angularjs v1.2. The formerly working dialog didn't show, just like yours. Had to change the structure to look something like this to make it visible again:
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<div class="row">
<div class="col-lg-9">
<h3>{{header}}</h3>
</div>
</div>
</div>
<div class="modal-body">
<form name = "kontoForm" szp-focus="sifra">
<!-- Šifra -->
<szp-input id="sifra" text="Šifra" place-holder="šifra" value="konto.sifra" required="!konto.sifra"></szp-input>
<!-- Naziv -->
<szp-input id="naziv" text="Naziv" place-holder="naziv" value="konto.naziv" required="!konto.naziv"></szp-input>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-hide="!kontoForm.$valid || (mode == 'edit')" ng-click="okContinue()">OK i nastavi</button>
<button class="btn btn-primary" ng-hide="!kontoForm.$valid" ng-click="ok()">OK i zatvori</button>
<button class="btn btn-warning" ng-click="cancel()">Odustani</button>
</div>
</div>
</div>
I had to wrap everythin in a div with a modal-content class to make it work.
include .map files for jquery and angular in your /js folder .may be that will help
I have experienced this off and on for some reason as well.
I solved it by adding a CSS statement, after doing an inspection via Chrome's tools I found that for some reason the modal display was still set to hidden.
.modal {
display: block;
}
Take a look this link.
vm_main.showModal = {
mostrarErrores : false,
showModal : function(jsonError) {
var options={
tituloModal: jsonError.titleModal,
textoPrincipal: jsonError.mainMessage,
textoBtnAceptar: "Aceptar",
accionBtnAceptar: "vm_popup.cerrarPopup()",
};
commonPopUpService.getDisclaimerGeneric($scope, 'commonPopUpController', options);
}
};
http://plnkr.co/edit/EYDEG5WSmNwxQflsB21T?p=preview
I think will help you on how load a simple dinamic html as a modal.
Regards

Resources