Have an angular application. Using ui-select.
<div class="col-md-12" ng-controller="FlatController as flat">
<form ng-submit="flat.createFlat()">
<ui-select ng-model="flat.flatData.typelocal" theme="bootstrap">
<ui-select-match placeholder="Type" id="localtype">
{{ $select.selected.type }}
</ui-select-match>
<ui-select-choices repeat="uitypelocal.type as uitypelocal in flat.typelocal1 track by $index | filter: $select.search">
<div ng-bind-html="uitypelocal.type | highlight: $select.search"></div>
</ui-select-choices>
</ui-select>
<button class="btn btn-danger">Send</button>
</form>
</div>
In form i have some inputs and selects. Now, i want to have in the select data from another controller. How can i do it?
I have a service:
angular.module('flatService', [])
.factory('Flat', function($http){
var flatFactory = {};
flatFactory.allFlats = function(){
return $http.get('/api/flats');
};
flatFactory.create = function(flatData){
return $http.post('/api/addflat', flatData);
};
return flatFactory;
})
});
If you want to call one controller into another there are five methods available
$rootScope.$emit() and $rootScope.$broadcast()
If Second controller is child ,you can use Parent child communication .
Use Services
Kind of hack - with the help of angular.element()
inject '$controller'
1. $rootScope.$emit() and $rootScope.$broadcast()
Controller and its scope can get destroyed,
but the $rootScope remains across the application, that's why we are taking $rootScope because $rootScope is parent of all scopes .
If you are performing communication from parent to child and even child wants to communicate with its siblings, you can use $broadcast
If you are performing communication from child to parent ,no siblings invovled then you can use $rootScope.$emit
HTML
<body ng-app="myApp">
<div ng-controller="ParentCtrl" class="ng-scope">
// ParentCtrl
<div ng-controller="Sibling1" class="ng-scope">
// Sibling first controller
</div>
<div ng-controller="Sibling2" class="ng-scope">
// Sibling Second controller
<div ng-controller="Child" class="ng-scope">
// Child controller
</div>
</div>
</div>
</body>
Angularjs Code
var app = angular.module('myApp',[]);//We will use it throughout the example
app.controller('Child', function($rootScope) {
$rootScope.$emit('childEmit', 'Child calling parent');
$rootScope.$broadcast('siblingAndParent');
});
app.controller('Sibling1', function($rootScope) {
$rootScope.$on('childEmit', function(event, data) {
console.log(data + ' Inside Sibling one');
});
$rootScope.$on('siblingAndParent', function(event, data) {
console.log('broadcast from child in parent');
});
});
app.controller('Sibling2', function($rootScope) {
$rootScope.$on('childEmit', function(event, data) {
console.log(data + ' Inside Sibling two');
});
$rootScope.$on('siblingAndParent', function(event, data) {
console.log('broadcast from child in parent');
});
});
app.controller('ParentCtrl', function($rootScope) {
$rootScope.$on('childEmit', function(event, data) {
console.log(data + ' Inside parent controller');
});
$rootScope.$on('siblingAndParent', function(event, data) {
console.log('broadcast from child in parent');
});
});
In above code console of $emit 'childEmit' will not call inside child siblings and It will call inside only parent, where $broadcast get called inside siblings and parent as well.This is the place where performance come into a action.$emit is preferrable, if you are using child to parent communication because it skips some dirty checks.
2. If Second controller is child, you can use Child Parent communication
Its one of the best method, If you want to do child parent communication where child wants to communicate with immediate parent then it would not need any kind $broadcast or $emit but if you want to do communication from parent to child then you have to use either service or $broadcast
For example HTML:-
<div ng-controller="ParentCtrl">
<div ng-controller="ChildCtrl">
</div>
</div>
Angularjs
app.controller('ParentCtrl', function($scope) {
$scope.value='Its parent';
});
app.controller('ChildCtrl', function($scope) {
console.log($scope.value);
});
Whenever you are using child to parent communication, Angularjs will search for a variable inside child, If it is not present inside then it will choose to see the values inside parent controller.
3.Use Services
AngularJS supports the concepts of "Seperation of Concerns" using services architecture. Services are javascript functions and are responsible to do a specific tasks only.This makes them an individual entity which is maintainable and testable.Services used to inject using Dependency Injection mecahnism of Angularjs.
Angularjs code:
app.service('communicate',function(){
this.communicateValue='Hello';
});
app.controller('ParentCtrl',function(communicate){//Dependency Injection
console.log(communicate.communicateValue+" Parent World");
});
app.controller('ChildCtrl',function(communicate){//Dependency Injection
console.log(communicate.communicateValue+" Child World");
});
It will give output Hello Child World and Hello Parent World . According to Angular docs of services Singletons – Each component dependent on a service gets a reference to the single instance generated by the service factory.
4.Kind of hack - with the help of angular.element()
This method gets scope() from the element by its Id / unique class.angular.element() method returns element and scope() gives $scope variable of another variable using $scope variable of one controller inside another is not a good practice.
HTML:-
<div id='parent' ng-controller='ParentCtrl'>{{varParent}}
<span ng-click='getValueFromChild()'>Click to get ValueFormChild</span>
<div id='child' ng-controller='childCtrl'>{{varChild}}
<span ng-click='getValueFromParent()'>Click to get ValueFormParent </span>
</div>
</div>
Angularjs:-
app.controller('ParentCtrl',function($scope){
$scope.varParent="Hello Parent";
$scope.getValueFromChild=function(){
var childScope=angular.element('#child').scope();
console.log(childScope.varChild);
}
});
app.controller('CarentCtrl',function($scope){
$scope.varChild="Hello Child";
$scope.getValueFromParent=function(){
var parentScope=angular.element('#parent').scope();
console.log(parentScope.varParent);
}
});
In above code controllers are showing their own value on Html and when you will click on text you will get values in console accordingly.If you click on parent controllers span, browser will console value of child and viceversa.
5.inject '$controller'
You can inject '$controller' service in your parent controller(MessageCtrl) and then instantiate/inject the child controller(DateCtrl) using:
$scope.childController = $controller('childController', { $scope: $scope.$new() });
Now you can access data from your child controller by calling its methods as it is a service.
Let me know if any issue.
The view's scope is determine by it's controller. However, angular allows nested view scope inheritance, meaning that a view nested inside another view has access to the parent view's scope.. for example:
<div ng-controller="OuterCtrl">
<div ng-controller="InnerCtrl">
...
</div>
<div>
the inner div would have access to the InnerCtrl's scope as well as the OuterCtrl's scope, but the outer div would only have access to the OuterCtrl's scope.
If you need data shared between non-related controllers (those that are not related by nested views), then the data should be provided by a service that can be injected into the controllers like:
app.factory('service', function() {
...
return {
data: someData
};
});
app.controller('ctrl1', [... function(..., service) {
$scope.data = service.data;
}]);
app.controller('ctrl2', [... function(..., service) {
$scope.data = service.data;
}]);
use $broadcase or $emit event like:
app.controller('ctrl1', ['$scope', '$rootScope', function($scope, $rootScope) {
$rootScope.$on("CustomEvent", function($event, data){
alert(data);
})
}]);
app.controller('ctrl2', ['$scope', function($scope) {
$scope.$emit("CustomEvent", "SecondControllerValue");
}]);
Related
This is my first post/question here on StackOverflow so if you see any improvement I can made - please give me an advice :).
Now let me dive into the issue.
For the sake of simplicity I removed any irrelevant portions of code and presented only necessary files.
//app.modules.js
if (typeof window.app == "undefined") {
window.app = angular.module("AppModule", []);
}
//app.services.js
window.app
.service("settingsOverlaySvc", function($rootScope) {
this.broadcastToggle = function() {
$rootScope.$broadcast("toggle-conf");
};
});
//settings-ctrl.js
window.app.controller("SettingsController", ["$scope", "$window", "$sce", "settingsOverlaySvc",
function($scope, $window, $sce, settingsOverlaySvc) {
$scope.visible = false;
$scope.open = false;
$scope.toggleSettings = function() {
$scope.open = !$scope.open;
};
$scope.broadcastToggle = function() {
settingsOverlaySvc.broadcastToggle();
};
$scope.$on("toggle-conf", function() {
console.log("toggle-conf received");
$scope.visible = !$scope.visible;
});
}
]);
angular.bootstrap($("div[ng-controller='SettingsController']").parent(":not(.ng-scope)"), ["AppModule"]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- Appended by JS - control1.html -->
<div>
<div ng-controller="SettingsController" ng-init="visible=true">
<span class="glyphicon glyphicon-cog" ng-class="{'settings-open': open}" ng-click="broadcastToggle();toggleSettings()">COG</span>
</div>
</div>
<!-- Appended by JS - control2.html-->
<div>
<div ng-controller="SettingsController" ng-cloak>
<div ng-if="visible">
<span class="glyphicon glyphicon-cog" ng-class="{'settings-open': open}" ng-click="toggleSettings()">COG</span>
<div ng-if="open">
<div class="divControl_2">Content_2</div>
</div>
</div>
</div>
</div>
The above snippet works as I expected - the $broadcast is called, all controllers receive the message and reacts. In my application the broadcast is received only by controller sending it. I think the problem is caused by dynamic HTML insertions.
The requirement is to render controls dynamically on page load. I'm using the following approach to generate content.
AddWidgets: function () {
var controlContainer1 = $("<section>", {
class: "sidebar-section",
id: "ctrl1-container"
});
var controlContainer2 = $("<section>", {
class: "sidebar-section",
id: "ctrl2-container"
});
$("aside.sidebar").append(controlContainer1);
$("aside.sidebar").append(controlContainer2);
$("#ctrl1-container").load("..\\assets\\ctrls\\control1.html");
$("#ctrl2-container").load("..\\assets\\ctrls\\control2.html");
}
I'm using the same controller because it shares the same logic for all controls.
I've read a lot materials about $broadcast and $emit functionality, guides on creating controllers, defining modules and services (that one gives me idea about creating service with $rootScope injected).
Now I'm thinking that generating angular content outside angular framework (AddWidgets function) can cause the problem (#stackoverflow/a/15676135/6710729).
What raised my spider sense alarm is when I've checked the JSFiddle example for the similar situation (http://jsfiddle.net/XqDxG/2342/) - no parent-child relation of controllers. When I peek at angulat scope of the controllers I can see that $$nextSibling and $$prevSibling properties are filled. In my case these are nulled [here].
Can you give me some guidelines how can I resolve my issue? I'm fairly new to AngularJS and learning as I'm developing the application.
inside view I'm conditionally rendering html using ng-include and ng-if
<div ng-controller="myController">
<div ng-if="myProperty == 1">
<div ng-include="'view1.html'"></div>
</div>
<div ng-if="myProperty == 2">
<div ng-include="'view2.html'"></div>
</div>
</div>
and inside controller I have $scope.myProperty which receive value inside controller using $scope injection from other js object. On this controller I have also callback function which updates $scope.myProperty every x seconds.
app.controller('myController', function ($scope) {
...
$scope.myProperty = 0; //init value
function callback() {
$scope.$apply(); // force update view
// correctly write myProperty value on every data change
console.log($scope.myProperty);
}
var otherJsObject= new myObject($scope, callback);
otherJsObject.work();
...
}
callback function correctly change myProperty value but it doesn't update inside view every time.
update:
$scope.bindUIProp = { a: $scope.myProperty};
function callback() {
$scope.$apply();
$scope.bindUIProp.a = $scope.myProperty;
console.log('Callback ' + $scope.myProperty);
console.log('drawstage ' + $scope.bindUIProp.a);
}
var otherJsObject= new myObject($scope, callback);
otherJsObject.work();
and inside view I used object property
<div ng-controller="myController">
<div ng-if="bindUIProp.a == 1">
<div ng-include="'view1.html'"></div>
</div>
<div ng-if="bindUIProp.a == 2">
<div ng-include="'view2.html'"></div>
</div>
</div>
this approach work every time when page is refreshed, parial view is not updated from view1 to view2 when scope.bindUIProp.a is changed to 2.
Instead of writing to property at root level. Write one level below.
Instead of $scope.myProperty,
use $scope.mp.myProperty
Both ng-if and ng-include create child scopes.
You are having problems due to using a primitive in your main scope. Primitives don't have inheritance so the binding is getting broken in the nested child scopes.
change it to an object:
$scope.myProperty = { someProp: 0};
Personally I rarely use ng-include because of the child scope it creates. I prefer having my own directive if all I want is to include a template.
I do this in Ionic Framework. It started in the home.html template with eventmenu.home state. The $scope.aaa variable is 10 and it shows that value correctly. However, in a child controller TestCtrl of that template, I cannot change the $scope.aaa value and gets it updated. What's wrong with my method as why aaa did not get increased? Is TestCtrl in a different scope than HomeCtrl although it should be the child of HomeCtrl, no?
CODE:
http://codepen.io/hawkphil/pen/xbmZGm
HTML:
<script id="templates/home.html" type="text/ng-template">
<ion-view view-title="Welcome" >
<ion-content class="padding" >
<p>Swipe to the right to reveal the left menu.</p>
<p>(On desktop click and drag from left to right)</p>
<p>{{ aaa }}</p>
<div ng-controller="TestCtrl">
<a ng-click="clickMe()" href="">Click Me</a>
</div>
</ion-content>
</ion-view>
</script>
JS:
.controller('HomeCtrl', function($scope) {
$scope.aaa = 10;
})
.controller('TestCtrl', function($scope) {
$scope.clickMe = function() {
$scope.aaa++;
}
})
p.s I am using the code from somebody so please ignore other stuffs.
This isn't an issue with Ionic, or with ui-router, or really even with Angular. It's just how javascript works.
You can see the same issues arise in plain Angular using ng-if. It has to do with how javascript inheritance works.
If the child scope doesn't have it's own aaa property it uses it's parent. But once you click the button, you make it's own property, one more than the parent's was. But now they are separate properties, the parent's aaa and the child's aaa.
Example
You can see a pure javascript example here...
Example. Click the Inc Parent as much as you like, both the parent and child displays are incremented. Clicking the Inc Child breaks the connection. Once the connection is broken you can "unhide" the parent value by deleting the child's property by clicking the Unhide button.
code
var obj = function () {}
obj.aaa = 10
obj.inc = function () {
this.aaa += 1;
};
var objB = function () {};
objB.__proto__ = obj; // Make objB a child of obj
objB.inc = function () {
this.aaa += 1;
};
objB.unhide = function () {
delete this.aaa;
};
var update = function () {
document.getElementById("p").textContent = obj.aaa;
document.getElementById("c").textContent = objB.aaa;
};
update();
html
<button onclick="obj.inc(); update();">Inc Parent</button>
<button onclick="objB.inc(); update();">Inc Child</button>
<button onclick="objB.unhide(); update();">Unhide</button>
<p>
Parent's value: <span id="p"></p>
</p>
<p>
Child's value: <span id="c"></p>
</p>
Every controller has its own $scope object. Child controllers can access their parents using $parent:
.controller('TestCtrl', function($scope) {
$scope.clickMe = function() {
$scope.$parent.aaa++;
}
})
codepen
I tried to update my parent scope from the child controller using two solutions but I can't make it work. apply() didn't work
HTML :
<div ng-controller="ControllerA">
<div ng-show="tab_selected == 1">Content1</div>
<div ng-show="tab_selected == 2">Content2</div>
<div ng-controller="ControllerB">
<span ng-click="updateScope()"></span>
</div>
</div>
JS :
app.controller('ControllerA', ['$scope',
function ($scope) {
$scope.tab_selected = 1;
}]);
app.controller('ControllerB', ['$scope',
function ($scope) {
$scope.updateScope = function(){
$scope.tab_selected = 2;
// $scope.apply(function(){ $scope.tab_selected = 2; });
}
}]);
You can't update primitive attributes in the parent object, only object attributes. So you need to use:
$scope.someObject= {};
$scope.someObject.tab_selected= 2;
Controllers have separate scope but there is a few way to communicate
$broadcast the event to parent scope
use service for two-way data binding i.e. http://plnkr.co/edit/Vqk1Fe?p=preview
with scope inheritance i.e. http://plnkr.co/edit/7wcMnJ?p=preview
How can I pass a function down through two directives with isolated scopes using expression binding?
I have two directives in a parent-child relationship:
app.directive('parentDir', function(){
return {
scope:{
parentData:'=',
changeRouteParent:'&'
},
templateUrl:'mydir/parentTemplate.html'
};
});
app.directive('childDir', function(){
return {
scope:{
dirData:'=',
changeRoute:'&'
},
templateUrl: 'mydir/childTemplate.html'
};
});
In parent template:
<div class="parentDirClass">
<a ng-click="changeRouteParent({newFoo:parentData.url})>fooLink</a>
<div child-dir ng-repeat="child in parentData.childData" dir-data="child" change-route="???"></div>
</div>
In child template:
<div class="childDirClass">
<a ng-click="changeRoute=({{newFoo:dirData.url}})">foolink</a>
</div>
Trying to use the directives together:
app.controller('exampleController', function($scope, $state){
$scope.changeRoute = function(newFoo) {
$state.go(newFoo);
};
$scope.theParentData = pData;
});
//parent data
pdata = {url:'/fooUrl',
childData:[{url:'/whatever1'},{url:'/whatever2'}, {url:'/whatever3'}]};
HTML:
<div ng-controller="exampleController">
<div parent-dir parent-data="pData" change-route-parent="changeRoute(newFoo???)"></div>
</div>
With one level of expression binding the solution seems easy as you can easily identify the variables being passed (changeRoute=({{newFoo:dirData.url}}) in the child directive) but then moving up a level I have no variable to pass. How can I continue to pass dirData.url up the chain into the example controller to call changeRoute ?
After some tinkering I found chaining can be achieved as follows --
When a bound expression is used in a template at the most-child directive:
childBoundExpression({variableFromChild:variableinIsolatedScope})
Then, starting from the parent of the most-child directive:
<div childDirective child-bound-expression="parentBoundExpression({variableToParent:variableFromChild}) ></div>
For the nth parent use the above expression and repeat until you reach the top directive/html. Eventually the name of variableToParent must be the variable that will be passed to the function on your most-parent $scope
The most important bit is that if you are passing the same variable from child to parent variableToParent and variableFromChild must be the same name.