问题
I have an angular control that I use to display a set of data looking roughly like the following example. Apart from a unique block, it has a number of repeating (but discrete) blocks of the same structure.
{
"person": {
"lastName": "Bettertester",
"firstName": "Fester",
"address": "Out in the woods 17",
"zipCode": "10666",
"place": "Back of beyond"
},
"contact1": {
"firstName": "Jane",
"lastName": "Doe",
"phone": "555-987-654",
"relationship": "Aunt"
},
"contact2": {
"firstName": "Kherumple",
"lastName": "Whaduffle",
"phone": "555-666-000",
"relationship": "Imaginary friend"
},
"contact3": {
"firstName": "Kherumple",
"lastName": "Whaduffle",
"phone": "555-666-000",
"relationship": "Imaginary friend"
}
}
I have written an angularjs component to retrieve and display this structure but want to hand the repeating block off to another component.
angular.module('myModule').component('mainComponent', {
templateUrl : 'person.template.html',
controller : [ '$scope', '$http', function mainController($scope, $http) {
var self = this;
self.data = null;
$http.get(url).then(function(response) {
self.data = response.data;
}, function(response, status) {
console.warn("Error while loading data");
console.warn(" - response=", response);
console.warn(" - status=", status);
self.data = null;
});
} ]
});
The corresponding template:
<div>
<h1>Person information</h1>
<table>
<tr>
<th class="label-column">First & last name</th>
<td class="data">{{$ctrl.data.person.firstName}} {{$ctrl.data.person.lastName}}</td>
</tr>
<tr>
<th class="label-column">Address</th>
<td class="data">{{$ctrl.data.person.address}}</td>
</tr>
<tr>
<th class="label-column">ZIP code & Place</th>
<td class="data">{{$ctrl.data.person.zipCode}} {{$ctrl.data.person.place}}</td>
</tr>
</table>
<contact details="{{$ctrl.data.contact1}}"></contact> <!-- passing the details like this sort of works -->
<contact details="{{$ctrl.data.contact2}}"></contact>
<contact details="$ctrl.data.contact3"></contact> <!-- passing the details like this does not work at all -->
</div>
The controller for the contact details looks as follows:
angular.module('myModule').component('contact', {
templateUrl : 'contact.template.html',
bindings : {
details : '@'
},
controller : [ '$scope', '$http', function contactController($scope, $http) {
var self = this;
console.log("- details=", self.details);
} ]
});
And the corresponding template:
<div>
<h2>Contact</h2>
<table>
<!-- this works -->
<tr>
<th class="label-column">Everything</th>
<td class="data">{{$ctrl.details}}</td>
</tr>
<tr>
<th class="label-column">First & last name</th>
<td class="data">{{$ctrl.details.firstName}} {{$ctrl.details.lastName}}</td>
</tr>
<tr>
<th class="label-column">Phone</th>
<td class="data">{{$ctrl.details.phone}}</td>
</tr>
<tr>
<th class="label-column">Relationship</th>
<td class="data">{{$ctrl.details.relationship}}</td>
</tr>
</table>
<contact details="{{$ctrl.data.contact1}}"></contact>
<contact details="{{$ctrl.data.contact2}}"></contact>
<contact details="{{$ctrl.data.contact3}}"></contact>
<contact details="{{$ctrl.data.contact4}}"></contact>
</div>
My questions is how to correctly pass the contact details that are part of the mainComponent to the contactComponent in a way that lets me access its fields in the corresponding template. If I pass them without the curly braces, the contact component does not seem to get any data at all. If I pass them with the curly braces, the contact component seems to get them in a way, but not as correct json object as I am unable to access fields within the contact block. I'm sure I'm missing something trivial but did not manage to find out where I go wrong.
回答1:
Use one-way
<
binding instead of interpolation@
binding because@
watches for interpolation and if there is no interpolation just passes the raw attribute string as the value itself.<
or=
binding watches for expression change and updates the template with the expression value correspondingly. In this particular case<
is more convenient than=
because we only need to watch input changes for component, we don't need that additional watch to affect parent back by changing the same expression.After applying
<
use attribute values without curly braces safely.Initialise your component logic in
$onInit
controller lifecycle hook, because this hook is called after the bindings have been initialised, which was the exact cause of why yourconsole.log("- details=", this.details);
was giving- details=undefined
incontact
controller.
Code Details:
let app = angular.module('app', []);
app.component('mainComponent', {
template: `
<div>
<h1>Person information</h1>
<table>
<tr>
<th class="label-column">First & last name</th>
<td class="data">{{$ctrl.data.person.firstName}} {{$ctrl.data.person.lastName}}</td>
</tr>
<tr>
<th class="label-column">Address</th>
<td class="data">{{$ctrl.data.person.address}}</td>
</tr>
<tr>
<th class="label-column">ZIP code & Place</th>
<td class="data">{{$ctrl.data.person.zipCode}} {{$ctrl.data.person.place}}</td>
</tr>
</table>
<contact details="$ctrl.data.contact1"></contact> <!-- passing the details like this sort of works -->
<contact details="$ctrl.data.contact2"></contact>
<contact details="$ctrl.data.contact3"></contact> <!-- passing the details like this does not work at all -->
</div>
`,
controller: ['$scope', '$http', function mainController($scope, $http) {
var self = this;
self.$onInit = () => {
self.data = {
"person": {
"lastName": "Bettertester",
"firstName": "Fester",
"address": "Out in the woods 17",
"zipCode": "10666",
"place": "Back of beyond"
},
"contact1": {
"firstName": "Jane",
"lastName": "Doe",
"phone": "555-987-654",
"relationship": "Aunt"
},
"contact2": {
"firstName": "Kherumple",
"lastName": "Whaduffle",
"phone": "555-666-000",
"relationship": "Imaginary friend"
},
"contact3": {
"firstName": "Kherumple",
"lastName": "Whaduffle",
"phone": "555-666-000",
"relationship": "Imaginary friend"
}
};
};
}]
});
app.component('contact', {
template: `
<div>
<h2>Contact</h2>
<table>
<!-- this works -->
<tr>
<th class="label-column">Everything</th>
<td class="data">{{$ctrl.details}}</td>
</tr>
<tr>
<th class="label-column">First & last name</th>
<td class="data">{{$ctrl.details.firstName}} {{$ctrl.details.lastName}}</td>
</tr>
<tr>
<th class="label-column">Phone</th>
<td class="data">{{$ctrl.details.phone}}</td>
</tr>
<tr>
<th class="label-column">Relationship</th>
<td class="data">{{$ctrl.details.relationship}}</td>
</tr>
</table>
</div>
`,
bindings: {
details: '<'
},
controller: ['$scope', '$http', function jassMonitorHandController($scope, $http) {
this.$onInit = () => {
console.log("- details=", this.details);
};
}]
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
<div ng-app="app">
<main-component></main-component>
</div>
回答2:
The problem is the details
binding in your contact
component.
It should be details: '<'
(one-way binding) instead of details: '@'
(one time string binding). When using this type of binding, you won't need the curly braces in your template.
回答3:
I think that you could rewrite your component to receive the array of contacts and use the ng-repeat directive
<div ng-repeat='item in contactList'>
<table>//your stuff</table>
Pass the data with bindings using the equal operator to receive the ng-model
bindings: '='
The last thing that I could suggest is to make a service to retrieve and store the data and inject in the corresponding component
Useful resources
Components angular
Angular style guide
来源:https://stackoverflow.com/questions/44231655/angularjs-how-to-pass-part-of-my-data-from-one-component-to-another