Basically I am trying to access controller scope property from directive's controller function. I am doing it through $parent property. It works fine for static directive but not for dynamically created directive.
please have a look on my plunker
Dynamic Directive
In a plunker, when I click on folder with Id = 1. all goes good and folder path shows as "1 path". Same goes for folder with Id = 2.
But it does not work for dynamically appended folder with Id = n
I am somewhat new to angular. Any help would be much appreciated.
Updated Answer
In light of the latest requirement:
I am trying to call the directive function (i.e updateMap) from
controller.
You can use a Service to share variables between Controllers and Isolated Directives. In the example below, the Service holds the function that will be executed. Each directive when clicked will set the Service's function to it's own updateMap() function. Then the Controller in onFolderPathClicked() calls the Services executeFunction() function, which runs the previously set function.
script.js:
var module = angular.module('testApp', []);
module.service('updateMapService', function(){
var updateMapFunction = null;
this.executeFunction = function(){
updateMapFunction();
};
this.setFunction = function(fn){
updateMapFunction = fn;
};
});
module.controller('crtl', function($scope, updateMapService) {
$scope.onFolderPathClicked = function(){
updateMapService.executeFunction();
};
});
module.directive('folder', function($compile) {
return {
restrict: 'E',
scope: {
id: '#',
folderPath: "="
},
template: '<p ng-click="onFolderClicked()">{{id}}</p>',
controller: function($scope, $element, updateMapService) {
$scope.onFolderClicked = function(){
updateMapService.setFunction(updateMap);
addFolder();
};
var addFolder = function() {
$scope.folderPath = $scope.id + ":click here for calling update map";
var el = $compile('<folder id="n" folder-path="folderPath"></folder>')($scope);
$element.parent().append(el);
};
var updateMap = function() {
alert('inside updateMap()..' + $scope.id);
}
}
}
});
index.html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-app="testApp" ng-controller="crtl">
<div>FolderPath : <a ng-click="onFolderPathClicked()">{{ folderPath }}</a> </div>
<folder id="1" folder-path="folderPath"></folder>
<folder id="2" folder-path="folderPath"></folder>
</div>
</html>
You could also move folder-path into a Service to save from passing it in as an attribute. The code smell being that passing it in as an attribute means doing so twice, whereas in a Service it means setting it and getting it once (code reuse).
Related
as you can see I have opened .xml file and parsed it to a xmlDoc. What I am trying to achieve is that this xmlDoc will be accessible from the whole script(I want to make some functions later which will be displaying elements from .xml to a screen). I searched the web and find that it is possible via global variable $rootScope but couldn't implement it correctly. I hope you guys can help me. Thanks.
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body ng-app="myApp">
<p id="title">asd</p>
<button name="opt1" ng-click="">YES</button>
<button name="opt2" ng-click="">NO</button>
<script>
var app = angular.module('myApp', []);
var parser, xmlDoc;
app.run(function($rootScope, $http) {
text = $http.get("file.xml").then(function(response) {
return response.data;
}).then(function(text) {
parser = new DOMParser();
xmlDoc = parser.parseFromString(text,"text/xml");
document.getElementById("title").innerHTML =
xmlDoc.getElementsByTagName("title")[0].childNodes[0].nodeValue;
});
});
</script>
</body>
</html>
There are many ways in angular to declare and use a global variable.
examples:
1. By using $rootScope.
we need to add a dependency in our controller or service like:
app.controller('myCtrl', ['$rootScope', '$scope', function($rootScope, $scope){
$rootScope.yourVar = 'YourValue';
....
....
}]);
and then You can use this `yourVar` variable anywhere in your code.
Another way is by using angular factory or servive.
app.factory('factoryObj', ['$scope', function($scope){
let factoryObj.yourVar = 'yourValue';
return factoryObj;
}]);
Now in any controller or any other service, by using this factoryObj as a dependency and then inside that controller or service we can use factoryObj.yourVar as a variable. as:
app.controller('myCtrl',['$rootScope','$scope','factoryObj'function($rootScope,$scope, factoryObj){
console.log('factoryObj.yourVar value: ',factoryObj.yourVar);
}]);
Im new to angular js and im not able to figure out how to change the child controller scope variable from parent controller. Here is the code snippet for that:
var mainApp = angular.module("mainApp", []);
var parentCtrl = function($rootScope, $scope, shareService, $log){
shareService.setDetails($scope.pdetails);
}
var mainCtrl1 = function($rootScope, $scope, shareService, $log){
$scope.msg = "Controller 1";
$scope.details = shareService.details;//shareService.details;
}
var mainCtrl2 = function($rootScope, $scope, shareService){
$scope.msg = "Controller 2";
$scope.details = shareService.details;//shareService.details;
}
parentCtrl.$inject = ["$rootScope", "$scope", "shareService", "$log"];
mainCtrl1.$inject = ["$rootScope", "$scope", "shareService", "$log"];
mainCtrl2.$inject = ["$rootScope", "$scope", "shareService", "$log"];
mainApp.controller("parentController", parentCtrl)
.controller("mainController1", mainCtrl1)
.controller("mainController2", mainCtrl2)
.factory("shareService", function(){
var shareData = {
details : "sadfgs detaisdfadsfasdf..",
setDetails: function(value){
this.details = value;
}
};
return shareData;
});
<html>
<head>
<title>Angular JS Views</title>
<script src='lib/angular.js'></script>
<script src='js/mainApp.js'></script>
<script src='js/studentController.js'></script>
</head>
<body ng-app = 'mainApp' ng-controller='parentController' ng-strict-di>
<div ng-controller='mainController1'>
1. Msg : {{msg}}<br/>
Share Details: {{details}}<br/><br/>
</div>
<div ng-controller='mainController2'>
2. Msg : {{msg}}<br/>
Share Details: {{details}}<br/><br/>
</div>
<input type='text' ng-model='pdetails'/>
</body>
</html>
Here is the Plunker link:
https://plnkr.co/edit/hJypukqMmdHSEZMVnkDO?p=preview
In order to change value of child controller from parent controller you can use $broadcast on $scope.
syntax
$scope.$broadcast(event,data);
$broadcast is used to trigger an event(with data) to the child scope from current scope.
In child controller use $on to receive the event(with data).
Here id the code snippet:
app.controller("parentCtrl",function($scope){
$scope.OnClick=function()
{
$scope.$broadcast("senddownward",$scope.messege);
}
});
app.controller("childCtrl",function($scope){
$scope.$on("senddownward",function(event,data)
{
$scope.messege=data;
});
});
In this example I am broadcasting the event on ng-click,you can use some other custom event.like $watch on $scope.
See this example
https://plnkr.co/edit/efZ9wYS2pukE0v4JsNCC?p=preview
P.S. you can change the name of event from senddownward to whatever you want
You can access the parent's scope properties directly due to the scope inheritance:
<div ng-controller='mainController1'>
Share Details: {{pdetails}}
</div>
Your example does not work because the controllers get executed only once before the view is rendered, pdetails is empty at that moment.
To monitor the changes to pdetails, you can use $watch in the child controller:
$scope.$watch('pdetails', function(newVal) {
$scope.details = newVal;
});
Here is my url
For Login - http://localhost/ang/#/login
For Dashboard - http://localhost/ang/#/dashboard
Here is my html for body tag
If this is current url is http://localhost/ang/#/login then the body should have the class="login-layout" tag i.e.,
<body ng-cloak="" class="login-layout>
else it should have
<body ng-cloak="" class="no-skin">
I tried to take do this in php by i can't take the url after # like said here
Is this possible to do in AngularJS itself ?
Update :
I tried to do this in AngularJS
From the controller i can get the url after #
var nextUrl = next.$$route.originalPath;
but how can i change the class name..
this is how i would do
<body class="{{currentclass}}" ng-cloak>
now in login controller just do
$rootScope.currentclass = "login-layout";
and in every other controller just do
$rootScope.currentclass = "no-skin";
OR
in app.run just check for the login path.
app.run(function($rootScope, $location){
rootScope.$on('$routeChangeStart', function(event, next, current){
if ($location.path() == '/login') {
$rootScope.currentclass = "login-layout";
}
else{
$rootScope.currentclass = "no-skin";
}
});
I need to do this in a project and this is how I achieved it:
inside my main app controller I have:
// Inject the $location service in your controller
// so it is available to be used and assigned to $scope
.controller('AppCtrl', ["$scope", "$location",...
// this_route() will be the variable you can use
// inside angular templates to retrieve the classname
// use it like class="{{this_route}}-layout"
// this will give you -> e.g. class="dashboard-layout"
$scope.this_route = function(){
return $location.path().replace('/', '');
};
Which exposes the current route name on the scope.
then my body tag simply reads this like:
<body ng-controller="AppCtrl" class="{{this_route()}}-view" ng-cloak>
You can similarly use this with $state and read the $state.current.url and assing it to scope
You can do it like in my example below
<body ng-cloak="" ng-class="bodyClass" class="login-layout>
$scope.bodyClass = "mainClass"
var nextUrl = next.$$route.originalPath;
if (..) {
$scope.bodyClass = "sometAnotherMainClass";
}
Shoud controller should look like this
angular.module('youAppName').controller('yourController', [ "$scope", function($scope) {
$scope.bodyClass = "mainClass"
var nextUrl = next.$$route.originalPath;
if (..) {
$scope.bodyClass = "sometAnotherMainClass";
}
}]);
I think that the most "Angular" way to solve it is using a directive. The biggest benefit is that you don't have to set a scope variable in every controller you use.
This is how it would look:
app.directive('classByLocation', ['$location', function($location) {
var link = function(scope, element) {
scope.$on('$routeChangeSuccess', function() {
if ($location.path() == '/login') {
element.removeClass('no-skin').addClass('login');
}
else{
element.removeClass('login').addClass('no-skin');
}
});
};
return {
restrict: 'A',
link: link
};
}]);
And in your HTML:
<body class-by-location>
Here is a working plunker: http://plnkr.co/edit/zT5l6x9KYOT1qeMOxtQU?p=preview
I have this code:
<!doctype html>
<html ng-app="myApp" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="utf-8">
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
</head>
<body>
<script type="text/javascript">
var app = angular.module("myApp", []);
app.controller("FirstCtrl",function ($scope) {
$scope.count = 0;
$scope.increment = function (){
$scope.count = $scope.count + 1;
}
});
</script >
<div ng-controller="FirstCtrl">
<button class="btn" ng-model="count"
ng-click="increment()">
Click to increment</button>
{{ count }}
</div>
what is wrong with it?
When I work without controllers it's works fine but when I use app and app.controller It will not work. why is that? am I doing something wrong?
The "Controller as" syntax is only available from version 1.1.5+
By what i know when using "Controller as" and you want to reference a variable with the assigned controller alias (in your case "app1") then you should assing the variables with "this." syntax in the controller, or access the variables without the "app1", then it will try to get it from the scope.
http://jsbin.com/zehagogoku/3/edit
var app = angular.module("myApp", []);
app.controller("FirstCtrl",function ($scope) {
this.count = 0;
this.increment = function (){
this.count = this.count + 1;
};
$scope.count = 0;
$scope.increment = function(){
$scope.count = $scope.count + 1;
};
});
You are mixing styles here. In your HTML, you are using the Controller As syntax, where you write FirstCtrl as app1. This makes an object called app1 on the scope, which is an instance of your FirstCtrl. app1.count, app1.increment(), etc. would be properties of the FirstCtrl object.
In your controller, you are not creating properties on the controller. You are, instead, assigning your variables as properties on the $scope object.
Using $scope has advantages, in that it is essentially a global object, so it is accessible from everywhere in your app. However, it's disadvantages are also rooted in the fact that it's a global object.
You can either change your javascript to match the Controller As syntax, as shown:
app.controller("FirstCtrl",function () {
//create an alias to this for consistent reference
var app1 = this;
app1.count = 0;
app1.increment = function (){
app1.count += 1;
};
});
Or, you can change your HTML to use $scope:
<div ng-controller="FirstCtrl">
<button class="btn" ng-model="count"
ng-click="increment()">
Click to increment</button>
{{ count }}
</div>
change in the javascript:
$scope.count += 1;
note, you don't have to reference $scope inside the HTML, as it's presence is implicit. However, this line in your javascript $scope.count = this.count + 1;
will never work in either case, again because you are mixing styles.
Also, as mentioned, Controller As syntax requires Angular 1.1.5 or higher.
I've built an app with firebase that can login a user and attain their id, but I can't figure out how to incorporate this with a user making a submission of a string.
See Code pen here: http://codepen.io/chriscruz/pen/OPPeLg
HTML Below:
<html ng-app="fluttrApp">
<head>
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.1/jquery-ui.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js"></script>
<script src="https://cdn.firebase.com/js/client/2.0.2/firebase.js"></script>
<script src="https://cdn.firebase.com/libs/angularfire/0.9.0/angularfire.min.js"></script>
</head>
<body ng-controller="fluttrCtrl">
<button ng-click="auth.$authWithOAuthPopup('google')">Login with Google</button>
<li>Welcome, {{user.google.displayName }}</li>
<button ng-click="auth.$unauth()">Logout with Google</button>
<input ng-submit= "UpdateFirebaseWithString()" ng-model="string" ></input>
Javascript Below:
<script>
var app = angular.module("fluttrApp", ["firebase"]);
app.factory("Auth", ["$firebaseAuth", function($firebaseAuth) {
var ref = new Firebase("https://crowdfluttr.firebaseio.com/");
return $firebaseAuth(ref);
}]);
app.controller("fluttrCtrl", ["$scope", "Auth", function($scope, Auth) {
$scope.auth = Auth;
$scope.user = $scope.auth.$getAuth();
$scope.UpdateFirebaseWithString = function () {
url = "https://crowdfluttr.firebaseio.com/ideas"
var ref = new Firebase(url);
var sync = $firebaseAuth(ref);
$scope.ideas = sync.$asArray();
$scope.ideas.$add({
idea: $scope.string,
userId:$scope.user.google.id,
});
};
}])
</script>
</body>
</html>
Also assuming, the above dependencies, the below works to submit an idea, but the question still remains in how to associate this with a user. See codepen here on this: http://codepen.io/chriscruz/pen/raaENR
<body ng-controller="fluttrCtrl">
<form ng-submit="addIdea()">
<input ng-model="title">
</form>
<script>
var app = angular.module("fluttrApp", ["firebase"]);
app.controller("fluttrCtrl", function($scope, $firebase) {
var ref = new Firebase("https://crowdfluttr.firebaseio.com/ideas");
var sync = $firebase(ref);
$scope.ideas = sync.$asArray();
$scope.addIdea = function() {
$scope.ideas.$add(
{
"title": $scope.title,
}
);
$scope.title = '';
};
});
</script>
</body>
There a couple of things tripping you up.
Differences between $firebaseand $firebaseAuth
AngularFire 0.9 is made up of two primary bindings: $firebaseAuth and $firebase. The $firebaseAuth binding is for all things authentication. The $firebase binding is for synchronizing your data from Firebase as either an object or an array.
Inside of UpdateFirebaseWithString you are calling $asArray() on $firebaseAuth. This method belongs on a $firebase binding.
When to call $asArray()
When you call $asArray inside of the UpdateFirebaseWithString function you will create the binding and sync the array each time the function is called. Rather than do that you should create it outside of the function so it's only created one item.
Even better than that, you can abstract creation of the binding and the $asArray function into a factory.
Plunker Demo
app.factory("Ideas", ["$firebase", "Ref", function($firebase, Ref) {
var childRef = Ref.child('ideas');
return $firebase(childRef).$asArray();
}]);
Get the user before the controller invokes
You have the right idea by getting the user from $getAuth. This is a synchronous method, the app will block until the user is returned. Right now you'll need to get the user in each controller. You can make your life easier, by retrieving the user in the app's run function. Inside of the run function we can inject $rootScope and the custom Auth factory and attach the user to $rootScope. This way the user will available to all controllers (unless you override $scope.user inside of your controller).
app.run(["$rootScope", "Auth", function($rootScope, Auth) {
$rootScope.user = Auth.$getAuth();
}]);
This is a decent approach, but as mentioned before $scope.users can be overridden. An even better way would be to resolve to user from the route. There's a great section in AngularFire guide about this.
Associating a user with their data
Now that we have the user before the controller invokes, we can easily associate their id with their input.
app.controller("fluttrCtrl", ["$scope", "Ideas", function($scope, Ideas) {
$scope.ideas = Ideas;
$scope.idea = "";
$scope.UpdateFirebaseWithString = function () {
$scope.ideas.$add({
idea: $scope.idea,
userId: $scope.user.google.id,
}).then(function(ref) {
clearIdea();
});
};
function clearIdea() {
$scope.idea = "";
}
}]);