Angularjs directive - select next element by a class name - angularjs

I have the following HTML:
<div>
<input type="text" ng-model="search.q" special-input>
<ul class="hidden">...</ul>
<ul class="hidden bonus">...</ul>
</div>
And the following directive:
myApp.directive('specialInput', ['$timeout', function($timeout)
{
return {
link: function(scope, element) {
element.bind('focus', function() {
$timeout(function() {
// Select ul with class bonus
element.parent().find('.bonus').removeClass('hidden');
});
});
}
}
}]);
I want to select the ul.bonus using jqLite but cannot find a way. I tried with .next(".bonus") but the selector is ignored completely and the first ul is selected. Does anyone have an idea why I can't do this?
P.S. I'm just relying on AngularJS internal jqLite without jQuery.
Thanks!

you will not need a directive to achieve this:
<div>
<input type="text"
ng-model="search.q"
ng-focus="bonus=true"
ng-blur="bonus=false">
<ul class="hidden">...</ul>
<ul ng-show="bonus">...</ul>
</div>
if your needs are more complex put the decision about the bonus state in your controller.

Related

angular js - using ng-click to focus on a text box

I'm trying to build an Angular JS form. I'd like user to be able to set the focus on a text field when they click a button. Not sure why this doesn't work? Thanks
html:
<div ng-app="" ng-controller="personController">
<p>Name: <input type="text" ng-model="name" id="question1" focus="{{focusThis}}"></p>
<p ng-bind="name"></p>
<input type="button" value="focus" ng-click="focus()">
</div>
Angular JS function:
function personController($scope)
{$scope.focus=function(){
$scope.focusThis="true";
};
};
How about some general solution ($ is jQuery):
mod.directive('nsFocusId', ['$timeout', function($timeout) {
return {
restrict: 'A',
link: function(scope, element, attrs, ctrl) {
element.click(function() {
$timeout(function () { $('#' + attrs.nsFocusId).focus(); }, 0);
});
}
};
}]);
Usage:
<label data-ns-focus-id="id-of-target-element">a</label>
This directive could be used on any element and it will focus element by id that you provide.
I've used $timeout just to make it more flexible for future upgrades (but feel free to wrap it with just scope.$apply function);
https://docs.angularjs.org/api/ng/directive/ngFocus
ng-focus executes an expression when the element is focused, so it doesn't actually set the element as focused but rather respond to it being focused.
How to set focus on input field?
Check this resource or google up 'how to set an element focused' and it should direct you in the right way.
I have modified your code, check it.
<div ng-app="TestApp" ng-controller="personController">
<p>Name: <input type="text" ng-model="name" id="question1" ></p>
<p ng-bind="name"></p>
<input type="button" value="focus" ng-click="focus()">
</div>
var app = angular.module('TestApp', []);
app.controller('personController', function ($scope, $http, $log) {
$scope.focus = function () {
$("#question1").focus();
console.log($("#question1"));
}
});

angularjs directive calls image effect script

I have a directive to make news item to have an effect like usatoday.com when user hover on the news. I'm new to angularjs :D
Directive:
app.directive('hotEvent', ['$rootScope', function ($rootScope) {
return {
restrict: 'EA',
templateUrl: '/App/Main/views/home/events.html',
link: function (scope, iElement, attrs) {
//attrs references any attributes on the directive element in html
var dathumb = $(iElement).find('.da-thumbs > li a');
//$(function () {
dathumb.each(function () {
$(this).hoverdir();
});
//});
}
};
}]);
View: /App/Main/views/home/events.html
<ul class="row da-thumbs">
<li ng-repeat="news in featuredEvents">
<a href="/">
<img src="abc.jpg" /> >> no effect ???
<div>
<h4>aaa</h4>
<p>
bbb
</p>
</div>
</a>
</li>
<li>
<a href="/">
<img src="abc.jpg" /> >> show effect
<div>
<h4>bbb</h4>
<p>
ccc
</p>
</div>
</a>
</li>
</ul>
On Home.html: (which already binded with controller)
<div hot-event></div>
It works when i don't bind data from the controller <li ng-repeat="news in featuredEvents">, now the effect just doesn't show up. Console.log show 0 error.
UPDATED: i ended up using document ready
app.directive('hotEvent', function () {
return {
restrict: 'EA',
templateUrl: '/App/Main/views/home/events.html',
link: function ($scope, iElement, attrs) {
angular.element(document).ready(function () {
var dathumb = $(iElement).find('.da-thumbs > li a');
dathumb.each(function () {
$(this).hoverdir();
});
});
}
}
});
If you debug your code you'd see that your directive didn't find any elements.
It happens because when the template loads, the directive link function gets called, but the ng repeat didn't have time to populate it self (it starts off empty), there for it's no where to be found.
An easy workaround is to use the jquery find method in a setTimeout 0 function, or use $scope.evalAsync on the function that does the jquery find (it requires angular 1.3).
But the best solution would be to fix the code to actually not require jquery.
P.s. when you see your self using selectors in a directive you are usually doing things wrong in angular (note: usually), please refer to this "Thinking in AngularJS" if I have a jQuery background? awesome question and answer.
note that he actually says that when you first learn angular, don't use jquery at all :)

AngularJS directive for accessing ng-repeat scope of child

Okay, so let me start off saying that I have a directive that creates a JQueryUI tab setup with a backing field that populate the tab name and the tab contents basically.
<div id="summaryTabPanel">
<ul>
<li ng-repeat="category in SummaryCategories">
{{category.Name}}
</li>
</ul>
<div ng-repeat="category in SummaryCategories" id="tabs-{{$index + 1}}">
{{category.Content}}
</div>
</div>
So as you can see, I have a <ul> with an ng-repeat in the <li>.
As I will be applying JQueryUI Tab function to the parent container id="summaryTabPanel", I need to wait until all the dom is rendered.
So I do some research, and find out I need to create a directive that kind of looks like the following:
Application.Directives.directive("repeatDone", function () {
return {
restrict: "A",
link: function (scope, element, attrs) {
scope.$watch("repeatDone", function () {
if (scope.$last) {
$("#summaryTabPanel").tabs({
event: "mouseover"
});
}
;
});
}
};
});
This watches to see if the last repeated item is found, and then applies the DOM changes via the statement $("#summaryTabPanel").tabs()
This is all fine and good, but it only works if I apply the directive to the last ng-repeated child item of the summaryTabPanel container.
<div id="summaryTabPanel">
<ul>
<li ng-repeat="category in SummaryCategories">
{{category.Name}}
</li>
</ul>
<div ng-repeat="category in SummaryCategories" id="tabs-{{$index + 1}}" repeat-done>
{{category.Content}}
</div>
</div>
If I move it to the previous ng-repeat item things work but not quite right. If I move it to the parent container, things don't work at all.
It seems wrong to me to apply this directive to the last child, and instead have a way to apply it to the root and somehow accomplish the same thing.
<div id="summaryTabPanel" repeat-done>
Is this possible somehow with Angular?
According to this link you should write your directive like this
Application.Directives.directive("repeatDone", function () {
return {
restrict: "A",
link: function (scope, element, attrs) {
$timeout( function () {
$("#summaryTabPanel").tabs({
event: "mouseover"
});
});
}
});
Please have a look at the jsfiddle I created to illustrate your case.

how to set visibility to a class with angular.js

using angular.js is there a way to set the visibility of all the elements in the page with a specific class using a single model variable?
the angular equivalent of
$(".myClass").hide();
thanks,
Luca
In html code you can use directive
<div ng-show="shouldShow"></div>
and in controller
$scope.shouldShow = (true or flase)
Try ngClass
Take a look at this.
Here depends upon the Boolean value of $scope.data the content will be shown or hidden.
i.e if $scope.data = true content will be hidden, and if false content will be shown
Working Demo
Html
<div ng-app='myApp' ng-controller="ArrayController">
<div ng-class="{show: data, hide:data}">Content</div>
</div>
Script
var app = angular.module('myApp', []);
app.controller('ArrayController', function ($scope) {
$scope.data = true;// for Hiding
});
CSS
.hide{
display:none;
}
I would say to add a directive like this.
directive('className', function () {
return {
restrict: 'C',
link: function (scope, elem, attrs) {
elem.hide();
}
}
});
In MarkUp
<div class="className">Content</div>
Here is an Plunker, made for you.
I think this can be useful to you.Have a look at this jsfiddle
Html
<div ng-app=''>
<div ng-class="selectCss">Content</div>
<input type="button" ng-model="selectCss" value="show" ng-click="selectCss='show'">
<input type="button" ng-model="selectCss" value="hide" ng-click="selectCss='hide'"/>
</div>
Css
.hide{
display:none;
}

Dynamic validation and name in a form with AngularJS

I have this form : http://jsfiddle.net/dfJeN/
As you can see the name value for the input is statically set :
name="username"
, the form validation works fine (add something and remove all text from the input, a text must appears).
Then I try to dynamically set the name value : http://jsfiddle.net/jNWB8/
name="{input.name}"
Then I apply this to my validation
login.{{input.name}}.$error.required
(this pattern will be used in an ng-repeat) but my form validation is broken. It is correctly interpreted in my browser (if I inspect the element I saw login.username.$error.required).
Any Idea ?
EDIT: After logging the scope in the console it appears that the
{{input.name}}
expression is not interpolate. My form as an {{input.name}} attribute but no username.
UPDATE: Since 1.3.0-rc.3 name="{{input.name}}" works as expected. Please see #1404
You can't do what you're trying to do that way.
Assuming what you're trying to do is you need to dynamically add elements to a form, with something like an ng-repeat, you need to use nested ng-form to allow validation of those individual items:
<form name="outerForm">
<div ng-repeat="item in items">
<ng-form name="innerForm">
<input type="text" name="foo" ng-model="item.foo" />
<span ng-show="innerForm.foo.$error.required">required</span>
</ng-form>
</div>
<input type="submit" ng-disabled="outerForm.$invalid" />
</form>
Sadly, it's just not a well-documented feature of Angular.
Using nested ngForm allows you to access the specific InputController from within the HTML template. However, if you wish to access it from another controller it does not help.
e.g.
<script>
function OuterController($scope) {
$scope.inputName = 'dynamicName';
$scope.doStuff = function() {
console.log($scope.formName.dynamicName); // undefined
console.log($scope.formName.staticName); // InputController
}
}
</script>
<div controller='OuterController'>
<form name='myForm'>
<input name='{{ inputName }}' />
<input name='staticName' />
</form>
<a ng-click='doStuff()'>Click</a>
</div>
I use this directive to help solve the problem:
angular.module('test').directive('dynamicName', function($compile, $parse) {
return {
restrict: 'A',
terminal: true,
priority: 100000,
link: function(scope, elem) {
var name = $parse(elem.attr('dynamic-name'))(scope);
// $interpolate() will support things like 'skill'+skill.id where parse will not
elem.removeAttr('dynamic-name');
elem.attr('name', name);
$compile(elem)(scope);
}
};
});
Now you use dynamic names wherever is needed just the 'dynamic-name' attribute instead of the 'name' attribute.
e.g.
<script>
function OuterController($scope) {
$scope.inputName = 'dynamicName';
$scope.doStuff = function() {
console.log($scope.formName.dynamicName); // InputController
console.log($scope.formName.staticName); // InputController
}
}
</script>
<div controller='OuterController'>
<form name='myForm'>
<input dynamic-name='inputName' />
<input name='staticName' />
</form>
<a ng-click='doStuff()'>Click</a>
</div>
The problem should be fixed in AngularJS 1.3, according to this discussion on Github.
Meanwhile, here's a temporary solution created by #caitp and #Thinkscape:
// Workaround for bug #1404
// https://github.com/angular/angular.js/issues/1404
// Source: http://plnkr.co/edit/hSMzWC?p=preview
app.config(['$provide', function($provide) {
$provide.decorator('ngModelDirective', function($delegate) {
var ngModel = $delegate[0], controller = ngModel.controller;
ngModel.controller = ['$scope', '$element', '$attrs', '$injector', function(scope, element, attrs, $injector) {
var $interpolate = $injector.get('$interpolate');
attrs.$set('name', $interpolate(attrs.name || '')(scope));
$injector.invoke(controller, this, {
'$scope': scope,
'$element': element,
'$attrs': attrs
});
}];
return $delegate;
});
$provide.decorator('formDirective', function($delegate) {
var form = $delegate[0], controller = form.controller;
form.controller = ['$scope', '$element', '$attrs', '$injector', function(scope, element, attrs, $injector) {
var $interpolate = $injector.get('$interpolate');
attrs.$set('name', $interpolate(attrs.name || attrs.ngForm || '')(scope));
$injector.invoke(controller, this, {
'$scope': scope,
'$element': element,
'$attrs': attrs
});
}];
return $delegate;
});
}]);
Demo on JSFiddle.
Nice one by #EnISeeK.... but i got it to be more elegant and less obtrusive to other directives:
.directive("dynamicName",[function(){
return {
restrict:"A",
require: ['ngModel', '^form'],
link:function(scope,element,attrs,ctrls){
ctrls[0].$name = scope.$eval(attrs.dynamicName) || attrs.dynamicName;
ctrls[1].$addControl(ctrls[0]);
}
};
}])
Just a little improvement over EnlSeek solution
angular.module('test').directive('dynamicName', ["$parse", function($parse) {
return {
restrict: 'A',
priority: 10000,
controller : ["$scope", "$element", "$attrs",
function($scope, $element, $attrs){
var name = $parse($attrs.dynamicName)($scope);
delete($attrs['dynamicName']);
$element.removeAttr('data-dynamic-name');
$element.removeAttr('dynamic-name');
$attrs.$set("name", name);
}]
};
}]);
Here is a plunker trial. Here is detailed explantion
I expand the #caitp and #Thinkscape solution a bit, to allow dynamically created nested ng-forms, like this:
<div ng-controller="ctrl">
<ng-form name="form">
<input type="text" ng-model="static" name="static"/>
<div ng-repeat="df in dynamicForms">
<ng-form name="form{{df.id}}">
<input type="text" ng-model="df.sub" name="sub"/>
<div>Dirty: <span ng-bind="form{{df.id}}.$dirty"></span></div>
</ng-form>
</div>
<div><button ng-click="consoleLog()">Console Log</button></div>
<div>Dirty: <span ng-bind="form.$dirty"></span></div>
</ng-form>
</div>
Here is my demo on JSFiddle.
I used Ben Lesh's solution and it works well for me. But one problem I faced was that when I added an inner form using ng-form, all of the form states e.g. form.$valid, form.$error etc became undefined if I was using the ng-submit directive.
So if I had this for example:
<form novalidate ng-submit="saveRecord()" name="outerForm">
<!--parts of the outer form-->
<ng-form name="inner-form">
<input name="someInput">
</ng-form>
<button type="submit">Submit</button>
</form>
And in the my controller:
$scope.saveRecord = function() {
outerForm.$valid // this is undefined
}
So I had to go back to using a regular click event for submitting the form in which case it's necessary to pass the form object:
<form novalidate name="outerForm"> <!--remove the ng-submit directive-->
<!--parts of the outer form-->
<ng-form name="inner-form">
<input name="someInput">
</ng-form>
<button type="submit" ng-click="saveRecord(outerForm)">Submit</button>
</form>
And the revised controller method:
$scope.saveRecord = function(outerForm) {
outerForm.$valid // this works
}
I'm not quite sure why this is but hopefully it helps someone.
This issue has been fixed in Angular 1.3+
This is the correct syntax for what you are trying to do:
login[input.name].$invalid
if we set dynamic name for a input like the below
<input name="{{dynamicInputName}}" />
then we have use set validation for dynamic name like the below code.
<div ng-messages="login.dynamicInputName.$error">
<div ng-message="required">
</div>
</div>

Resources