I want to implement a feature in table where user can set value of cell by clicking on it.
there can be say 3-4 states,also a ng-model attached to it.
I looked for the toggle button in angularjs but they are mere on/off type.
In short; Clicking on the button will set the value as: Active, Inactive, Excluded
Looking for solution with multiple state.
Any help with this is really appreciated.
Check the below working example :
http://jsfiddle.net/vishalvasani/ZavXw/9/
and controller code
function MyCtrl($scope) {
$scope.btnarr=0;
$scope.btnTxt=["Active","Inactive","Excluded"]
$scope.change=function(){
switch($scope.btnarr)
{
case 0:
$scope.btnarr=1;
break;
case 1:
$scope.btnarr=2
break;
case 2:
$scope.btnarr=0;
break;
}
}
}
OR
Shorter Version of Controller
function MyCtrl($scope) {
$scope.btnarr=0;
$scope.btnTxt=["Active","Inactive","Excluded"]
$scope.change=function(){
$scope.btnarr = ($scope.btnarr + 1) % $scope.btnTxt.length;
}
}
and HTML
<div ng-controller="MyCtrl">
<button ng-modle="btnarr" ng-Click="change()">{{btnTxt[btnarr]}}</button>
</div>
There isn't much to it.
When I make menus in Angular, on each item, I'll have a "select" function, which then selects that particular object, out of the list...
Making an iterable button is even smoother:
var i = 0;
$scope.states[
{ text : "Active" },
{ text : "Inactive" },
{ text : "Excluded" }
];
$scope.currentState = $scope.states[i];
$scope.cycleState = function () {
i = (i + 1) % $scope.states.length;
$scope.currentState = $scope.states[i];
// notify services here, et cetera
}
<button ng-click="cycleState">{{currentState.text}}</button>
The actual array of states wouldn't even need to be a part of the $scope here, if this was the only place you were using those objects -- the only object you'd need to have on the $scope would then be currentState, which you set when you call the cycleState method.
Here is a fiddle with two possibilities: selecting the state from a list or cycling by clicking the button itself.
http://jsfiddle.net/evzKV/4/
The JS code looks like this:
angular.module('test').directive('toggleValues',function(){
return {
restrict: 'E',
replace: true,
template: '<div>Set Status:<div ng-repeat="value in values" class="status" ng-click="changeTo($index)">{{value}}</div><span ng-click="next()">Current Status (click to cycle): {{values[selectedValue]}}</span></div>',
controller: ['$scope', '$element', function ($scope, $element) {
$scope.values = ["Active", "Inactive", "Pending"];
$scope.changeTo = function (index) {
$scope.selectedValue = (index < $scope.values.length) ? index : 0;
};
$scope.next = function () {
$scope.selectedValue = ($scope.selectedValue + 1) % $scope.values.length;
// the modulo is stolen from Norguard (http://stackoverflow.com/a/18592722/2452446) - brilliant idea
};
$scope.selectedValue = 0;
}]
};
});
HTML:
<div ng-app="test">
<toggle-values></toggle-values>
</div>
Related
So I have a decimal value in controller like this:
// Controller
var MyController = function($scope) {
...
$scope.percentValue = 0.05; // can be stored
...
};
<!-- View -->
<span>{{percentValue}}</span>
<input ng-model="percentValue" />
With the above code, the value in the input element is 0.05 - however, I want to allow a user to enter an integer value like 5.
So if the $scope.percentValue is 0.05, I want to show it as 5 in the input element. And if a user enters 5, the $scope.percentValue should be 0.05.
However, the tricky thing here is I only want to update the view value - meaning that the span element should still show 0.05. Only the value in the input element should be 5.
I am trying to achieve this with ngModel, but I am still struggling.
This is what I have now:
var MyDirective = function() {
function link(scope, element, attrs, ngModel) {
ngModel.$render = function() {
element.val(ngModel.$viewValue || '');
};
ngModel.$formatters.push(function (value) {
return value * 100;
});
element.on('change blur', function() {
ngModel.$setViewValue(element.val());
});
}
return {
restrict: 'A',
require: '?ngModel',
scope: {},
link: link
};
};
Please advise!!
Including my comment as an answer because it seemed to help. :-)
To summarise: since you've already provided a $formatters function for your directive, which converts a model value ($modelValue) to displayed form ($viewValue), it's simply a matter of providing a $parsers function to do the reverse and convert any user input back to the model value.
Example Plunker
What you're trying to achieve is probably possible, but I would find it really confusing to read the code. The simplest solution that I think would solve your problem and maintain readability is to store an integer value (5) in $scope.percentValue, so that ng-model is always dealing with an integer when typing and displaying the value in the <input>. Then create a custom filter and use it to output the value as 0.05 in the <span>.
Edit: adding a concrete code example. Play with it here: https://plnkr.co/edit/C1cX2L9B2GM2yax1rw7Z?p=preview
JS:
var MyController = function ($scope) {
$scope.percentValue = 5;
};
function formatPercent (input) {
return input / 100;
}
var myApp = angular.module('MyApp', []);
myApp.filter('percent', function () { return formatPercent });
myApp.controller('MyController', ['$scope', MyController]);
HTML:
<body ng-controller="MyController">
<span>{{ percentValue | percent }}</span>
<input ng-model="percentValue">
</body>
I'd create a filter for percentage :
angular.module('myModule')
.filter('percentage', ['$filter', function($filter) {
return function(input, decimals) {
return $filter('number')(input*100, decimals)+'%';
};
}]);
The input will store integer (such as 5)
<input ng-model="percentValue" />
But I'll add a filter to the span part :
<span>{{percentValue | percentage:2}}</span>
Credit to https://stackoverflow.com/a/21727765/3687474 for the filter directive.
Other than creating a filter you can also calculate on the template
<span>{{percentValue * 100}}</span>
First what I want:
I have a series of thumbnails. When I click on one, I want that specific thumbnail to be shown in a bigger div, with its description. (For later: should be animated).
Now I have the directive and the controller, but I don't know how to set the appropriate variable!
So some code:
First HTML: here is the root .jade file for this section. I have a directive called product.
section
.detail-view(ng-show="vm.showDetail")
.prod-desc(ng-bind="vm.detailPic")
.prod-img(ng-bind="vm.detailDesc")
product.col-xs-12.product_tile(ng-repeat="item in vm.products", item="::item")
As you can see, the product directive is part of an ng-repeat; for this reason, the div I want to show the resized image is outside the iteration (.detail-view).
The product directive:
'use strict';
ProductDirective.$inject = ['ShopCart', '$animate', 'User'];
function ProductDirective(ShopCart, $animate, User) {
return {
restrict: 'E',
scope: {
item: '='
},
template: require('./product.html'),
link: link
};
function link(scope, element) {
scope.toggleDetails = toggleDetails;
}
function toggleDetails() {
if (!scope.isSelected && isBlurred()) return;
scope.vm.detailPic = scope.item.photo;
scope.vm.detailDesc = scope.item.prod_description;
scope.vm.isSelected = !scope.isSelected;
scope.showDetail = !scope.showDetail;
var action = scope.isSelected ?
}
}
Now the div I want to update with the image in big is outside the iteration - and hence outside the scope of the directive. How can I set the value of showDetail, showDesc and showPic?
As I am using controllerAs with value vm, I thought I could just do scope.vm.detailPic = scope.item.photo;, as in other solutions I have seen that when setting a property on a root object, it would be propagated...but I get
Cannot set property 'detailPic' of undefined
For now, what works (but looks a bit odd to me) is this, in toggleDetails()
scope.$parent.$parent.vm.detailPic = scope.item.photo;
scope.$parent.$parent.vm.detailDesc = scope.item.prod_description;
scope.$parent.$parent.vm.showDetail = !scope.$parent.$parent.vm.showDetail
Hi I am new to AngularJs and trying hard to find a solution for this. I am just learning directives and have been able to get my directive to work showing a textbox for the user to type into. The logic to change to upper and lower case characters from a string is what I'm puzzled with.
I thought maybe something like:
if (inputValue % 2 == 0) {
//have the user input.ToUpperCase()
}
E.g if user types in computer the textbox would dynamically update as the user types to CoMpUtEr.
Any help is greatly appreciated.
The best you can do in this case is to write custom directive. You need to make sure that the model is properly transformed in all cases: through input in the field, as well as when the model changes in code like $scope.model = 'some' - then it's supposed to get translated to SoMe in the view.
Here is a basic directive I wrote to alternate characters case.
var app = angular.module('app', []);
app.directive('capitalizeAlternate', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModelController) {
function formatter(value) {
if (value) {
for (var i = 0; i < value.length; i++) {
if (i % 2 == 0) {
value = value.substr(0, i) + value[i].toUpperCase() + value.substr(i + 1);
}
}
return value;
}
}
ngModelController.$parsers.push(function(value) {
if (value) {
ngModelController.$viewValue = formatter(value);
ngModelController.$render();
return ngModelController.$viewValue;
}
});
ngModelController.$formatters.push(formatter);
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<input capitalize-alternate type="text" ng-model="input" /> {{input}}
</div>
On your input add the handler ng-change="unformatMyText". Then add this funtion to your view, like unformatMyText: function(txt){}. Within the function body add your code. This should do the trick every time.
http://plnkr.co/edit/39FGMocKB5GtQWnI1TFw?p=preview
I have sidebar which contains a list of tags, when you click on a tag I use the TagDetailsFactory to send a tag into the scope of the view controller.
Everything works great except for when you hover over a tag in the TagDetailsFactory scope.
The tagDetails template does not show up, however if you hover over the same tag in the sidebar scope, the tagDetails shows up in both. This is wrong.
Hovering over a tag in the sidebar should only show the tag details for that tag and the same for the tags inside the view scope.
Hovering over a tag in the view scope, doesn't display it's details
Hovering over a tag inside of the sidebar scope, should only show details for it's tag, and not the tag in the view scope, like it does here:
Steps:
- The first tags Array is in the cnt controller
- When you click on a tag, it gets stored in the TagDetailsFactory service
- I then broadcast an event to the view controller to then call the getTagDetails function in TagDetailsFactory to retrieve the saved tags and store them into the viewTags array in the view controller.
// Code goes here
angular.module('app', [])
.directive('tagDetails', function() {
return {
restrict: "E",
link: function($scope, el, attrs) {
// console.debug($scope, attrs);
},
scope:{
tag:'='
},
template: '<div ng-show="tag.showDetails">{{tag.details}}</div>'
};
})
.factory('TagDetailsFactory', function() {
var savedTags = [];
var saveTagDetails = function(tag) {
savedTags.push(tag);
}
var getTagDetails = function() {
return savedTags;
}
return {
saveTagDetails : saveTagDetails,
getTagDetails : getTagDetails
};
})
.controller('sidebar', function($scope,
$rootScope,
TagDetailsFactory) {
$scope.tags = [];
for(var i = 0; i < 10; i++) {
$scope.tags.push(
{ name: 'Foo Bar ' + i, details: 'Details' + i }
);
}
$scope.showTagDetails = function(t) {
t.showDetails = true;
}
$scope.leaveTag = function(t) {
t.showDetails = false;
}
$scope.sendTag = function(t) {
TagDetailsFactory.saveTagDetails(t);
$rootScope.$broadcast('updateView');
}
})
.controller('view', function($scope,
$rootScope,
TagDetailsFactory) {
$scope.viewTags = [];
$scope.$on('updateView', function() {
$scope.viewTags = TagDetailsFactory.getTagDetails();
});
$scope.showTagDetails = function(v) {
v.showDetails = true;
}
$scope.leaveTag = function(v) {
v.showDetails = false;
}
});
Do I have to create a 2nd directive here? To be the template for the tag details in the view scope? Or can my current tagDetails directive be repurposed somehow in an Angular way?
I forked your Plunker with a working copy, and I'll explain the changes I made.
You have two issues with the code here. The first is a simple typo, which is causing your header to not reference the correct function for mouseover. Your functions are calling showTagDetailsView(v)and leaveTagView(v), but they are named showTagDetails and leaveTag on the controller.
The second issue is with the way that the items are added to the savedTags[]. In JavaScript, objects are passed by reference. when you call savedTags.push(tag);, you are pushing a reference to the same object into the new array. Any changes made to the object in one array will be reflected in the other array.
Instead, what you want is a separate copy of the object in the savedTags[]. This can be accomplished by using angular.copy. Note that I also reset tag.showDetails = false; before making the copy, else the new copy will have it set to true, and the details will be showing the instant the copy appears, even though you are hovering over the other element when you click it.
var saveTagDetails = function(tag) {
tag.showDetails = false;
savedTags.push(angular.copy(tag));
}
Just a side note, you might also have an issue with CSS here, as hovering seems to change the position of the lists, and in some cases the hover actually causes the tag to move itself out of the hover, causing a bounce effect.
I have a problem with $scope.$watch call, when it obviously should be called.
I have a paginator (bootstrap UI) inside my html document:
<pagination total-items="paginatorTotalItems" items-per-page="paginatorItemsPerPage"
page="paginatorCurrentPage" max-size="paginatorSize" class="pagination-sm"
boundary-links="true">
</pagination>
A certain part, where my items are shown (for them I need a paginator):
<div ng-show="reviews" ng-repeat="review in reviewsPerPage">
...
</div>
And a Controller:
...
$scope.reviewsArray = [];
$scope.paginatorItemsPerPage = 1;
$scope.paginatorSize = 3;
$scope.reviewsPerPage = [];
$scope.paginatorTotalItems = $scope.reviews.result.total;
//restangular object to Array
for (var i = 0; i < $scope.paginatorTotalItems; i++) {
$scope.reviewsArray.push($scope.reviews.result.reviews[i]);
};
$scope.paginatorCurrentPage = 1;
$scope.$watch('paginatorCurrentPage', function () {
var begin = (($scope.paginatorCurrentPage - 1) * $scope.paginatorItemsPerPage);
var end = begin + $scope.paginatorItemsPerPage;
console.log($scope.paginatorCurrentPage);
console.log(begin + ' ' + end);
$scope.reviewsPerPage = $scope.reviewsArray.slice(begin,end);
console.log($scope.reviewsPerPage);
});
So, making long story short, I have a variable paginatorCurrentPage, that I change by clicking numbers in my <pagination>, but $watch does not react. This $watch is called only once: when I'm assigning it a value of 1 (after making an array from my restangular object), after that $watch is never called anymore.
Also I'm cheking how paginatorCurrentPage changes in my html file:
<p>Current : {{paginatorCurrentPage}}</p>
And it actually works, this variable is changing, when i switch my pagination buttons, but $watch is not called.
Sorry for my English, and Thank you!
Edited :
I have updated my bootstrap UI, so now in paginator I use ng-model istead of page. And I realized that variable paginatorCurrentPage changes only in my view, but in controller I still have my default $scope.paginatorCurrentPage = 1. Problem still exists.
Thanks for all comments. The problem was about scope. I rewrote ng-model in paginator: ng-model="paginatorPage.current"
and changed
$scope.paginatorCurrentPage = 1;
to
$scope.paginatorPage = {current : 1};
And thanks to #Leo Farmer for advice about dots in directives.