How to get attribute value on click? - angularjs

Hey guys I need to get tha data value of the button when I click on it. I tried in this way but it doesn't work..
var app = angular.module('myApp', []);
app.controller("myAppCtrl", function($scope, $attrs) {
$scope.events = {}
$scope.events.btnLoadMore = function() {
var panel = $attrs.emptywidget;
alert(panel);
}
});
HTML
<html ng-app="myApp">
<head>
<title></title>
</head>
<body ng-controller="myAppCtrl">
<button data-emptywidget="#panel2" ng-click="events.btnLoadMore()">Click</button>
</body>
</html>

The only way you can access that data attribute would be through standard DOM access, which is not wise inside a controller. The $attrs variable you're passing into your controller also won't give you very much, as your controller doesn't directly related to anything (or at least it doesn't need to or shouldn't)
If you need to do something like that, then you could change your ng-click to something like
ng-click="events.btnLoadMore('#panel2')"
Then change the definition of your btnLoadMore function to take an argument. Alternatively you can write a directive that would be given that value, but that's more complex. But it depends what you want to do with it really. The above should work though

You could use a combination of angular.element and passing in the $event source as well: http://jsfiddle.net/ahchurch/9USEv/1/
<div ng-controller="myAppCtrl">
<button data-emptywidget="#panel2" ng-click="events.btnLoadMore($event)">Click</button>
</div>
var app = angular.module('myApp', []);
app.controller("myAppCtrl", function($scope, $attrs) {
$scope.events = {}
$scope.events.btnLoadMore = function($event) {
console.log($event);
var panel = angular.element($event.target).attr("data-emptywidget");
console.log(panel);
}
});

You can do a few different things.
1) You can pass in the data via the function so like
ng-click="events.btnLoadMore('#panel2')"
Then change your function to match so:
$scope.events.btnLoadMore = function(panel) {
alert(panel);
}
2) You can pass in the data via the $event parameter
ng-click="events.btnLoadMore($event)"
Then change your function to match so:
$scope.events.btnLoadMore = function(clickEvent) {
alert(clickEvent.target.attributes['data-emptywidget'].value);
}
3) You can just look at the arguments passed in to the function
$scope.events.btnLoadMore = function() {
alert(arguments[0].target.attributes['data-emptywidget'].value);
}

Related

AngularJS - two way binding not working using service

I am learning Angular using W3Schools.
I just modified an example about "Services"... The following is the code:
<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<p><input type="text" ng-model="num"></p>
<h2>{{num}}</h2>
<h1>{{hex}}</h1>
</div>
<p>A custom service whith a method that converts a given number into a hexadecimal number.</p>
<script>
var app = angular.module('myApp', []);
app.service('hexafy', function() {
this.myFunc = function (x) {
return x.toString(16);
}
});
app.controller('myCtrl', function($scope, hexafy) {
$scope.num = 200;
$scope.hex = hexafy.myFunc($scope.num);
});
</script>
</body>
</html>
When I update the textbox, the "HEX" part is not updating. Why?
Your hexafy.myFunc is called only once when the controller is initialized, hence only the initial value is converted to hex. If you want a function to be called on the value change of a scope variable in runtime, you need filters. AngularJS has a lot of inbuilt filters that are ready to use.
You can also define a custom filter, just like you define services or controllers.
<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<p><input type="text" ng-model="num"></p>
<h2>{{num}}</h2>
<h1>{{num | hexafy}}</h1>
</div>
<p>A custom filter that converts a given number into a hexadecimal number.</p>
<script>
var app = angular.module('myApp', []);
app.filter('hexafy', function() {
return function (x) {
return Number(x).toString(16); // Also convert the string to number before calling toString.
}
});
app.controller('myCtrl', function($scope) {
$scope.num = 200;
//$scope.hex = hexafy.myFunc($scope.num);
});
</script>
</body>
</html>
Further reading: AngularJS Filters
NOTE: A filter is a good idea if you're gonna be using the hexafy functionality at multiple places in/across views. Otherwise, it is just an overkill and the $scope.$watch method will work fine for you, as described in other answers
$scope.hex is not updating because there're no way for it update itself.
The hexafy.myFunc is called only once when the controller is loaded for the first time.
If you want want the $scope.hex property to change with num, you might need a watch on the num property.
$scope.$watch('num', function(newVal, oldVal) {
$scope.hex = hexafy.myFunc($scope.num); /// or newVal
}
The function passed in $scope.$watch will be called everytime the value of $scope.num changes.
for more info see https://docs.angularjs.org/api/ng/type/$rootScope.Scope (the watch section)
Hope it helps.
No need of a service here, you can simple use $watch on the num. See below code snippet, it will update your ui, I have updated your controller code, please check.
app.controller('myCtrl', function($scope, hexafy) {
$scope.num = 200;
$scope.hex = "some default val";
$scope.$watch('num', function(newValue, oldValue) {
$scope.hex = newValue.toString();
});
});
Your Text box is only bind to 'num'. '$scope.hex is not binded to your text box'. So that it is not update when you typing text. You could use '$watch' on 'num'. Read here
app.controller('myCtrl', function($scope, hexafy) {
$scope.num = 200;
$scope.$watch('num', function() {
$scope.hex = hexafy.myFunc(parseInt($scope.num));
});
});

AngularJS: How can I assign values to main page from templates and show them?

In my mine file I have the div with ng-view and it is loaded with the some template. Inside the temp`late I have a button which will change the value.
after to click the button, I am trying to show the value in the index but I recieving a null value.
In index.html I could have something like:
<html>
...
<body ng-app="productsApp" ng-Controller="mycontroller">
<div ng-view></div>
{{value}}
</body>
</html>
In my controller I have something like
angular.module('productsApp').controller('ProductController',
['$scope', 'dataService', function ($scope, dataService) {
$scope.value;
$scope.button = function () {
$scope.value= "123";
};
}]);
The template could be something like:
<button ng-click="button">CHANGE</button>
How can I assign values to main page from templates and show them?
You need to beware which controller you set, the names have to match. Also beware of typos, even when typing fast ;). Also look into the function call. You forgot to use () at the end. And naming things a little bit better would be recommended to (I don't say my namings are the best, but calling a function button is not very readable).
HTML
<body ng-controller="MainCtrl">
<p>Value {{value}}!</p>
<button ng-click="btnPressed()">Change</button>
</body>
JS
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.value = "456";
$scope.btnPressed = function() {
$scope.value = 123;
}
});
The controllers name in this example is MainCtrl. You need to refer the correct name as well in your HTML (you mix mycontroller and ProductController).
Working Plnkr
http://plnkr.co/edit/tpl:8rFfZljYNl3z1A4LKSL2
You have defined $scope.button in ProductController which set $scope.value.
But this $scope.value is come under scope of ProductController and not available under scope of mycontroller which parent scope.Thats why you getting null value.
Define $scope.value and $scope.button in mycontroller.
Or better way is define this functionality in some factory and access it wherever needed.
Or define that function and variable in $rootScope like
$rootScope.value;
but it make this global.
or define like this
$scope.$parent.value;

Scope values to a requested content

I have a view that contains a button, when the button is clicked, a $http.get request is executed and the content is appended on the view.
View:
<button ng-click="includeContent()">Include</button>
<div id="container"></div>
Controller:
$scope.includeContent = function() {
$http.get('url').success(function(data) {
document.getElementById('container').innerHTML = data;
}
}
The content to include:
<h1>Hey, I would like to be {{ object }}</h1>
How can I scope a value to object? Do I need to approach this in a complete different way?
The built-in directive ng-bind-html is the way you are looking for.
Beware, that ng-bind-html requires a sanitized string, which is either done automatically when the correct libary is found or it can be done manually ($sce.trustAsHtml).
Don't forget to inject $sce in your controller.
$scope.includeContent = function() {
$http.get('url').success(function(data) {
$scope.data = $sce.trustAsHtml(data);
}
}
<button ng-click="includeContent()">Include</button>
<div ng-bind-html="data"></div>
As you also want to interpolate your requested HTML, I suggest using $interpolate or, if it can contain whole directives or should have a full fledged two-way-data-binding, use $compile instead.
In your case alter the assignment to
$scope.data = $sce.trustAsHtml($interpolate(data)($scope));
Don't forget to inject $interpolate/$compile aswell.
As I don't know about your $scope structure I assume that "object" is available in this scope. If this isn't the case then change the $scope parameter to whatever object contains your interpolation data.
You should use a controller to do this (I imagine you are since you're using $scope).
ctrl function () {
var ctrl = this;
ctrl.includeContent = function () {
$http.get("url").success(function (data) {
ctrl.object = data;
});
};
}
<div ng-controller="ctrl as ctrl">
<button ng-click="ctrl.includeContent()">Include</button>
<div id="container">
<h1 ng-show="ctrl.object">Hey, I would like to be {{ctrl.object}}</h1>
</div>
</div>
You need not select an element and append the data to it. Angular does it for you. That's what is magic about angular.
In your controller's scope, just update object and angular does the heavy-lifting
$scope.includeContent = function() {
$http.get('url').success(function(data) {
$scope.object = data;
}
}
If that's html code from a server, then you should use the 'ng-bind-html' attribute:
<button ng-click="includeContent()">Include</button>
<div id="container" ng-bind-html="htmlModel.ajaxData"></div>
Controller:
$scope.htmlModel = {ajaxData:''};
$scope.includeContent = function() {
$http.get('url').success(function(data) {
$scope.htmlModel.ajaxDataL = data;
}
}
One way is to use ng-bind-html as suggested.
Another way is with $compile:
app.controller('MainCtrl', function($scope, $http, $compile) {
$scope.error='error!!!';
$scope.includeContent = function() {
$http.get('url').success(function(data) {
var elm = angular.element(document.getElementById('container')).html(data);
$compile(elm)($scope);
}).error(function(){
var elm = angular.element(document.getElementById('container')).html('{{error}}');
$compile(elm)($scope);
})
}
});
Also, typically in angular, when you want to manipulate the DOM you use directives.
DEMO

AngularJS: making a simple stopwatch, a little confused

Here's the code so far: http://plnkr.co/edit/DK9SSrIJZieSxDb5EN41?p=preview
Goal: press the start button and the seconds counter begins.
cant seem to get it working, the CountdownCtrl function should only start when button clicked.. so do i have two seperate functions, or how do i go about doing this.
My fork of your plunk shows how to do this. Check it out. I simplified your original code to show how you can make this easier to understand and to maintain.
First thing, you needed to initialize your module correctly.
var myApp = angular.module('myApp', []); // the second param is your module's dependencies. since you have none, it MUST be an empty array
Next, declare your controller, and take in two dependencies, the $scope and $timeout:
myApp.controller('CountDownCtrl', ['$scope', '$timeout', function($scope, $timeout) { // angular will inject the controller's $scope and the $timeout service
// todo
}]);
Then, declare an item on the $scope to store the counter value. This value will be accessible in the view. Then define a function to perform the actual countdown. It simply increments $scope.value and then sets a new timeout to itself, in effect calling itself every second until it is canceled.
Finally, add functions to start and stop the countdown:
$scope.value = 0;
// because of JS closures, $scope from the outer context will be included in countdown()'s context.
function countdown() {
$scope.value++;
$scope.timeout = $timeout(countdown, 1000);
}
$scope.start = function() {
countdown();
};
$scope.stop = function() {
$timeout.cancel($scope.timeout);
};
Lastly, make sure your view instructs it what controller to use and add in the appropriate controller functions.
<body ng-controller="CountDownCtrl">
{{value}}
<button ng-click="start()">Start</button>
<button ng-click="stop()">Stop</button>
</body>
You were definitely on the right track. There were just a few things that weren't wired up.
Here is an updated plunker.
These are the main things that were needed to get everything working:
Defined 'myApp' as the name of the module and wired up. This allows controller to be registered correctly to the module.
Make CountDownCtrl function a scoped function.
Set up of the dependencies $timeout was moved to be registered against the controller (not the function).
HTML:
<html ng-app="myApp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="m">
{{value}}
<button ng-click="CountDownCtrl()">Start</button>
</body>
</html>
JS:
var myApp = angular.module('myApp', []);
myApp.controller('m', ['$scope', '$timeout', function($scope, $timeout) {
$scope.value = 0;
$scope.CountDownCtrl = function() {
$scope.value = 0;
var change = function() {
$scope.value += 1;
$timeout(change,1000);
};
$timeout(change, 1000);
}
}]);

Angularjs different controllers for one include

I want to have element where i can have 2 views using their own controller but only one at a time.
I can't use a ng-view and use the routeProvider because in the future I need to include more ng-includes that need to change their content depending on the possible actions.
I created a fiddle http://jsfiddle.net/EvHyT/29/.
So I used a ng-include and then I set the src for it from a main controller. At that point I want to use controller 1 or controller 2.
function MainCtrl($rootScope, $scope, navService){
$scope.template = {};
$scope.loadCtrl1=function(param){
navService.loadCtrl1(param);
}
$scope.loadCtrl2=function(param){
navService.loadCtrl2(param);
}
$rootScope.$on('loadCtrl1', function(e, args){
$scope.template = {'url': 'temp1'};
});
$rootScope.$on('loadCtrl2', function(e, args){
$scope.template = {'url': 'temp2'};
});
}
I use a service for communication because i want to move the load controller functions in a child controller.
var myApp = angular.module('myApp',[]);
myApp.factory('navService', function($rootScope) {
return {
loadCtrl1:function(param){
$rootScope.$broadcast('loadCtrl1', {'id':param});
},
loadCtrl2:function(param){
$rootScope.$broadcast('loadCtrl2', {'id':param});
}
};
});
I know this solution is bad because the controllers are not yet created when a different template is inserted so my event listener will not fire. Also can I destroy the previous instances of the controller because switching between the two controllers makes my event fire multiple times.
function Child1Ctrl($scope, $rootScope){
$rootScope.$on('loadCtrl1', function(e, args){
alert(args.id);
});
}
function Child2Ctrl($scope, $rootScope){
$rootScope.$on('loadCtrl2', function(e, args){
alert(args.id);
});
}
You don't need to broadcast (and shouldn't be broadcasting) to make this happen.
In my experience, if you're broadcasting on the rootScope, you're probably working too hard.
A simpler way of loading the template is very similar to what you're doing:
my.NavService = function() {
this.template = 'index.html';
this.param = null;
};
my.NavService.prototype.setTemplate(t, p) {
this.template = t;
this.param = p;
};
my.ctrl = function($scope, nav) {
$scope.nav = nav;
$scope.load = function(t, p) {
nav.setTemplate(t, p);
};
};
my.ctrl1 = function($scope, nav) {
$scope.param = nav.param;
};
my.ctrl2 = function($scope, nav) {
$scope.param = nav.param;
};
module.
service('nav', my.NavService).
controller('mainCtrl', my.ctrl).
controller('ctrl1', my.ctrl1).
controller('ctrl2', my.ctrl2);
<script type="text/ng-template" id="temp1.html">
<div ng-controller="ctrl1">Foo {{param}}.</div>
</script>
<script type="text/ng-template" id="temp2.html">
<div ng-controller="ctrl2">Bar {{param}}.</div>
</script>
<div ng-controller="mainCtrl">
<a ng-click="load('temp1.html', 16)">Load 1</a>
<a ng-click="load('temp2.html', 32)">Load 2</a>
<div ng-include src="nav.template"></div>
</div>
A setup like this would work much better for you.
Alternatively, you should look into selectively showing elements with ng-switch. Unlike ng-show/hide, ng-switch does not simply add "display:none" to the CSS. It removes it from the DOM.
Some notes:
The above example may not be a working example, it's a demonstration of the idea.
A working example can be seen here: http://jsbin.com/ofakes/1 It modifies your original code.
JSFiddle had some issues with loading the include from the on page script
tag.
JSBin was a little better.
I didn't really get it to work as expected until I broke out the templates
into their own files.

Resources