Передача даних між контролерами в 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.