I am using an Angular 1.5 component to build the below structure:
<parent>
<child active="model.active">
<child active="model.active">
<child active="model.active">
</parent>
My component:
(function() {
'use strict'
angular.module('module').component('child',
{
templateUrl: 'template.html',
bindings: {
active: '<'
},
controllerAs: 'model',
controller: function() {
var model = this;
model.select = function() {
model.deselectAllChildren()(model);
model.active = true;
};
}
});
})();
Parent deselectAllChildren method:
model.deselectAllChildren = function(tabModel) {
model.active = false;
};
When a user selects one of the child components, I want it to be made active and the other children inactive. So the logic makes all children inactive (to clean up previous selections) then makes the selected child active. The parent's model.active property is false by default. This works fine for the first select but subsequent selects don't work as the previously selected children don't deselect as the model.active = false change is not propagating to the child components (because the parent's model.active value is still false - but some of the children model.active equals true. This no doubt is a scope/binding issue; I feel like I need to force push model.active = false down to all children. Any ideas?
How about:
<parent>
<child active="model.active === 0" on-select="model.active=0">
<child active="model.active === 1" on-select="model.active=1">
<child active="model.active === 2" on-select="model.active=2">
</parent>
angular.module('module').component('child',
{
templateUrl: 'template.html',
bindings: {
active: '<',
onSelect: '&'
},
controllerAs: 'model',
controller: function() {
var model = this;
model.select = function() {
model.onSelect();
};
}
});
Related
This question already has an answer here:
How to update child components from the updated list of parents
(1 answer)
Closed 4 years ago.
I have parent component:
(function () {
angular.module('app.module')
.component('parentComponent', {
templateUrl: 'parent.html',
controllerAs: 'vm',
controller: function ($scope) {
this.$onInit = () => {
$scope.parentData = 'test'
}
})
})()
child component
(function () {
angular.module('app.module').component('childComponent', {
templateUrl: 'child.html',
controllerAs: 'vm',
controller: function () {
this.$onInit = () => {
}
}
})
})()
parent.html
<child-component></child-component>
child.html
<p>{{parentData}}</p>
So I want to have access to parentData in my child component for display string 'test' in my child component. How can I do it? I read something about bindings but I don't know how to use it in this example.
Thanks for any suggestions.
Use one-way < binding:
<child-component in-data="$ctrl.parentData"></child-component>
The child component:
app.component("childComponent", {
bindings: {
inData: '<',
},
template: `
<div>{{$ctrl.inData}}</div>
`,
})
The DEMO
angular.module("app",[])
.component("parentComponent", {
template: `
<fieldset>
Inside parent component<br>
parentData={{$ctrl.parentData}}
<child-component in-data="$ctrl.parentData"></child-component>
</fieldset>
`,
controller: function () {
this.$onInit = () => {
this.parentData = 'test'
};
},
})
.component("childComponent",{
bindings: {
inData: '<',
},
template: `
<fieldset>Inside child component<br>
inData={{$ctrl.inData}}
</fieldset>
`,
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<parent-component>
</parent-component>
<body>
For more information, see
AngularJS Developer Guide - Component-based application architecture
AngularJS Comprehensive API Reference - scope
You can access parent's data via the parent controller, for that you can use require in your component declaration:
Here is an example:
app.component('childComponent', {
require: {
parentCtrl: '^parentComponent'
},
controller: function() {
var self = this;
this.$onInit = function() {
self.parentCtrl.anyData;
};
}
});
Take a look at codelord.net - Advanced Angular 1.x: Component Communication with Require
If you don't need the parent controller you can bind data to your child component: refer to #georgeawg answer
Say I have a component that looks like this:
parent.component.js
...
template: `<div>
<child-one value="value" callback="callback"></child-one>
<child-two value="value"></child-two>
</div>`,
controller: function() {
this.callback = function(n) {
this.value = n;
}
}
...
Then the child components look like this:
childOne.component.js
...
bindings: {
value: '<',
callback: '<'
},
template: `<input type="text"
ng-model="$ctrl.value"
ng-change="$ctrl.callback($ctrl.value)"
/>`
...
childTwo.component.js
...
bindings: {
value: '<'
},
template: `<div>{{$ctrl.value}}</div>`
...
(binding technique thanks to krawaller)
I want the value that is set in childOne to go to childTwo. Updating the value in childOne does update the value in the parent but does not pass it down to childTwo.
Note:
You are setting values in the this object. Not directly in the $scope.
Use $scope, instead of this
Modified code:
parent.component.js
...
template: `<div>
<child-one value="value" callback="callback"></child-one>
<child-two value="value"></child-two>
</div>`,
controller: function($scope) {
$scope.callback = function(n) {
$scope.value = n;
console.log($scope.value);
}
}
...
If you want to write a code with this keyword, then use controllerAs syntax.
Refer below code for parent.component.js
...
template: `<div>parent: {{vm.value}} <br/><div/>
<div>
<child-one value="vm.value" callback="vm.callback"></child-one>
<child-two value="vm.value"></child-two>
</div>`,
controller: function() {
const vm = this;
vm.callback = function(n) {
vm.value = n;
console.log(vm.value);
}
},
controllerAs: "vm"
...
Result:
Updating the value in childOne will update the value in the parent as well as childTwo.
I have an angular 1.5 component (i.e. myCarousel: it wraps a bootstrap carousel) that triggers an event and exposes it with a function binding.
A parent container (i.e. myContainer) component binds a function to that event and modifies some inner state when triggered.
Another child-component of the parent container is bound to the container's inner state.
The problem:
When I trigger the event on myCarousel from a button click event, everything works fine and I can see the child-component's binding being updated.
However, when I trigger the event from a carousel slide event (slide.bs.carousel), the binding does not get updated.
Here's a plunker to demonstrate:
https://plnkr.co/edit/AHxaX8o0sE94Nfir7DVB
Can anyone explain why this is happening and how to solve this?
Some relevant code:
mainApp.component("myCarousel", {
templateUrl: "myCarousel.html",
bindings: {
onEventTriggered: "&"
},
controllerAs: "vm",
controller: function() {
let vm = this;
vm.$onInit = function() {
console.log("init!");
$("#theCarousel").carousel();
$("#theCarousel").on("slide.bs.carousel", (event) => {
console.log("sliding " + event.direction);
vm.onEventTriggered();
});
};
}
});
mainApp.component("myContainer", {
templateUrl: "myContainer.html",
controllerAs: "vm",
controller: function() {
let vm = this;
vm.counter = 0;
vm.triggerEvent = function() {
console.log("event!");
vm.counter++;
}
}
});
mainApp.component("childComponent", {
template: "<div>Event {{vm.attribute}}</div>",
controllerAs: "vm",
bindings: {
attribute: "<"
}
});
One way to do this is :
controller: function($scope) {
let vm = this;
vm.$onInit = function() {
console.log("init!");
$("#theCarousel").carousel();
$("#theCarousel").on("slide.bs.carousel", (event) => {
vm.onEventTriggered();
console.log("sliding " + event.direction);
$scope.$apply();
});
};
}
using $scope.$apply()
updated plunkr : https://plnkr.co/edit/8yZOyuQfofdoYz00tFBk?p=preview
You need to use $scope.$apply(), because $("#theCarousel").on("slide.bs.carousel", ... is jquery code, and you need to notify angular about it using $scope.$apply()
i want to pass a pageTitle data from ui-router based on a $stateParams and then update the title of the page using a directive
ui-router example:
.state('index.something.detail', {
url: '/{type}/{else}',
views: {
'': {
templateUrl: 'tempalte.html',
controller: function($scope, $stateParams) {
if ($stateParams.type == "param") {
$scope.title = "param";
}
}
}
},
data: {
pageTitle: 'Something {{title}}', //NOT WORKING
},
ncyBreadcrumb: {
label: 'Sometgin {{title}}', //IT WORKS
parent: 'index.something'
}
})
directive example:
function pageTitle($rootScope, $timeout) {
return {
link: function(scope, element) {
var listener = function(event, toState, toParams, fromState, fromParams) {
// Default title - load on Dashboard 1
var title = 'APP';
// Create your own title pattern
if (toState.data && toState.data.pageTitle) title = 'APP | ' + toState.data.pageTitle;
$timeout(function() {
element.text(title);
});
};
$rootScope.$on('$stateChangeStart', listener);
}
}
};
angular-breadcrumb pass the data ok
Something param
but in the pageTitle directive i get
Something {{title}}
how can i pass the $scope of the ui-router state?
Thanks!
We cannot pass this via data setting. The reason is, that the string param in data {} is just a string, not a view template. We would need to compile it and pass a scope.
But there is a better solution. Check this working example
Let's have index.html like this:
<H1><ui-view name="title" /></H1>
<ul>
<li><a ui-sref="parent">parent</a>
<li><a ui-sref="parent.child">parent.child</a>
</ul>
<div ui-view=""></div>
And our state can now inject stuff into two locations:
.state('state name', {
...
views : {
"" : {
// default view
}
"title" : {
template : "parent is setting title inside of index.html"
}
}
})
Check this here in action
Also, observe the UI-Router default sample application (doing the same trick) here:
http://angular-ui.github.io/ui-router/sample/#/
The most important part of the code is Contact state def, there is a small snippet:
// You can have unlimited children within a state. Here is a second child
// state within the 'contacts' parent state.
.state('contacts.detail', {
...
views: {
// So this one is targeting the unnamed view within the parent state's template.
'': {
...
},
// This one is targeting the ui-view="hint" within the unnamed root, aka index.html.
// This shows off how you could populate *any* view within *any* ancestor state.
'hint#': {
template: 'This is contacts.detail populating the "hint" ui-view'
},
// This one is targeting the ui-view="menuTip" within the parent state's template.
'menuTip': {
I'm trying to inject default data into a ui-view template.
$stateProvider.state('foo', {
template: '<div ui-view=bar></div>',
abstract: true,
views: {
bar: {
template: 'test'
}
}
});
$stateProvider.state('foo.whiz', {
// ...
});
This example doesn't work, but I hope it's enough to show you what I mean. So what I'm basically trying to do is, when I enter the state foo.whiz, which has a parent of foo, I'd like foo to inject default data into the bar ui-view. Currently it only seems you can populate named views via the child state (bar#foo)? How can I, essentially, initialize data via the route object?
I would say, there are 4 ways (maybe more) how to pass some stuff from child to parent. There is a working plunker.
Let's have these anchors/targets in the index.html:
<div ui-view="title"></div>
<div ui-view="scopeInheritance"></div>
<div ui-view="factory"></div>
<div ui-view="content"></div>
And this would be the common def of parent state 'foo'
.state('foo', {
url: '/foo',
views: {
'title' : { template: '<h3>foo - parent state title</h3>',},
'scopeInheritance' :
{
template: '<h3 ui-view="inheritance">{{parentModel.greeting}}</h3>',
controller: 'parentCtrl',
},
'factory' : { template: '<h3>{{dataHolder.message}}</h3>',},
'content' : {
template: '<div >Content of foo (parent) <ui-view /></div>',
},
},
})
I. child view replaces parent view completely
In this case, we will define different content for the view 'title' in our child state:
.state('foo.whiz', {
views: {
'title#' : {
template: '<h3>{{resolved}} - child state title</h3>',
controller : 'titleCtrl',
resolve : { resolvedData : function() { return "whiz"; }, },
},
...
So, we are using Absolute Names 'title#' to place our view into root template.
II. scope inheritance, child can fill properties of parents $scope.parentModel reference
.state('foo.whiz', {
views: {
'inheritance' :
{
controller: 'childCtrl',
},
...
Here we declare some model in parent $scope, and due to Scope Inheritance by View Hierarchy Only we can change these values in a child:
// parent declares model
.controller('parentCtrl', function($scope){
$scope.parentModel = { greeting : "Parent greets"};
...
// child changes value and does clean up
.controller('childCtrl', function($scope){
var remember = $scope.parentModel.greeting;
$scope.parentModel.greeting = "Child greets"
$scope.$on("$destroy", function (){$scope.parentModel.greeting = remember;});
...
III. factory as data holder
Simply let's inject some singleton, in angular world service/factory, as a holder of data into $rootScope
// singleton
.factory('dataHolder', function(){
return {};
});
// available everywhere
app.run(
['$rootScope', 'dataHolder',
function($rootScope, dataHolder) {
$rootScope.dataHolder = dataHolder;
...
// child can assign these, while parent will render them
.controller('childCtrl', function($scope){
$scope.dataHolder.message = "Child sent message"
Check that all here
IV. child view is nested in parent:
the most typical with UI-Router
// the anchor is inside of the parents view
.state('foo', {
views: {
'content' : { // the anchor for child
template: '<div >Content of foo (parent) <ui-view /></div>',
},
...
// child inject directily into parent
.state('foo.whiz', {
views: {
'' : {
template: '<h4>Content of whiz (Child)</h4>',
},
...
These are the basic ways how to pass data/view/messages with UI-Router (skipping the eventing system of angular at all)