简体   繁体   中英

Sharing data between Angularjs components

I'm trying to refactor an Angularjs 1.4 webapp to use components.
The webapp has a header with a "Search" input box: the main section shows a list of contacts filtered according to the value entered in the Search input text.
I'm trying to use ui-router (to switch from list to details. I've created Header and Index component, defined a service in which I store the Search value, defined a state app.index with "resolve" that calls the service.
The problem is that the resolve is done just once when the state is loaded, while I want to update the filtering for each jeypress.
In my code I've already binded the search model between a parent component and header so I could just create components repeating the header component inside each template and I think it would work but I wonder if there is a more elegant way to do it (signals is not very elegant, don't you think so?) Here is my
App.html

<header-cnt-component search="$ctrl.search"></header-cnt-component>
<ui-view> </ui-view>

App.js

angular
  .module('app')
  .component('app', {
    templateUrl: 'app/containers/App.html',
    controller: App
  });

function App() { 
  this.search = {};
}

HeaderCnt.html

<nav class="navbar navbar-default" role="navigation">
    <div class="collapse navbar-collapse" id="nav-toggle">
        <form class="navbar-form navbar-right" role="search">
            <input type="text" class="form-control" placeholder="Search" ng-model="$ctrl.search.name" ng-keyup="$ctrl.startSearch()">
        </form>
    </div>
</nav>

HeaderCnt.js

angular
  .module('app')
  .component('headerCntComponent', {
    templateUrl: 'app/components/HeaderCnt.html',
    controller: HeaderCnt,
    bindings: {
      search: '='
    }
  });

  /** @ngInject */
function HeaderCnt(contactsService, searchService, $location) {
  this.contactsService = contactsService;
  this.searchService = searchService;
  this.location = $location;
  this.contacts = contactsService.get();
}

HeaderCnt.prototype = {

  startSearch: function () {
    this.searchService.set(this.search);
    this.location.path('/');
  }
};

Index.html

<div class="table-responsive">
    <table class="table table-striped">
        <thead>
            <tr>
                <th>Name</th>
                <th>Email Address</th>
                <th>Phone Number</th>
            </tr>
        </thead>
        <tbody>
            <tr ng-repeat="contact in $ctrl.contacts | filter:$ctrl.search">
                <td>{{contact.name}}</td>
                <td>{{contact.email}}</td>
                <td>{{contact.phone}}</td>
            </tr>
        </tbody>
    </table>
</div>

Index.js

angular
  .module('app')
  .component('indexComponent', {
    templateUrl: 'app/components/Index.html',
    controller: Index,
    bindings: {
      search: '='
    }
  });

/** @ngInject */
function Index(contactsService) {
  this.contactsService = contactsService;
  this.contacts = contactsService.get();
}

Index.prototype = {
};

search.js

function SearchService() {
   var search = {};

   this.get = function () {
     return search;
   };
   this.set = function (searchData) {
     search = searchData;
   };
 }

route.js

angular
  .module('app')
  .config(routesConfig);

/** @ngInject */
function routesConfig($stateProvider, $urlRouterProvider, $locationProvider) {
  $locationProvider.html5Mode(true).hashPrefix('!');
  $urlRouterProvider.otherwise('/');

  $stateProvider
    .state('app', {
      component: 'app'
    })
    .state('app.index', {
      url: '/',
      component: 'indexComponent',
      resolve: {
        search: function (searchService) {
          // var search = {};
          // search.name = 'aus';
          return searchService.get();
          // return search;
        }
      }
    });
}

If I got you correctly this might help. I would add new state for search:

.state('app.index.search', {
      url: '/search/:searchString',
      component: 'indexComponent',
      resolve: {
        search: $stateParams => $stateParams.searchString
        }
      }
    })

if you have binding on search in indexComponent rest should be fairly simple.

/search/some user search

will resolve to your state with search string and you can use if you want to switch to particular searchString state on your controller:

$state.go('app.index.search', searchString)

one more thing, you can use ng-model-options={debounce: 500} to get delay after typing (0.5s) and put watcher on controller for changes on model.

so your html for input:

<input type="text" class="form-control" placeholder="Search" ng-model="$ctrl.search.name" ng-model-options="{debounce: 500}"/>

and in controller:

this.$scope.$watch('$ctrl.search.name', (newVal, oldVal) => {
  newVal && $state.go('app.index.search', newVal');
});

Let me know if it helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM