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.
Related
Question: I have two controllers Dog and Cat. If I change dog name then $digest cycle runs and Cat also update here view because of method {{ctrl.getTime()}} usage. This is a big problem for big applications which loads all controllers and don't use ng-if or state. In our current app we have a lot of places where we use methods inside templates and what is event worse we load and initialise all application controllers and don't use states or ng-if, we only use ng-show. I know what better solution is to use properties, but I wonder is it possible tell angular to $digest only DogCtrl scope instead of all app scopes?
HTML
<div ng-app="myApp">
<div ng-controller="DogCtrl as ctrl">
<h3>DogCtrl</h3>
Dog name: <input ng-model="ctrl.name" />{{ctrl.name}}
</div>
<hr />
<div ng-controller="CatCtrl as ctrl">
<h3>CatCtrl</h3>
Cat getTime: {{ctrl.getTime()}} {{ctrl.random}}
</div>
</div>
Javascript
var myApp = angular.module('myApp',[]);
myApp.controller('DogCtrl', DogCtrl);
myApp.controller('CatCtrl', CatCtrl);
function DogCtrl() {
this.name = 'Doggy';
}
function CatCtrl() {
this.getTime = function() {
return new Date().getTime();
}
}
Fiddle: http://jsfiddle.net/nvgofo46/1/
Change your html to this:
<div ng-controller="CatCtrl as ctrl">
<h3>CatCtrl</h3>
Cat getTime: {{ctrl.currentTime}} {{ctrl.random}}
</div>
And change your controller to this:
function CatCtrl() {
this.getTime = function() {
return new Date().getTime();
}
this.currentTime = this.getTime();
}
As a rule of thumb, try not to have functions in your view. Because the functions will execute on every digest.
It's more efficient to bind to a model and update the model accordingly.
I could have remove the function and simply set this.currentTime = new Date().getTime(), but that's not the point of this exercise.
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");
}]);
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.