Why do I need $parent to enable the function in ng-click when using ion-scroll?
Asked Answered
W

2

3

I am using the following versions:

  1. Ionic, v1.0.0-beta.14
  2. AngularJS v1.3.6

Route configuration:

myApp.config(function ($stateProvider) {
    $stateProvider
        .state('manager_add', {
            url: '/managers/add',
            templateUrl: 'app/components/mdl.role_members/views/add.html',
            controller: 'ManagerAddController'
        });
});

Controller configuration:

myApp.controller('ManagerAddController', function ($scope, $state, $ionicLoading, $filter, ContactService, ManagersService, RoleRequest, RoleRequestsSentService, ToastrService) {

    $scope.role_request = RoleRequest.new();
    $scope.showContactSearch = false;

    $scope.managers = ManagersService.collection();
    $scope.$watchCollection("managers", function( newManagers, oldManagers ) {
        if(newManagers === oldManagers){ return; }
        $scope.managers = newManagers;
        $scope.contactsToBeInvited = getNotInvitedContacts();
    });

    $scope.contacts = ContactService.collection();
    $scope.$watchCollection("contacts", function( newContacts, oldContacts ) {
        if(newContacts === oldContacts){ return; }
        $scope.contacts = newContacts;
        $scope.contactsToBeInvited = getNotInvitedContacts();
    });

    $scope.contactsToBeInvited = getNotInvitedContacts();
    function getNotInvitedContacts() {
        var notinvited = [];
        angular.forEach($scope.contacts, function(contact) {
            if(angular.isObject($scope.managers)) {
                var results = $filter('filter')($scope.managers, {member_id: Number(contact.contact_id)}, true);
                if (results.length == 0) {
                    this.push(contact);
                }
            } else {
                this.push(contact);
            }
        }, notinvited);

        return notinvited;
    }

    $scope.search_contact = "";
    $scope.search = function(contact) {
        if($scope.search_contact === "" || $scope.search_contact.length === 0) {
            return true;
        }

        $scope.showContactSearch = true;
        var found = false;
        if(contact.display_name) {
            found = (contact.display_name.toLowerCase().indexOf($scope.search_contact.toLowerCase()) > -1);
            if(found) { return found; }
        }
        if(contact.contact.getFullName()) {
            found = (contact.contact.getFullName().toLowerCase().indexOf($scope.search_contact.toLowerCase()) > -1);
            if(found) { return found; }
        }
        if(contact.contact.email) {
            found = (contact.contact.email.toLowerCase().indexOf($scope.search_contact.toLowerCase()) > -1);
            if(found) { return found; }
        }

        return found;
    }

    $scope.selectContact = function(contact) {
        $scope.search_contact = contact.contact.getFullName();

        // TODO: Make dynamic role
        $scope.role_request.role_id = 4;
        $scope.role_request.email = contact.contact.email;
    };

    $scope.addRoleMember = function(valid) {
        if($scope.role_request.email === "") { return; }
        if(!valid) { return; }

        $ionicLoading.show({
            template: 'Please wait...'
        });

        RoleRequestsSentService.add($scope.role_request).then(function(roleJsonResponse){
            ToastrService.toastrSuccess('Request send', 'We have send an invite to '+ $scope.search_contact +'.');
            $ionicLoading.hide();
            $state.go('managers');
        });
    }
});

View configuration:

<ion-view view-title="ManagerAdd" >

    <ion-content class="has-header scroll="true">
        <div class="content">

            <div class="list">
                <div class="item item-border">
                    <p>Some text</p>
                </div>
            </div>


            <form name="managerForm">
                <div class="list">

                    <div class="item item-divider">
                        Some text
                    </div>

                    <div class="item item-border">
                        <form name="fillForm">
                            <div class="form-group">
                                <label class="item item-input item-stacked-label item-textarea">
                                    <span class="input-label border-none">Personal message: <span class="text-red required">*</span></span>
                                    <textarea name="message" ng-model="role_member.message" required></textarea>
                                </label>
                                <p ng-show="managerForm.message.$dirty && managerForm.message.$error.required"
                                   class="error-message">Message required!</p>
                            </div>

                            <div class="form-group">
                                <label class="item item-input">
                                    <span class="input-label">Search on name <span class="text-red required">*</span></span>
                                    <input type="text" name="search_contact" ng-model="$parent.search_contact">
                                </label>

                                <div class="searchResultBox" ng-show="showContactSearch">
                                    <ion-scroll direction="y" class="scrollArea">
                                        <div class="list">

                                            <a class="item item-border item-avatar pointer" ng-repeat="contact in $parent.filteredContacts = (contactsToBeInvited | filter:search:false)" ng-click="$parent.selectContact(contact)">
                                                <img src="{{ contact.getImage('thumbnail') }}">
                                                <h2>{{contact.getIconName()}}</h2>
                                                <p>City: {{contact.contact.city}}</p>
                                            </a>

                                        </div>
                                    </ion-scroll>

                                    <div class="notFound pointer" ng-hide="filteredContacts.length">
                                        <h3>Nobody found</h3>
                                        <p>You can only search through existing contacts</p>
                                    </div>

                                </div>
                            </div>

                        </form>
                    </div>
                </div>


                <div class="form-actions">
                    <button type="submit" class="button button-block regie-button" ng-click="addRoleMember(registerForm.$valid)">
                        Sent request
                    </button>
                </div>
            </form>

            <p class="text-red" style="text-align:center; font-size:14px; font-weight: 400;">* required</p>

        </div>
    </ion-content>

</ion-view>

As you can see in the view I need to use $parent to the following fields to get it to work:

  • ng-model="$parent.search_contact"
  • ng-repeat="contact in $parent.filteredContacts = (contactsToBeInvited | filter:search:false)"
  • ng-click="$parent.selectContact(contact)"

I really don't understand why this is necessary because the complete view is using the same controller. Does anyone have an idea?

Wagstaff answered 12/5, 2015 at 10:17 Comment(0)
S
2

The problem is the inheritance. Between your controller's scope and those fields, there are several new scopes (ion-content, ion-scroll and probably others).

So for example the ng-model="search_content". When you write in there, it is going to create a search_content variable inside the ion-content scope (or an intermediary scope if there is any that I didn't see). Since search_content is being created inside ion-content, your controller won't see it.

If you do $parent.search_content it will create it in the parent content (AKA the controller's scope).

You shouldn't do that, what $parent is for you today, tomorrow it can point to anything else (because you could add a new scope in between without knowing it so $parent will then point to the ion-content.

So instead of doing that, you need to use objects and no primitives, for example:

ng-model="form.search_contact"

Thank to that, it will look for the form object through the prototype chain until it finds it on the controller's scope and use it (just what you need).

Read this which is hundred times better than my explanation.

Sandal answered 12/5, 2015 at 10:48 Comment(1)
Thank you this solved my issue on this page. I thought I could solve another issue on a different view and controller with the same solution. Because there the child scope also was different from the parent. But I can't get it to work, see next: #30191124Wagstaff
C
3

This is a very typical angular scoping issue. Ion-view creates a new child scope and uses prototypical inheritance: https://github.com/angular/angular.js/wiki/Understanding-Scopes

Three are several ways to solve:

Cilia answered 12/5, 2015 at 10:54 Comment(0)
S
2

The problem is the inheritance. Between your controller's scope and those fields, there are several new scopes (ion-content, ion-scroll and probably others).

So for example the ng-model="search_content". When you write in there, it is going to create a search_content variable inside the ion-content scope (or an intermediary scope if there is any that I didn't see). Since search_content is being created inside ion-content, your controller won't see it.

If you do $parent.search_content it will create it in the parent content (AKA the controller's scope).

You shouldn't do that, what $parent is for you today, tomorrow it can point to anything else (because you could add a new scope in between without knowing it so $parent will then point to the ion-content.

So instead of doing that, you need to use objects and no primitives, for example:

ng-model="form.search_contact"

Thank to that, it will look for the form object through the prototype chain until it finds it on the controller's scope and use it (just what you need).

Read this which is hundred times better than my explanation.

Sandal answered 12/5, 2015 at 10:48 Comment(1)
Thank you this solved my issue on this page. I thought I could solve another issue on a different view and controller with the same solution. Because there the child scope also was different from the parent. But I can't get it to work, see next: #30191124Wagstaff

© 2022 - 2024 — McMap. All rights reserved.