Передача даних між контролерами в Angular JS?


243

У мене є базовий контролер, який відображає мою продукцію,

App.controller('ProductCtrl',function($scope,$productFactory){
     $productFactory.get().success(function(data){
           $scope.products = data;
     });
});

На мою думку, я відображаю цю продукцію в списку

<ul>
    <li ng-repeat="product as products">
        {{product.name}}
    </li>
</ul

Те, що я намагаюся зробити, це коли хтось натискає на назву продукту, у мене є інший вигляд кошика, на якому додається цей товар.

 <ul class="cart">
      <li>
          //click one added here
      </li>
      <li>
          //click two added here
      </li>
 </ul>

Тому я сумніваюсь у тому, як передати ці натиснуті продукти з першого контролера на другий? Я припускав, що візок теж повинен бути контролером.

Я обробляю події клацання за допомогою директиви. Крім того, я відчуваю, що я повинен використовувати сервіс для досягнення вище функціональності просто не можу зрозуміти, як? оскільки кошик буде заздалегідь визначений, кількість доданих продуктів може становити 5/10 залежно від того, на якій сторінці користувач. Тож я хотів би зберегти це загальне.

Оновлення:

Я створив послугу для трансляції і в другому контролері я її отримую. Тепер запит, як я можу оновити dom? Оскільки мій список для викидання товару є досить жорстким кодом.


Відповіді:


322

З опису, схоже, вам слід скористатися послугою. Перегляньте http://egghead.io/lessons/angularjs-sharing-data-bet between-controllers та службу AngularJS, що передає дані між контролерами, щоб переглянути деякі приклади.

Ви можете визначити свою послугу продуктів (як фабрику) як таку:

app.factory('productService', function() {
  var productList = [];

  var addProduct = function(newObj) {
      productList.push(newObj);
  };

  var getProducts = function(){
      return productList;
  };

  return {
    addProduct: addProduct,
    getProducts: getProducts
  };

});

Залежність вводить послугу обом контролерам.

У вашому ProductControllerвизначте деяку дію, яка додає вибраний об'єкт до масиву:

app.controller('ProductController', function($scope, productService) {
    $scope.callToAddToProductList = function(currObj){
        productService.addProduct(currObj);
    };
});

У своєму розпорядженні CartControllerотримуйте товари від послуги:

app.controller('CartController', function($scope, productService) {
    $scope.products = productService.getProducts();
});

3
Контролер кошика оновлюється один раз, коли викликається getProducts ()? Або кожен раз, коли додається новий продукт?
— кишаніо

1
Якщо ви хочете, щоб оновлення автоматично оновилося, ви можете додати трансляцію з відповіді Максима Шустіна та зателефонувати getProducts () у функцію $ on, щоб оновити область CartCtrl.
— Chalise

2
@krillgar Ви абсолютно праві, що спочатку було не помічено. Я відредагував відповідь, щоб відобразити робоче рішення.
— Chalise

1
@DeveshM Так, роби так, як ти пропонуєш. Ви можете розглянути можливість розширення методів, щоб вони не просто зберігали дані в пам'яті і були більш стійкими (наприклад, збережіть на сервері за допомогою $httpдзвінка).
— Chalise

1
Що станеться, якщо у мене є, наприклад, 2 ProductControllers та 2 візки? В обох буде доданий елемент? Як ви вирішите це в цьому випадку?
— 16-16

67

як передати ці натиснуті продукти з першого контролера на другий?

Після натискання ви можете викликати метод, який викликає трансляцію :

$rootScope.$broadcast('SOME_TAG', 'your value');

а другий контролер буде слухати цей тег, як:

$scope.$on('SOME_TAG', function(response) {
      // ....
})

Оскільки ми не можемо ввести $ range у сервіси, то немає нічого подібного до області $ singleton.

Але ми можемо зробити ін’єкцію $rootScope. Отже, якщо ви зберігаєте цінність у Сервісі, ви можете запускатись $rootScope.$broadcast('SOME_TAG', 'your value');у тій службі. (Дивіться @Charx опис послуг)

app.service('productService',  function($rootScope) {/*....*/}

Перевірте хорошу статтю про $ трансляцію , $ emit


1
Так це працює як шарм, який я використовую на заводі? Є лише одна остання річ, де я застряг, я отримую дані в новий контролер щоразу, коли клацаю продукт. Тепер, як я оновити його в DOM? Тому що у мене вже є скажімо список із 5
— жорстких кодів з рамками,

1
@KT - Ви коли-небудь отримували відповідь? Це здається очевидним питанням, але відповіді я ніде не можу знайти. Я хочу, щоб контролер змінив якесь значення. Коли це значення додатка змінюється, будь-який інший контролер прослуховування повинен оновлювати себе за необхідності. Неможливо знайти його.
— raddevus

2
@daylight відповідь на ваш Q. ґрунтується на структурі контролера у вас: паралельній чи дочірній. $rootScope.$broadcastсповіщає всіх контролерів, які мають паралельну структуру aka того ж рівня.
— Максим Шустін

@MS Дякую за відповідь. Чи можна це зробити за допомогою $ watch? До речі, шахта є паралельними контролерами, і я змусив її працювати, використовуючи ваш метод. Для мене, коли б я намагався додати $ watch (навіть до $ rootScope), метод, пов'язаний з $ watch у 2-му контролері, запустив би лише перший раз (ініціалізація 2-го контролера). Дякую.
— raddevus

1
ми можемо використовувати, $watchякщо значення існує на $rootScopeрівні. Інакше лише трансляція може повідомити інших контролерів. FYI, якщо інший програміст бачить у вашому коді broadcast- логіка зрозуміла, що ви намагаєтесь зробити - "трансляція події". У будь-якому випадку, з мого досвіду. його не рекомендується використовувати rootscopeдля watch. Про "лише перший раз": перегляньте цей приклад: plnkr.co/edit/5zqkj7iYloeHYkzK1Prt?p=preview, у вас є старі та нові значення. будьте впевнені, що кожного разу ви отримуєте нове значення, яке не дорівнює старому
— Максим Шустін,

24

Рішення без створення сервісу, використовуючи $ rootScope:

Для обміну властивостями між контролерами додатків ви можете використовувати Angular $ rootScope. Це ще один варіант обміну даними, розміщуючи їх так, щоб люди знали про них.

Кращим способом обміну деякими функціональними можливостями між контролерами є Послуги, для читання або зміни глобальної властивості можна використовувати $ rootcope.

var app = angular.module('mymodule',[]);
app.controller('Ctrl1', ['$scope','$rootScope',
  function($scope, $rootScope) {
    $rootScope.showBanner = true;
}]);

app.controller('Ctrl2', ['$scope','$rootScope',
  function($scope, $rootScope) {
    $rootScope.showBanner = false;
}]);

Використання $ rootScope у шаблоні (Доступ до властивостей з $ root):

<div ng-controller="Ctrl1">
    <div class="banner" ng-show="$root.showBanner"> </div>
</div>

27
$rootScopeслід уникати якомога більше.
— Шаз

1
@Shaz ви могли б пояснити, чому?
— Мирко

5
@Mirko Ви повинні намагатися уникати глобального стану, наскільки це можливо, тому що кожен може змінити це, зробивши стан програми непередбачуваним.
— Умут Сім

4
$ rootScope має глобальну сферу застосування, тому ми, як і вроджені глобальні змінні, повинні їх обережно використовувати. Якщо будь-який контролер змінить свої значення, він буде змінений для глобальної сфери.
— Саньєєв

1
Обидва послуги та фабрики є однотонними, але ви можете вибрати, чи потрібно вводити будь-яку послугу у свій контролер. Але всі сфери дії дитини випливають з кореневого огляду і слідують за прототипним ланцюгом. Тож якщо ви спробуєте отримати доступ до ресурсу у вашому масштабі та його немає, він буде шукати ланцюжок. Є ймовірність, що ви небажано можете змінити властивість rootcope.
— Саньєєв

16

Зробити це можна двома методами.

  1. Використовуючи $rootscope, але я не рекомендую цього. Це $rootScopeнайвища сфера застосування. У додатку може бути лише той, $rootScopeякий буде поділятися між усіма компонентами програми. Отже, вона діє як глобальна змінна.

  2. Використання послуг. Це можна зробити, поділившись послугою між двома контролерами. Код послуги може виглядати так:

    app.service('shareDataService', function() {
        var myList = [];
    
        var addList = function(newObj) {
            myList.push(newObj);
        }
    
        var getList = function(){
            return myList;
        }
    
        return {
            addList: addList,
            getList: getList
        };
    });

    Ви можете побачити мою загадку тут .


і 3-й варіант, ви можете відправити подію, просто додати її до списку
— DanteTheSmith

ЩО ЯКЩО КОРИСТУВАЧ ОПОВНІТЬ СТОРІНУ? ТАКІ getProducts () ВИ ДАЄТЕ НІЧОГО! (Ви не можете сказати всім користувачам не освіжатись, чи не так?)
— shreedhar bhat

2
@shreedharbhat Питання не запитує про збереження даних під час перезавантаження сторінок.
— Джек Віал

9

Ще простішим способом обміну даними між контролерами є використання вкладених структур даних. Замість, наприклад

$scope.customer = {};

ми можемо використовувати

$scope.data = { customer: {} };

dataМайно буде успадковуватися від батьківської області , тому ми можемо переписати свої поля, зберігаючи доступ з інших контролерів.


1
Контролери успадковують властивості області дії від батьківських контролерів до власної області застосування. Чисто! простота робить її красунею
— Карлос Р Балебона

не можу змусити його працювати. на другому контролері я використовую $ range.data, і це не визначено.
— Барт Калікто

Думає, він говорить про вкладені контролери. Мені теж не дуже корисно.
— Норберт Норбертсон

8
angular.module('testAppControllers', [])
    .controller('ctrlOne', function ($scope) {
        $scope.$broadcast('test');
    })
    .controller('ctrlTwo', function ($scope) {
        $scope.$on('test', function() {
        });
    });

він використовує
— niether

5

ми можемо зберігати дані в сеансі і можемо використовувати їх будь-де у програмі.

$window.sessionStorage.setItem("Mydata",data);

Інше місце

$scope.data = $window.sessionStorage.getItem("Mydata");

4

Тут я побачив відповіді, і він відповідає на питання обміну даними між контролерами, але що робити, якщо я хочу, щоб один контролер повідомив іншого про те, що дані були змінені (без використання трансляції)? ЛЕГКО! Просто використовуючи відомий шаблон відвідувачів:

myApp.service('myService', function() {

    var visitors = [];

    var registerVisitor = function (visitor) {
        visitors.push(visitor);
    }

    var notifyAll = function() {
        for (var index = 0; index < visitors.length; ++index)
            visitors[index].visit();
    }

    var myData = ["some", "list", "of", "data"];

    var setData = function (newData) {
        myData = newData;
        notifyAll();
    }

    var getData = function () {
        return myData;
    }

    return {
        registerVisitor: registerVisitor,
        setData: setData,
        getData: getData
    };
}

myApp.controller('firstController', ['$scope', 'myService',
    function firstController($scope, myService) {

        var setData = function (data) {
            myService.setData(data);
        }

    }
]);

myApp.controller('secondController', ['$scope', 'myService',
    function secondController($scope, myService) {

        myService.registerVisitor(this);

        this.visit = function () {
            $scope.data = myService.getData();
        }

        $scope.data = myService.getData();
    }
]);

Таким простим способом один контролер може оновити інший контролер, щоб деякі дані були оновлені.


2
Невже модель спостереження ( en.wikipedia.org/wiki/Observer_pattern ) не буде тут більш доречною? Шаблон відвідувачів - це щось інше ... en.wikipedia.org/wiki/Visitor_pattern
— Ant Kutschera

3

Я створив фабрику, яка контролює спільну область дії між шаблоном маршруту маршруту, тому ви можете підтримувати спільні дані лише тоді, коли користувачі пересуваються в одному батьківському шляху маршруту.

.controller('CadastroController', ['$scope', 'RouteSharedScope',
    function($scope, routeSharedScope) {
      var customerScope = routeSharedScope.scopeFor('/Customer');
      //var indexScope = routeSharedScope.scopeFor('/');
    }
 ])

Отже, якщо користувач піде в інший шлях маршруту, наприклад, "/ Підтримка", спільні дані для шляху "/ Клієнт" будуть автоматично знищені. Але, якщо замість цього, користувач піде на «дочірні» контури, як-от «/ Клієнт / 1» або «/ Клієнт / список», область застосування не буде знищена.

Ви можете подивитися зразок тут: http://plnkr.co/edit/OL8of9


3

1

using $localStorage

app.controller('ProductController', function($scope, $localStorage) {
    $scope.setSelectedProduct = function(selectedObj){
        $localStorage.selectedObj= selectedObj;
    };
});

app.controller('CartController', function($scope,$localStorage) { 
    $scope.selectedProducts = $localStorage.selectedObj;
    $localStorage.$reset();//to remove
});

2

Після натискання ви можете викликати метод, який викликає трансляцію:

$rootScope.$broadcast('SOME_TAG', 'your value');

and the second controller will listen on this tag like:
$scope.$on('SOME_TAG', function(response) {
      // ....
})

3

using $rootScope:

4

window.sessionStorage.setItem("Mydata",data);
$scope.data = $window.sessionStorage.getItem("Mydata");

5

Один із способів використання кутового сервісу:

var app = angular.module("home", []);

app.controller('one', function($scope, ser1){
$scope.inputText = ser1;
});

app.controller('two',function($scope, ser1){
$scope.inputTextTwo = ser1;
});

app.factory('ser1', function(){
return {o: ''};
});

2
додайте ще одне пояснення у свою відповідь.
— Герхард Барнард

2

Створіть заводський модуль у своєму модулі та додайте посилання на завод у контролері та використовуйте його змінні в контролері, а тепер отримайте значення даних в іншому контролері, додавши посилання, куди хочете.


2

Я не знаю, чи це комусь допоможе, але на основі відповіді Charx (спасибі!) Я створив просту службу кешування. Сміливо користуйтеся, реміксуйте та діліться:

angular.service('cache', function() {
    var _cache, _store, _get, _set, _clear;
    _cache = {};

    _store = function(data) {
        angular.merge(_cache, data);
    };

    _set = function(data) {
        _cache = angular.extend({}, data);
    };

    _get = function(key) {
        if(key == null) {
            return _cache;
        } else {
            return _cache[key];
        }
    };

    _clear = function() {
        _cache = {};
    };

    return {
        get: _get,
        set: _set,
        store: _store,
        clear: _clear
    };
});

2

Один із способів використання кутового сервісу:

var app = angular.module("home", []);

app.controller('one', function($scope, ser1){
$scope.inputText = ser1;
});


app.controller('two',function($scope, ser1){
$scope.inputTextTwo = ser1;
});

app.factory('ser1', function(){
return {o: ''};
});



<div ng-app='home'>

<div ng-controller='one'>
  Type in text: 
  <input type='text' ng-model="inputText.o"/>
</div>
<br />

<div ng-controller='two'>
  Type in text:
  <input type='text' ng-model="inputTextTwo.o"/>
</div>

</div>

https://jsfiddle.net/1w64222q/


1

FYI Об'єкт $ області має $ емісію, $ трансляцію, $ на І Об'єкт $ rootScope має однаковий $ емісію, $ трансляцію, $ на

детальніше про опублікувати / передплатити шаблон дизайну в кутовому тут


1

Для вдосконалення рішення, запропонованого @Maxim за допомогою $ Broadcast, надсилання даних не змінюється

$rootScope.$broadcast('SOME_TAG', 'my variable');

але для прослуховування даних

$scope.$on('SOME_TAG', function(event, args) {
    console.log("My variable is", args);// args is value of your variable
})

1

Є три способи зробити це,

а) користування послугою

б) Використання залежних відносин батько / дитина між областями контролера.

c) У кутовому 2.0 "As" ключове слово буде передавати дані від одного контролера до іншого.

Для отримання додаткової інформації з прикладом, будь ласка, перевірте нижче за посиланням:

http://www.tutespace.com/2016/03/angular-js.html


0
var custApp = angular.module("custApp", [])
.controller('FirstController', FirstController)
.controller('SecondController',SecondController)
.service('sharedData', SharedData);

FirstController.$inject = ['sharedData'];
function FirstController(sharedData) {
this.data = sharedData.data;
}

SecondController.$inject['sharedData'];
function SecondController(sharedData) {
this.data = sharedData.data;
}

function SharedData() {
this.data = {
    value: 'default Value'
}
}

Перший контролер

<div ng-controller="FirstController as vm">
<input type=text ng-model="vm.data.value" />
</div>

Другий контролер

 <div ng-controller="SecondController as vm">
    Second Controller<br>
    {{vm.data.value}}
</div>

-1

Я думаю, що

Кращий спосіб

це використовувати $ localStorage. (Працює весь час)

app.controller('ProductController', function($scope, $localStorage) {
    $scope.setSelectedProduct = function(selectedObj){
        $localStorage.selectedObj= selectedObj;
    };
});

Вашою карткою буде Контролер

app.controller('CartController', function($scope,$localStorage) { 
    $scope.selectedProducts = $localStorage.selectedObj;
    $localStorage.$reset();//to remove
});

Ви також можете додати

if($localStorage.selectedObj){
    $scope.selectedProducts = $localStorage.selectedObj;
}else{
    //redirect to select product using $location.url('/select-product')
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.