I have a couple of functions that I've written in Angular to display multiple ng-show attributes on a page via scopes. I attempted to put all three into a single function using if else statements, but I couldn't get the function to fire for a second time. I'm wondering if there's a way to do this in a single function, and what the best practice for this sort of functionality is.
JS Fiddle is here - http://jsfiddle.net/ezy_/01L050mr/2/
Here's the JS that's currently spread across 3 different functions.
var app = angular.module('app',[]);
function AppCtrl($scope) {
$scope.skillsState = false;
$scope.rolesState = false;
$scope.qualsState = false;
$scope.skillsStateTrigger = function() {
$scope.skillsState = true;
$scope.rolesState = false;
$scope.qualsState = false;
};
$scope.rolesStateTrigger = function() {
$scope.skillsState = false;
$scope.rolesState = true;
$scope.qualsState = false;
};
$scope.qualsStateTrigger = function() {
$scope.skillsState = false;
$scope.rolesState = false;
$scope.qualsState = true;
};
}
Do I need a $watch or $apply function to trigger updates if I set the function to fire from a single statesTrigger(resp) function?
Have a look here: updated with 1 function
$scope.triggerState = function (state) {
if (state == 'skills') {
$scope.skillsStateTrigger();
} else if (state == 'roles') {
$scope.rolesStateTrigger();
} else if (state == 'quals') {
$scope.qualsStateTrigger();
}
}
<button ng-click="triggerState('skills')">Skills</button>
<button ng-click="triggerState('roles')">Roles</button>
<button ng-click="triggerState('quals')">Quals</button>
You can even be more specific in the HTML itself like this: State variable
<body ng-app="app" class="ng-scope">
<div class="container ng-scope" ng-controller="AppCtrl">
<button ng-click="currentState = 'skills'">Skills</button>
<button ng-click="currentState = 'roles'">Roles</button>
<button ng-click="currentState = 'quals'">Quals</button>
<div class="row" ng-show="currentState == 'skills'" style="display: none;">
Skills State Active
</div>
<div class="row" ng-show="currentState == 'roles'" style="display: none;">
Roles State Active
</div>
<div class="row" ng-show="currentState == 'quals'" style="display: none;">
Quals State Active
</div>
</div>
</body>
Here is your refactor. The trick is to use an object to store the 3 states.
js:
var app = angular.module('app',[]);
function AppCtrl($scope) {
$scope.states = {
skills: false,
roles: false,
quals: false
};
$scope.trigger_state = function(state) {
for(var i in $scope.states) {
$scope.states[i] = (i == state);
}
};
}
html:
<body ng-app="app" class="ng-scope">
<div class="container ng-scope" ng-controller="AppCtrl">
<button ng-click="trigger_state('skills')">Skills</button>
<button ng-click="trigger_state('roles')">Roles</button>
<button ng-click="trigger_state('quals')">Quals</button>
<div class="row" ng-show="states.skills" style="display: none;">
Skills State Active
</div>
<div class="row" ng-show="states.roles" style="display: none;">
Roles State Active
</div>
<div class="row" ng-show="states.quals" style="display: none;">
Quals State Active
</div>
</div>
</body>
In that case it's kinda a bad idea to use 3 states, you could use 1 one variable to store "state" = "mystate" for example (since the states are exclusive).
Related
I created one TODO List in AngularJS but I didn't finish the update function. I have some difficulty.
How do I create the update function?
Link: https://plnkr.co/edit/XfWoGVrEBqSl6as0JatS?p=preview
<ul class="list-todo">
<li ng-repeat="t in tasks track by $index">
<div class="row">
<div class="six columns">
<p>{{ t }}</p>
</div>
<div class="three columns">
<button class="button" ng-click="update()">update</button>
</div>
<div class="three columns">
<button class="button" ng-click="delete()">x</button>
</div>
</div>
</li>
</ul>
Angular Code:
angular.module('todoTest', [])
.controller('todoController', function($scope) {
$scope.tasks = [];
$scope.add = function() {
$scope.tasks.push($scope.dotask);
}
$scope.update = function(){
$apply.tasks.push($scope.dotask);
}
$scope.delete = function() {
$scope.tasks.splice(this.$index, 1);
}
})
If you want to update the value you have to pass as parameter the position of the task inside the tasks array ($index is the position):
<button class="button" ng-click="update($index)">update</button>
And then the update function would be:
$scope.update = function(index){
$scope.tasks[index] = $scope.dotask;
}
Is that what you needed?
Button for updating. Visible only while updating.
<div class="row">
<button type="submit" class="u-full-width button button-primary">Criar Tarefa</button>
<button ng-show="updateMode" ng-click="update()" type="button" class="u-full-width button button-primary">Update</button>
</div>
New update function.
$scope.update = function(index) {
if ($scope.updateMode) {
$scope.tasks[$scope.updateIndex] = $scope.dotask;
$scope.updateMode = false;
} else {
$scope.dotask = $scope.tasks[index];
$scope.updateMode = true;
$scope.updateIndex = index;
}
If you click update on the task it will make the big update button visible and bring the old todo to the input. After hitting the big update button the todo task will update.
Plunker
I can't seem to get my filter to only execute on a button click, it executes the first time and then filters automatically based on whatever text is in the box. Can someone help me out?
<section class="searchField">
<input type="text" ng-model="searchTerm">
<button type="button" ng-click="search()">Search</button>
</section>
<section ng-show="!resultsHidden">
<div class="no-recipes" ng-hide="players.length">No results</div>
<article class="playerSearch" ng-repeat="player in players | filter: filter" ng-cloak>
<h2>
{{player.name}}
</h2>
</article>
</section>
Javascript:
$scope.myFilter = function (player) {
var found = [];
for(player in players) {
if (player.name === $scope.searchTerm || player.number === $scope.searchTerm){
found.push(player);
}
}
return found;
};
function search(){
$scope.filter = $scope.searched;
$scope.resultsHidden = false;
}
$scope.searched = function(player){
if (!$scope.searchTerm || (player.number == $scope.searchTerm || (player.name.toLowerCase().indexOf($scope.searchTerm.toLowerCase()) != -1))){
return true;
}
return false;
};
var app = angular.module("app",[])
app.controller('ctrl',['$scope', function($scope){
$scope.resultsHidden = false;
$scope.players = [
{"name": "alpha"},
{"name": "beta"},
{"name": "gama"}
];
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<section class="searchField">
<input type="text" ng-model="searchTerm">
<button type="button" ng-click="filter = searchTerm;resultsHidden = !resultsHidden">Search</button>
</section>
<section ng-show="resultsHidden">
<div class="no-recipes" ng-hide="players.length">No results</div>
<article class="playerSearch" ng-repeat="player in players | filter:{name:filter}" ng-cloak>
<h2>
{{player.name}}
</h2>
</article>
</section>
</div>
try this
<button type="button" ng-click=" filter = searchTerm">Search</button>
First of all, you should create the filter like this:
angular.module('myApp')
.filter('my-filter', function( /* dependencies goes here */ ){
return function (input, isFilterActive) {
if (isFilterActive){
// filter your data
// your list will go into 'input' variable
// return filtered list
}
return input; //return data unfiltered;
}
});
And your html should look something like this:
<p ng-repeat="x in subcategories | my-filter:isFilterActive">
{{x.text}}
</p>
Where 'isFilterActive' variable can be placed on $scope. If 'isFilterActive' is true, your list will be filtered.
You can create a button that changes the value of it.
You can also check angular filters for more details or this tutorial: https://scotch.io/tutorials/building-custom-angularjs-filters
Have fun.
This causes the same item to be added to the array - which is used for querying - potentially twice. When the modal is closed and opened again and filtering is not used, everything is fine (this is because my modals are simply within the HTML, I just ng-hide them on click). When an item which has already been added, but its checkmark has been lost due to filtering, is checked again two of the same items are added. When it is unchecked both are removed, unless it is the last item in the array, this cannot be removed (I'm aware this is my slapdash logic). Below is the relevant HTML and JavaScript code (famous last words).
HTML:
<div style="display: inline-block;" ng-controller="modalCtrl">
<button ng-click="showModal = !showModal">Entities</button>
<div ng-init="showModal=false">
<div class="modal fade in" aria-hidden="false"
style="display: block;" ng-show="showModal">
<div class="modal-dialog">
<div class="modal-content">
<strong>ENTITIES</strong>
<div>
<div>
<input type="text" placeholder="Search" ng-model="simpleFilter">
<button type="button" ng-click="showModal=false">Ok</button>
</div>
</div>
<br>
<div ng-repeat="entity in entityArray | filter:simpleFilter">
<label> <input
style="display: inline-block; margin-top: 5px"
type="checkbox" ng-model="entityChecked"
ng-change="getEntityFromModal(entity, entityChecked)" /> <a>{{entity}}</a>
</label>
</div>
</div>
</div>
</div>
</div>
</div>
Modal Controller:
angular.module("app").controller("modalCtrl", ["$scope", "shareDataService", "getDataService", function ($scope, shareDataService, ngDialog, getDataService) {
$scope.entityArray = shareDataService.getEntityArray();
$scope.depotArray = shareDataService.getDepotArray();
$scope.getEntityFromModal = function (entity, checked) {
shareDataService.setModalEntity(entity, checked);
};
$scope.getDepotFromModal = function (depot, checked) {
shareDataService.setModalDepot(depot, checked);
};
}]);
shareDataService (relevant methods):
angular.module("app").factory("shareDataService", function () {
var entityArrayService = [];
var depotArrayService = [];
var modalEntity = [];
var modalDepot = [];
getEntityArray: function () {
return entityArrayService;
},
getDepotArray: function () {
return depotArrayService;
},
setModalEntity: function (entity, checked) {
if (!checked) {
for (var i = 0; i < modalEntity.length; i++) {
if (modalEntity[i] === entity) {
modalEntity.splice(i, 1);
}
}
} else {
modalEntity.push(entity);
}
},
setModalDepot: function (depot, checked) {
if (!checked) {
for (var i = 0; i < modalDepot.length; i++) {
if (modalDepot[i] === depot) {
modalDepot.splice(i, 1);
}
}
} else {
modalDepot.push(depot);
}
},
});
There are other instances when the dataservice methods are called in my main controller, but these are only used for the array's length. So if the checkbox problem is solved everything is solved.
entityChecked is not declared anywhere in your javascript, so each time you filter, entityChecked is reset, so you need to make the model something that the repeater will see and have access to.
<!-- entity is now in the created child scope -->
<div ng-repeat="entity in entityArray | filter:simpleFilter">
<label> <input style="display: inline-block; margin-top: 5px"
<!-- set the checked value on the 'entity' itself, then it will be retained -->
type="checkbox" ng-model="entity.entityChecked"
ng-change="getEntityFromModal(entity, entityChecked)" /> <a>{{entity}}</a>
</label>
</div>
Finally have an answer - the array holding my values entityArray needs to be changed into an array holding JSON values, and for each value val there must be a value checked , which is represented by ng-model - in the above case it would be passed to getEntityFromModal(entity, entity.checked).
working plnk - https://plnkr.co/edit/2ptIAdOyaIw8mGqpU7Cp?p=preview
I am trying to work how to add a class with ngClick. I have uploaded up my code onto plunker Click here. Looking at the angular documentation i can't figure out the exact way it should be done. Below is a snippet of my code. Can someone guide me in the right direction
<div ng-show="isVisible" ng-class="{'selected': $index==selectedIndex}" class="block"></div>
Controller
var app = angular.module("MyApp", []);
app.controller("subNavController", function ($scope){
$scope.toggle = function (){
$scope.isVisible = ! $scope.isVisible;
};
$scope.isVisible = false;
});
I want to add or remove "active" class in my code dynamically on ng-click, here what I have done.
<ul ng-init="selectedTab = 'users'">
<li ng-class="{'active':selectedTab === 'users'}" ng-click="selectedTab = 'users'"><a href="#users" >Users</a></li>
<li ng-class="{'active':selectedTab === 'items'}" ng-click="selectedTab = 'items'"><a href="#items" >Items</a></li>
</ul>
You just need to bind a variable into the directive "ng-class" and change it from the controller. Here is an example of how to do this:
var app = angular.module("ap",[]);
app.controller("con",function($scope){
$scope.class = "red";
$scope.changeClass = function(){
if ($scope.class === "red")
$scope.class = "blue";
else
$scope.class = "red";
};
});
.red{
color:red;
}
.blue{
color:blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="ap" ng-controller="con">
<div ng-class="class">{{class}}</div>
<button ng-click="changeClass()">Change Class</button>
</body>
Here is the example working on jsFiddle
There is a simple and clean way of doing this with only directives.
<div ng-class="{'class-name': clicked}" ng-click="clicked = !clicked"></div>
you can also do that in a directive, if you want to remove the previous class and add a new class
.directive('toggleClass', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.bind('click', function() {
if(element.attr("class") == "glyphicon glyphicon-pencil") {
element.removeClass("glyphicon glyphicon-pencil");
element.addClass(attrs.toggleClass);
} else {
element.removeClass("glyphicon glyphicon-ok");
element.addClass("glyphicon glyphicon-pencil");
}
});
}
};
});
and in your template:
<i class="glyphicon glyphicon-pencil" toggle-class="glyphicon glyphicon-ok"></i>
You have it exactly right, all you have to do is set selectedIndex in your ng-click.
ng-click="selectedIndex = 1"
Here is how I implemented a set of buttons that change the ng-view, and highlights the button of the currently selected view.
<div id="sidebar" ng-init="partial = 'main'">
<div class="routeBtn" ng-class="{selected:partial=='main'}" ng-click="router('main')"><span>Main</span></div>
<div class="routeBtn" ng-class="{selected:partial=='view1'}" ng-click="router('view1')"><span>Resume</span></div>
<div class="routeBtn" ng-class="{selected:partial=='view2'}" ng-click="router('view2')"><span>Code</span></div>
<div class="routeBtn" ng-class="{selected:partial=='view3'}" ng-click="router('view3')"><span>Game</span></div>
</div>
and this in my controller.
$scope.router = function(endpoint) {
$location.path("/" + ($scope.partial = endpoint));
};
var app = angular.module("MyApp", []);
app.controller("subNavController", function ($scope){
$scope.toggle = function (){
$scope.isVisible = ! $scope.isVisible;
};
$scope.isVisible = false;
});
<div ng-show="isVisible" ng-class="{'active':isVisible}" class="block"></div>
I used Zack Argyle's suggestion above to get this, which I find very elegant:
CSS:
.active {
background-position: 0 -46px !important;
}
HTML:
<button ng-click="satisfaction = 'VeryHappy'" ng-class="{active:satisfaction == 'VeryHappy'}">
<img src="images/VeryHappy.png" style="height:24px;" />
</button>
<button ng-click="satisfaction = 'Happy'" ng-class="{active:satisfaction == 'Happy'}">
<img src="images/Happy.png" style="height:24px;" />
</button>
<button ng-click="satisfaction = 'Indifferent'" ng-class="{active:satisfaction == 'Indifferent'}">
<img src="images/Indifferent.png" style="height:24px;" />
</button>
<button ng-click="satisfaction = 'Unhappy'" ng-class="{active:satisfaction == 'Unhappy'}">
<img src="images/Unhappy.png" style="height:24px;" />
</button>
<button ng-click="satisfaction = 'VeryUnhappy'" ng-class="{active:satisfaction == 'VeryUnhappy'}">
<img src="images/VeryUnhappy.png" style="height:24px;" />
</button>
If you prefer separation of concerns such that logic for adding and removing classes happens on the controller, you can do this
controller
(function() {
angular.module('MyApp', []).controller('MyController', MyController);
function MyController() {
var vm = this;
vm.tab = 0;
vm.setTab = function(val) {
vm.tab = val;
};
vm.toggleClass = function(val) {
return val === vm.tab;
};
}
})();
HTML
<div ng-app="MyApp">
<ul class="" ng-controller="MyController as myCtrl">
<li ng-click="myCtrl.setTab(0)" ng-class="{'highlighted':myCtrl.toggleClass(0)}">One</li>
<li ng-click="myCtrl.setTab(1)" ng-class="{'highlighted':myCtrl.toggleClass(1)}">Two</li>
<li ng-click="myCtrl.setTab(2)" ng-class="{'highlighted':myCtrl.toggleClass(2)}">Three</li>
<li ng-click="myCtrl.setTab(3)" ng-class="{'highlighted':myCtrl.toggleClass(3)}">Four</li>
</ul>
CSS
.highlighted {
background-color: green;
color: white;
}
I can't believe how complex everyone is making this. This is actually very simple. Just paste this into your html (no directive./controller changes required - "bg-info" is a bootstrap class):
<div class="form-group col-md-12">
<div ng-class="{'bg-info': (!transport_type)}" ng-click="transport_type=false">CARS</div>
<div ng-class="{'bg-info': transport_type=='TRAINS'}" ng-click="transport_type='TRAINS'">TRAINS</div>
<div ng-class="{'bg-info': transport_type=='PLANES'}" ng-click="transport_type='PLANES'">PLANES</div>
</div>
for Reactive forms -
HTML file
<div class="col-sm-2">
<button type="button" [class]= "btn_class" id="b1" (click)="changeMe()">{{ btn_label }}</button>
</div>
TS file
changeMe() {
switch (this.btn_label) {
case 'Yes ': this.btn_label = 'Custom' ;
this.btn_class = 'btn btn-danger btn-lg btn-block';
break;
case 'Custom': this.btn_label = ' No ' ;
this.btn_class = 'btn btn-success btn-lg btn-block';
break;
case ' No ': this.btn_label = 'Yes ';
this.btn_class = 'btn btn-primary btn-lg btn-block';
break;
}
I have a form with additional data fields displayed in a modal:
<form class="form-horizontal" name="newForm" ng-controller="FormCtrl" ng-submit="submit()">
<label>Test</label>
<div ng-controller="ModalCtrl">
<a class="btn btn-link" ng-click="open()">Open</a>
<ng-include src="'partials/modal.html'"></ng-include>
</div>
</form>
includes:
<div modal="shouldBeOpen" close="close()" options="opts">
<div class="modal-header">
<h4>I'm a modal!</h4>
</div>
<div class="modal-body">
<ul>
<input type="text" tabindex="16" ng-model="someinput" name="someinput" size="32" class="validate[someinput]" />
</ul>
</div>
<div class="modal-footer">
<button class="btn btn-warning cancel" ng-click="close()">Cancel</button>
</div>
</div>
JS:
var ModalCtrl = function ($scope) {
$scope.open = function () {
$scope.shouldBeOpen = true;
};
$scope.close = function () {
$scope.shouldBeOpen = false;
};
$scope.opts = {
backdropFade: true,
dialogFade:true
};
};
How can I read/inject/transfer the user input from the modal form into the main form?
What you need is to communicate between two controllers and this can be achieved by creating a service.
Using this as reference.
You could create a service as follows:
angular.module('myApp', [])
.service('sharedInput', function () {
var modalInput = "";
return {
getModalInput:function () {
return modalInput;
},
setModalInput:function (value) {
modalInput = value;
}
};
});
Next, in your ModalCtrl() function, I assume that you will have a button to submit the input. Let us say that clicking this button invokes the "submitInput" function in the scope of ModalCtrl. You will write this function as:
$scope.submitInput = function() {
sharedInput.setMOdalInput($scope.someinput);
}
...and in your FormCtrl() you will write the following code to read the modal input:
var input = sharedInput.getModalInput()
You also need to pass the parameter "sharedInput" to the ModalCtrl and FormCtrl just like how you passed $scope. And now you have your two controllers communicating.
I found a comment to an issue on GitHub with a plunker explaining the problem and the solution with angular-ui, without the shared service. Works like a charm.
http://plnkr.co/edit/ktfq0Y?p=preview