1

Here is my situation:

I have a component, the view of which renders a "save" and "cancel" button. The buttons are related to a single input field which a user clicks "edit" to edit the value (or cancel the edit).

The original implementation had the three buttons in its view as shown here:

 <div class="input-edit input-no-border" ng-show="!$ctrl.editingTitle && !$ctrl.isSaving">
                    <input type="text" ng-value="$ctrl.knowledgeHubSection.title" class="field" readonly>
                    <button type="button"
                            class="button"
                            ng-click="$ctrl.editingTitle = !$ctrl.editingTitle">Edit
                    </button>
                </div>
                <div class="input-edit" ng-show="$ctrl.editingTitle">
                    <div class="field open">
                        <input type="text" ng-model="$ctrl.knowledgeHubSection.title" maxlength="20">
                    </div>
                    <div class="save-cancel-buttons">
                        <button class="button save-button"
                                ng-click="$ctrl.updateSection()"
                                ng-disabled="$ctrl.isSaving">
                            {[{ $ctrl.isSaving ? $ctrl.submitButtonLabels.submit.saving : $ctrl.submitButtonLabels.submit.idle }]}
                        </button>
                        <button type="button"
                                class="button cancel-button"
                                ng-click="$ctrl.reset()"
                                ng-disabled="$ctrl.isSaving">
                            Cancel
                        </button>
                    </div>
                </div>

Our application uses the save and cancel buttons in many different views. As such, I want to move that code out views where it used to its own component (we are on AngularJS 1.5) so that I can add the buttons with a component DOM element.

I have done this via the following two files that are accessed by the tag:

<ui-save-cancel-component></ui-save-cancel-component>

which accesses the following view and component:

angular.module('app.admin.ui')
.component(
    'uiSaveCancelComponent', {
        templateUrl: function(UiTemplate) {
            return UiTemplate.EDIT_SAVE_CANCEL;
        },
        bindings: {
            editing: '@'
        },
        require: {
            test: '^knowledgeHubComponent'
        },
        // transclude: true,
        controller: function() {
            'use strict';

            var $ctrl = this;

            $ctrl.submitButtonLabels = {
                submit: {
                    saving: 'Saving',
                    idle: 'Save'
                }
            };

            $ctrl.reset = function() {
                console.log('reset');
                $ctrl.editing = false;
            };


        }
    }
);

and

<div class="save-cancel-buttons">
   <button class="button save-button"
        ng-click="$ctrl.test.updateSection()"
        ng-disabled="$ctrl.test.isSaving">
    {[{ $ctrl.test.isSaving ? $ctrl.submitButtonLabels.submit.saving : $ctrl.submitButtonLabels.submit.idle }]}
  </button>
  <button type="button"
        class="button cancel-button"
        ng-click="$ctrl.reset()"
        ng-disabled="$ctrl.test.isSaving">
    Cancel
  </button>
</div>

The first issue I am having with this solution is that the parent component's name is hardcoded in the component (test: '^knowledgeBaseComponent'). As I want to use this component with other components, how do I make the parent component's name a variable in the require of the child?

I thought maybe I could pass the parent's name as an attribute: <ui-save-cancel-component parent="knowledgeHubComponent"></ui-save-cancel-component> and in the child component as:

bindings: { parent: '@' },
require: { parent: parent }

This did not work.

So the question is how do I dynamically 'pass' the parent component to the child component so that I can use the child throughout the application?

1 Answer 1

1

You can add bindings for save/cancel-callbacks on your button component, so it'll look somewhat like this (I've stripped it down to bare minimum to emphasize my changes):

angular.module('app.admin.ui').component(
    'uiSaveCancelComponent', {
        template: [
            '<div>',
             '<button ng-click="$ctrl.onSave()">Save</button>',
             '<button ng-click="$ctrl.onCancel()">Cancel</button>',
            '</div>'
            ].join(''),
        bindings: {
          onSave: '&',
          onCancel: '&'
        }
    }
);

Then in your parent-component, you can pass on the callbacks like this:

<ui-save-cancel-component 
    on-save="$ctrl.updateSection()" 
    on-cancel="$ctrl.reset()"></ui-save-cancel-component>

Here's a plunker where you can see it in action

Only passing what is necessary from the parent component, will make your button component a lot more reusable, since it doesn't have to know anything about the components that use it.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks so much. Your recommendation inspired further decoupling in my code. Again, thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.