How to get an element's attribute with AngularJS - javascript

I have the following code:
<div class="col-md-10" data-ng-controller="type-controller">
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-success" ng-model="typeId" data-btn-radio="'1'">
Option 1
</label>
<label class="btn btn-success" ng-model="typeId" data-btn-radio="'2'">
Option 2
</label>
</div>
<input data-ng-model="typeId" name="typeId" type="hidden" data-start="2" />
</div>
My type-controller is empty so I'm omitting it - but I want to get the value of the attribute data-start from the last input inside the type-controller.
I'm not using jQuery.

IF the attribute data-start is significant because it is being used by some other 3rd party library, then you might consider simply using ng-init when you create this on the server:
<input data-ng-model="typeId" name="typeId" type="hidden" data-start="2"
ng-init='start = 2' />
This will essentially run any code you need, and doesn't involve you having to parse out data attributes from the DOM.
You could write a pretty trivial directive to pull in the value and publish using an expression. This will essentially accomplish the same thing, but is more difficult in my opinion:
angular.module('data-pluck', [])
.controller('fooController', function() {
this.name = 'Foo Controller';
})
.directive('pluckData', ['$parse',
function($parse) {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
var expression = function() {};
expression.assign = function() {};
scope.$watch(attrs.placeData, function() {
expression = $parse(attrs.placeData);
});
scope.$watch(attrs.pluckData, function() {
expression.assign(scope, attrs[attrs.pluckData]);
});
}
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='data-pluck' ng-controller='fooController as ctrl'>
<h1>{{ctrl.name}}</h1>
<div data-my-val="I'm value one" pluck-data='myVal' place-data='ctrl.valueOne'>
<p>I'm a regular old <code><p></code> tag</p>
<input type='hidden' data-my-val="I'm the second value" pluck-data='myVal' place-data='ctrl.valueTwo' />
</div>
<h3>These Values Populated Dynamically</h3>
<ul>
<li>ctrl.valueOne = {{ctrl.valueOne}}</li>
<li>ctrl.valueTwo = {{ctrl.valueTwo}}</li>
</ul>
</div>

Angular comes with jqLite built in, which still has the attr() function. But it's not the Angular "way" to be manually fiddling around in the DOM from a controller. Your scope should be the interface between them.
I'm curious as to why you have a value in an attribute in your UI that isn't defined first in your model / scope? How does this value get changed? Is there a reason why you can't set it in the controller:
$scope.start = 2;
and then:
<input data-ng-model="typeId" name="typeId" type="hidden" data-start="{{start}}" />
Can you explain a little about what data-start is meant to do?

Related

AngularJS data binding not working - within the controller Scope variables are not showing the entered value

I have a strange situation in which $scope variables binding do not appear to be work as expected.
Here is the HTML:
<div class="input-group" style="width:100px">
<input type="number"
class="form-control"
id="Sampling_Request_for_Current_Sampling_INPUT"
ng-model="aabbcc"
style="width:125px;text-align:center">
<span class="input-group-btn">
<button class="btn btn-default" ng-disabled="Cannot_Allocate_Yet" ng-click="Get_Sampling_Request_Details()" type="button">{{All_Labels.Common.Display}}</button>
</span>
</div>
and here is the scope function invoked upon clicking on the button:
$scope.Get_Sampling_Request_Details = function () {
console.log("$scope.aabbcc: " + $scope.aabbcc) ;
}
The variable $scope.aabbcc is initialized to 0 upon controller's loading.
Regardless what I type into the input element, I always get 0 in the console.
This scenario generally happens, If you have wrapped your HTML inside ng-if, ng-switch ng-repeat.. or some other directive that creates new child scope.
See this fiddle.
So it's a best practice to wrap your scope in some model to leverage protypical inheritance and correctly bind data to $scope.
Like : $scope.data.aabbcc = 0 and use it like ng-model ='data.aabbcc'.
See this for few minutes and Read this for complete understanding.
check this working example
<div ng-controller="MyCtrl">
Hello, {{name}}!
<input type="number" ng-model="name"/>
<button class="btn btn-default" ng-disabled="Cannot_Allocate_Yet" ng-
click="Get_Sampling_Request_Details()" type="button">test</button>
</div>
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.name = 0;
$scope.Get_Sampling_Request_Details = function () {
console.log("$scope.aabbcc: " + $scope.name) ;
}
}
AngularJS controllers control the data. The scope is the binding part between the HTML (view) and the JavaScript (controller). You must define the ng-model inside a ng-controller within which its scope lies. Try this out.
<div ng-controller="myCtrl">
<div class="input-group" style="width:100px">
<input type="number"
class="form-control"
id="Sampling_Request_for_Current_Sampling_INPUT"
ng-model="aabbcc"
style="width:125px;text-align:center">
<span class="input-group-btn">
<button class="btn btn-default" ng-disabled="Cannot_Allocate_Yet" ng-click="Get_Sampling_Request_Details()" type="button">{{All_Labels.Common.Display}}</button>
</span>
</div>
</div>
<script>
var app = angular.module('Myapp', []);
app.controller('myCtrl', function($scope) {
$scope.Get_Sampling_Request_Details = function () {
console.log("$scope.aabbcc: " + $scope.aabbcc) ;
}
});
</script>
Declare an empty object in your controller section .
eg: $scope.obj = {};
And use like ng-model="obj.key_name" in your html. It will work.

Isolate scope variable is getting undefined inside link function

I have a directive that receives a data object and a function to it's isolate scope. In link function I'm declaring a scope method to trigger in a certain event (button click).
Problem is the value passed to the above mentioned method is available inside it, but the scope variable is still undefined.
Directive:
commentsModule.directive('commentsDirective', [ function() {
return {
restrict: 'E',
templateUrl: '/alarm-viewer-comments-template.html',
scope: {
alarmComments: "=value",
sendNewComment: "&sendNewComment"
},
link: function(scope, elems, attrs, ngModelCtrl) {
scope.sendComment = function(data) {
console.log(scope.newComment);//this newComment variable is undefined
scope.sendNewComment(data);//data is correct
scope.newComment = '';
};
}
}
}
]);
Here inside link function, data passed into scope.sendComment is available but yet scope.newComment gets undefined.
Template:
<h4>Comments</h4>
<div ng-repeat="comment in alarmComments.comments">
<p>{{comment.timestamp}} | <strong>{{comment.user}}</strong>: {{comment.commentType}} {{comment.comment}}</p>
</div>
<div ng-if="alarmComments.editPermission && alarmComments.isActiveAlarm">
<form name="commentsForm" role="form" track-form>
<input type="text" ng-model="newComment" pattern="/.{1,}" maxlength="4" required ng-enter="sendComment(newComment)"/>
<button type="button" class="btn btn-primary" ng-disabled="commentsForm.$invalid" ng-click="sendComment(newComment)">Send</button>
</form>
</div>
UI:
<comments-directive value="alarmComments" send-new-comment="addNewComment(comment)"></comments-directive>
Can someone help me out...?
edit: what I want is to clear the input text field after entering a comment.
Within directive scope you should map the newComment property also along with alarmComments. like below -
scope: {
alarmComments: "=value",
newComment: "=newComment",
sendNewComment: "&sendNewComment"
},
A good way to debug problems like this is render scope ids (scope.$id) and verify they are of the same id.
Could you verify during link, what is the scope.$id and render it in your template?
<h4>Comments</h4>
<div ng-repeat="comment in alarmComments.comments">
<p>{{comment.timestamp}} | <strong>{{comment.user}}</strong>: {{comment.commentType}} {{comment.comment}}</p>
</div>
<div ng-if="alarmComments.editPermission && alarmComments.isActiveAlarm">
<form name="commentsForm" role="form" track-form>
<input type="text" ng-model="newComment" pattern="/.{1,}" maxlength="4" required ng-enter="sendComment(newComment)"/>
<button type="button" class="btn btn-primary" ng-disabled="commentsForm.$invalid" ng-click="sendComment(newComment)">Send</button>
{{$id}}
</form>
</div>
Sometimes templates create its own scope and you might have to use $parent.newComment in your templates.

Access "calling scope" and strange behaviour

I have a simple snippet of code :
function SomeCtrl($scope) {
$scope.modify = function(value) {
$scope.something = "Hello";
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app>
<div ng-controller="SomeCtrl">
<div ng-repeat="toto in [1,2,4,5]">
<input ng-model="something" />
<input ng-model="something" />
<button ng-click="modify()">Modify</button>
</div>
</div>
</div>
Does anyone can explain how I could change it so the modify() function only change the textfields inside the scope of the button I click ?
I also don't get why only the text fields which have not been edited are modified by the function.
Thank you very much
This is because ng-repeat creates it's own scope. Using prototypal inheritance. By declaring ng-model you're creating a new field on that new scope.
But this will work for what you're trying to do.
<div ng-repeat="toto in [1,2,4,5]" ng-init="something = {}">
<input ng-model="something.hi" />
<input ng-model="something.hi" />
<button ng-click="modify(something)">Modify</button>
</div>
</body>
.controller('ctrl', function ($scope) {
$scope.modify = function (something) {
something.hi = "hello";
}
})
In this case you are just pushing out on screen the same info for times, meanwhile binding everything to the same variable. You can just simply create array and bind every input line to appropriate array element. And by pressing "modify" button, pass parameter, witch array element must be changed.
function SomeCtrl($scope) {
$scope.something = [];
$scope.modify = function(toto) {
$scope.something[toto] = toto;
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app>
<div ng-controller="SomeCtrl">
<div ng-repeat="toto in [1,2,4,5]">
<input ng-model="something[toto]" />
<input ng-model="something[toto]" />
<button ng-click="modify(toto)">Modify</button>
</div>
</div>
</div>

Easy way to access $route from html side in AngularJS

Is there a way to access $route from the html side of AngularJS?
I'd really like to do something like this in HTML and eliminate yet another $scope function. (yes, i know its not really something that works):
<button ng-disabled="!route.current.scope.currentform.$valid">
I am working on a large angular application that is somewhat 'down the road' in the development cycle. At this time we have decided to implement form validation (don't ask why it wasn't 1 or 2 on the list).
I am currently using two buttons in the footer "previous" and "next", both which need to ng-disabled set to !$scope.formname.$valid. This needs to work across multiple controllers / pages, since the buttons are in my footer on index.html.
The code i'm using right now looks like this:
// Want to eliminate this isFormValid() method
$scope.isFormValid = function() {
if ($route.current) {
if ($route.current.scope) {
return !$route.current.scope.currentform.$valid;
}
}
};
$scope.prevPage = function() {
if (angular.isFunction($route.current.scope.PrevAction)) {
// do stuff particular to the current controller
$route.current.scope.PrevAction()
}
};
$scope.nextPage = function() {
if (angular.isFunction($route.current.scope.NextAction)) {
// do stuff particular to the current controller
$route.current.scope.NextAction()
}
};
and the corresponding button code is as follows:
<div class="footer">
<button id="main-prev-button" type="button" class="btn btn-primary previous" ng-click="prevPage($event)" ng-disabled="isFormValid()">Previous</button>
<button id="main-next-button" type="button" class="btn btn-primary next" ng-click="nextPage($event)" ng-disabled="isFormValid()">Next</button>
</div>
Each page code looks something like this
<ng-form id="currentform" name="currentform">
<label>Full Name</label>
<input type="text" class="form-control" ng-model="nl.firstName" id="firstName" placeholder="First Name" name="firstName" ng-minlength="5" ng-maxlength="20" ng-required="true">
<pre>currentform.firstName.$error = {{ currentform.firstName.$error | json }}</pre>
<ng-messages for="currentform.firstName.$error">
<ng-message when="required">You did not enter a field</ng-message>
<ng-message when="minlength">Your field is too short</ng-message>
<ng-message when="maxlength">Your field is too long</ng-message>
</ng-messages>
</ng-form>
Add the $route to the root scope $rootScope and then access it in your html/view using the $rootScope.$route.
E.g:
angular.module('myApp').run(['$rootScope', '$route',
function ($rootScope, $route) {
$rootScope.$route = $route;
}]);

AngularJS: Radio buttons do not work with Bootstrap 3

I have a radio button, which sets the value of True or False based on the value of transaction type
The demo can be found here
The problem is when I click on any of the radio button, the value of $scope.transaction.debit does not change
My javascript code is
var app = angular.module('myApp', []);
app.controller("MainCtrl", function($scope){
$scope.transaction = {};
$scope.transaction.debit=undefined;
console.log('controller initialized');
});
Please let me know what I am doing wrong.
Also, I do not want to use Angular-UI or AngularStrap for this purpose, unless no other option is available.
I modified dpineda's solution. You can use without removing bootsrap.js dependency. Also there is a working example here.
This is the flow:
Remove data-toggle="buttons" for preventing bootstrap execution.
Add some CSS for fixing the broken view (btn-radio css class)
Add some AngularJS logic for checked style effect.
html
<div class="btn-group col-lg-3">
<label class="btn btn-default btn-radio" ng-class="{'active': transaction.debit == '0'}">
<input type="radio" data-ng-model="transaction.debit" value="0"> Debit
</label>
<label class="btn btn-default btn-radio" ng-class="{'active': transaction.debit == '1'}">
<input type="radio" data-ng-model="transaction.debit" value="1"> Credit
</label>
</div>
<p>Transaction type: {{transaction.debit}}</p>
JavaScript
var app = angular.module('myApp', []);
app.controller("MainCtrl", function($scope) {
$scope.transaction = {
debit: 0
};
});
Style
.btn-radio > input[type=radio] {
position : absolute;
clip : rect(0, 0, 0, 0);
pointer-events : none;
}
I found the problem in bootstrap.js. Comment the line e.preventDefault(), it works.
// BUTTON DATA-API
// ===============
$(document)
.on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
var $btn = $(e.target)
if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
Plugin.call($btn, 'toggle')
e.preventDefault() //Before
//e.preventDefault() //After
})
.on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
$(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
})
You have a large label stuck over the top of the radio buttons which prevents input to your radio buttons.
The html should read:
<input type="radio" data-ng-model="transaction.debit" value="True">Debit</input>
<input type="radio" data-ng-model="transaction.debit" value="False">Credit</input>
It then works, of course it may not look the way you want it to then.
if you remove de bootstrap code you can control the styles with conditionals
<label class="btn btn-default" ng-class="{'active': transaction.debit == 'some'}">
<input type="radio" data-ng-model="transaction.debit" name="debit" value="some"> Some
</label>
<label class="btn btn-default" ng-class="{'active': transaction.debit == 'other'}">
<input type="radio" data-ng-model="transaction.debit" name="debit" value="other"> Other
</label>
Here's a working version using a new directive:
html
<section ng-controller="MainCtrl">
<div class="form-group">
<label class="col-lg-2 control-label">Type</label>
<div class="btn-group col-lg-3" data-toggle="buttons">
<label class="btn btn-default" radio-button ng-model="transaction.debit" value="True">
Debit
</label>
<label class="btn btn-default" radio-button ng-model="transaction.debit" value="False">
Credit
</label>
</div>
<p>Transaction type: {{transaction.debit}}</p>
</div>
</section>
javascript
// Code goes here
var app = angular.module('myApp', []);
app.controller("MainCtrl", function($scope){
$scope.transaction = {};
$scope.transaction.debit=undefined;
console.log('controller initialized');
});
app.directive("radioButton", function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
element.bind('click', function () {
if (!element.hasClass('active')) {
scope.$apply(function () {
scope.transaction.debit = attrs.value;
});
}
});
}
};
})
Based on francisco.preller's answer I wrote two solutions trying to make it fit for generic use, without loosing the input tags:
html:
<label class="btn btn-info" radiobuttonlbl>
<input ng-model="query.gender" type="radio" value="0">male
</label>
solution #1:
.directive("radiobuttonlbl", function() {
return {
restrict: 'A',
link: function(scope, element, attrs, ctrl) {
element.bind('click', function () {
var input_elem = angular.element(element.find('input')[0]);
(function(o, s, v) {
s = s.replace(/\[(\w+)\]/g, '.$1');
s = s.replace(/^\./, '');
var a = s.split('.').reverse();
while(a.length>1) {
var k = a.pop();
o = o[k];
}
scope.$apply(function(){ o[a.pop()]=v;});
})(scope, input_elem.attr('ng-model'), input_elem.attr('value'));
});
}
};
})
Solution #2:
.directive("radiobuttonlbl", function() {
return {
restrict: 'A',
link: function(scope, element, attrs, ctrl) {
element.bind('click', function () {
var input_elem = angular.element(element.find('input')[0]);
input_elem.prop('checked',true);
input_elem.triggerHandler('click');
});
}
};
})
I have a feeling the first one is better because it make angular do the updating work.
If someone is still searching for an easy way to do this (I personally am hesitant to overload my code with directives), here is what I did:
You can set the value using ng-click on the label. Furthermore, notice the ng-init and active class on the label of the first radio item. This way, you can let bootstrap do its thing, and angular do its thing. The only drawback is you are not letting angular control this using ng-model.
<div class="btn-group col-lg-3" data-toggle="buttons">
<label class="btn btn-default active" ng-init="transaction.debit=true" ng-click="transaction.debit=true">
<input type="radio" checked> Debit
</label>
<label class="btn btn-default" ng-click="transaction.debit=false">
<input type="radio"> Credit
</label>
</div>
I had the same problem. Use ng-click on your labels and it will work fine with bootstrap
<label class="btn btn-default" ng-click="transaction.debit = 'debit'">
Here it's working in plunker
I have the same problem, in my case, the default style change and can't use angular ng-model inside any radio or checkbox button. So i read some articles and found that sometimes if you load JQuery after Bootstrap it overwrites any other instance of jQuery, and it prevent default styles and components to be loaded as bootstrap components, this also happens if you load angularJS after jQuery or viceversa.
PS.- My answer: Check your load script stack, play with it and find which order works for you. (first jquery, then angularJs, finally bootstrap). Usually you require to jQuery to be the first option, Angular and almost every new framework works on top of it. Cheers.

Categories