Reputation: 397
I'm studying the sample app of UI-Router and got the question.
In sample-app-angularjs/app/contacts/editContact.component.js line71~74 it has:
export const editContact = {
bindings: { pristineContact: '<' },
controller: EditContactController,
In sample-app-angularjs/app/contacts/contacts.states.js line29~38 it has:
export const viewContactState = {
name: 'contacts.contact',
url: '/:contactId',
resolve: {
// Resolve the contact, based on the contactId parameter value.
// The resolved contact is provided to the contactComponent's contact binding
contact: ['Contacts', '$transition$', (Contacts, $transition$) => Contacts.get($transition$.params().contactId)]
},
component: 'contactView'
};
And In sample-app-angularjs/app/contacts/contacts.states.js line49~61 it has:
export const editContactState = {
name: 'contacts.contact.edit',
url: '/edit',
views: {
// Relatively target the grand-parent-state's $default (unnamed) ui-view
// This could also have been written using ui-view@state addressing: $default@contacts
// Or, this could also have been written using absolute ui-view addressing: !$default.$default.$default
'^.^.$default': {
bindings: { pristineContact: "contact" },
component: 'editContact'
}
}
};
My questions are on the parameter pristineContact:
How it is been passed from state contacts.contact to contacts.contact.edit and then to component editContact? I understand the resolve contact in state contacts.contact is directly available in it's child state contacts.contact.edit, but I cannot find any knowledge-base document related to the clause 'bindings: { pristineContact: "contact" }' (line57) of contacts.states.js. I GUESS it passes the resolve down to it's component(s) so it's component(s) can use "bindings{...}" to input it.
I did not see in the template of component editContact it is used/referenced, but $ctrl.contact is used/referenced instead. -- I cannot understand why the name "pristineContact" is there at all.
Upvotes: 1
Views: 101
Reputation: 8216
In the sample app, the editContact
component uses dirty checking to determine if a contact has been edited or not. Dirty checking is used to prompt the user if they want to discard their changes if they switch to a different URL.
contact
resolve?As you stated, the contact
resolve is available in the nested state. However, the editContact
component doesn't have a contact
binding. Instead, it has a pristineComponent
binding.
In order to map the contact
resolve to the pristineContact
component binding, a bindings
mapping is added to the state.
bindings: { pristineContact: "contact" },
This supplies pristineContact
component binding with the data from the contact
resolve.
This guide describes resolve bindings: https://ui-router.github.io/guide/ng1/route-to-component#resolve-bindings
... and this API doc has even more of the gory details: https://ui-router.github.io/ng1/docs/latest/interfaces/ng1.ng1viewdeclaration.html#bindings
$ctrl.contact
and where is pristineContact
in the template?The editContact
component's dirty checking creates a copy of the original (pristine) contact in $onInit()
. Any changes in the form are applied to the copy ($ctrl.contact
):
$onInit() {
// Make an editable copy of the pristineContact
this.contact = angular.copy(this.pristineContact);
}
When the user clicks save, the editable copy is saved and then the parent state is activated, with reload: true
to reload the changed data.
/** Save the contact, then go to the grandparent state ('contacts') */
save(contact) {
this.Contacts.save(contact)
.then(() => this.canExit = true)
.then(() => this.$state.go("^", null, { reload: true }));
}
The pristineContact
is only used to make the initial copy and then to perform the dirty checking:
uiCanExit() {
if (this.canExit || angular.equals(this.contact, this.pristineContact)) {
return true;
}
let message = 'You have unsaved changes to this contact.';
let question = 'Navigate away and lose changes?';
return this.DialogService.confirm(message, question);
}
Upvotes: 1